mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-18 18:23:53 +08:00
ea793788f8
The hash used for mapping irq numbers to msi descriptors does not utilize all buckets that were allocated. Fix this by using the same value (computed by the number of bits used for the hash function) at relevant places. Reviewed-by: Gerald Schaefer <gerald.schaefer@de.ibm.com> Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
143 lines
3.2 KiB
C
143 lines
3.2 KiB
C
/*
|
|
* Copyright IBM Corp. 2012
|
|
*
|
|
* Author(s):
|
|
* Jan Glauber <jang@linux.vnet.ibm.com>
|
|
*/
|
|
|
|
#define COMPONENT "zPCI"
|
|
#define pr_fmt(fmt) COMPONENT ": " fmt
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/err.h>
|
|
#include <linux/rculist.h>
|
|
#include <linux/hash.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/msi.h>
|
|
#include <asm/hw_irq.h>
|
|
|
|
/* mapping of irq numbers to msi_desc */
|
|
static struct hlist_head *msi_hash;
|
|
static const unsigned int msi_hash_bits = 8;
|
|
#define MSI_HASH_BUCKETS (1U << msi_hash_bits)
|
|
#define msi_hashfn(nr) hash_long(nr, msi_hash_bits)
|
|
|
|
static DEFINE_SPINLOCK(msi_map_lock);
|
|
|
|
struct msi_desc *__irq_get_msi_desc(unsigned int irq)
|
|
{
|
|
struct msi_map *map;
|
|
|
|
hlist_for_each_entry_rcu(map,
|
|
&msi_hash[msi_hashfn(irq)], msi_chain)
|
|
if (map->irq == irq)
|
|
return map->msi;
|
|
return NULL;
|
|
}
|
|
|
|
int zpci_msi_set_mask_bits(struct msi_desc *msi, u32 mask, u32 flag)
|
|
{
|
|
if (msi->msi_attrib.is_msix) {
|
|
int offset = msi->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
|
|
PCI_MSIX_ENTRY_VECTOR_CTRL;
|
|
msi->masked = readl(msi->mask_base + offset);
|
|
writel(flag, msi->mask_base + offset);
|
|
} else {
|
|
if (msi->msi_attrib.maskbit) {
|
|
int pos;
|
|
u32 mask_bits;
|
|
|
|
pos = (long) msi->mask_base;
|
|
pci_read_config_dword(msi->dev, pos, &mask_bits);
|
|
mask_bits &= ~(mask);
|
|
mask_bits |= flag & mask;
|
|
pci_write_config_dword(msi->dev, pos, mask_bits);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
msi->msi_attrib.maskbit = !!flag;
|
|
return 1;
|
|
}
|
|
|
|
int zpci_setup_msi_irq(struct zpci_dev *zdev, struct msi_desc *msi,
|
|
unsigned int nr, int offset)
|
|
{
|
|
struct msi_map *map;
|
|
struct msi_msg msg;
|
|
int rc;
|
|
|
|
map = kmalloc(sizeof(*map), GFP_KERNEL);
|
|
if (map == NULL)
|
|
return -ENOMEM;
|
|
|
|
map->irq = nr;
|
|
map->msi = msi;
|
|
zdev->msi_map[nr & ZPCI_MSI_MASK] = map;
|
|
INIT_HLIST_NODE(&map->msi_chain);
|
|
|
|
pr_debug("%s hashing irq: %u to bucket nr: %llu\n",
|
|
__func__, nr, msi_hashfn(nr));
|
|
hlist_add_head_rcu(&map->msi_chain, &msi_hash[msi_hashfn(nr)]);
|
|
|
|
spin_lock(&msi_map_lock);
|
|
rc = irq_set_msi_desc(nr, msi);
|
|
if (rc) {
|
|
spin_unlock(&msi_map_lock);
|
|
hlist_del_rcu(&map->msi_chain);
|
|
kfree(map);
|
|
zdev->msi_map[nr & ZPCI_MSI_MASK] = NULL;
|
|
return rc;
|
|
}
|
|
spin_unlock(&msi_map_lock);
|
|
|
|
msg.data = nr - offset;
|
|
msg.address_lo = zdev->msi_addr & 0xffffffff;
|
|
msg.address_hi = zdev->msi_addr >> 32;
|
|
write_msi_msg(nr, &msg);
|
|
return 0;
|
|
}
|
|
|
|
void zpci_teardown_msi_irq(struct zpci_dev *zdev, struct msi_desc *msi)
|
|
{
|
|
int irq = msi->irq & ZPCI_MSI_MASK;
|
|
struct msi_map *map;
|
|
|
|
msi->msg.address_lo = 0;
|
|
msi->msg.address_hi = 0;
|
|
msi->msg.data = 0;
|
|
msi->irq = 0;
|
|
zpci_msi_set_mask_bits(msi, 1, 1);
|
|
|
|
spin_lock(&msi_map_lock);
|
|
map = zdev->msi_map[irq];
|
|
hlist_del_rcu(&map->msi_chain);
|
|
kfree(map);
|
|
zdev->msi_map[irq] = NULL;
|
|
spin_unlock(&msi_map_lock);
|
|
}
|
|
|
|
/*
|
|
* The msi hash table has 256 entries which is good for 4..20
|
|
* devices (a typical device allocates 10 + CPUs MSI's). Maybe make
|
|
* the hash table size adjustable later.
|
|
*/
|
|
int __init zpci_msihash_init(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
msi_hash = kmalloc(MSI_HASH_BUCKETS * sizeof(*msi_hash), GFP_KERNEL);
|
|
if (!msi_hash)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < MSI_HASH_BUCKETS; i++)
|
|
INIT_HLIST_HEAD(&msi_hash[i]);
|
|
return 0;
|
|
}
|
|
|
|
void __init zpci_msihash_exit(void)
|
|
{
|
|
kfree(msi_hash);
|
|
}
|