Commit 078efae0 authored by Anish Bhatt's avatar Anish Bhatt Committed by James Bottomley

[SCSI] cxgb4i: avoid holding mutex in interrupt context

cxgbi_inet6addr_handler() can be called in interrupt context, so use rcu
protected list while finding netdev.  This is observed as a scheduling in
atomic oops when running over ipv6.

Fixes: fc8d0590 ("libcxgbi: Add ipv6 api to driver")
Fixes: 759a0cc5 ("cxgb4i: Add ipv6 code to driver, call into libcxgbi ipv6 api")
Signed-off-by: default avatarAnish Bhatt <anish@chelsio.com>
Signed-off-by: default avatarKaren Xie <kxie@chelsio.com>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 7d1311b9
......@@ -1647,7 +1647,7 @@ static int cxgbi_inet6addr_handler(struct notifier_block *this,
if (event_dev->priv_flags & IFF_802_1Q_VLAN)
event_dev = vlan_dev_real_dev(event_dev);
cdev = cxgbi_device_find_by_netdev(event_dev, NULL);
cdev = cxgbi_device_find_by_netdev_rcu(event_dev, NULL);
if (!cdev)
return ret;
......
......@@ -57,6 +57,9 @@ MODULE_PARM_DESC(dbg_level, "libiscsi debug level (default=0)");
static LIST_HEAD(cdev_list);
static DEFINE_MUTEX(cdev_mutex);
static LIST_HEAD(cdev_rcu_list);
static DEFINE_SPINLOCK(cdev_rcu_lock);
int cxgbi_device_portmap_create(struct cxgbi_device *cdev, unsigned int base,
unsigned int max_conn)
{
......@@ -142,6 +145,10 @@ struct cxgbi_device *cxgbi_device_register(unsigned int extra,
list_add_tail(&cdev->list_head, &cdev_list);
mutex_unlock(&cdev_mutex);
spin_lock(&cdev_rcu_lock);
list_add_tail_rcu(&cdev->rcu_node, &cdev_rcu_list);
spin_unlock(&cdev_rcu_lock);
log_debug(1 << CXGBI_DBG_DEV,
"cdev 0x%p, p# %u.\n", cdev, nports);
return cdev;
......@@ -153,9 +160,16 @@ void cxgbi_device_unregister(struct cxgbi_device *cdev)
log_debug(1 << CXGBI_DBG_DEV,
"cdev 0x%p, p# %u,%s.\n",
cdev, cdev->nports, cdev->nports ? cdev->ports[0]->name : "");
mutex_lock(&cdev_mutex);
list_del(&cdev->list_head);
mutex_unlock(&cdev_mutex);
spin_lock(&cdev_rcu_lock);
list_del_rcu(&cdev->rcu_node);
spin_unlock(&cdev_rcu_lock);
synchronize_rcu();
cxgbi_device_destroy(cdev);
}
EXPORT_SYMBOL_GPL(cxgbi_device_unregister);
......@@ -167,12 +181,9 @@ void cxgbi_device_unregister_all(unsigned int flag)
mutex_lock(&cdev_mutex);
list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) {
if ((cdev->flags & flag) == flag) {
log_debug(1 << CXGBI_DBG_DEV,
"cdev 0x%p, p# %u,%s.\n",
cdev, cdev->nports, cdev->nports ?
cdev->ports[0]->name : "");
list_del(&cdev->list_head);
cxgbi_device_destroy(cdev);
mutex_unlock(&cdev_mutex);
cxgbi_device_unregister(cdev);
mutex_lock(&cdev_mutex);
}
}
mutex_unlock(&cdev_mutex);
......@@ -191,6 +202,7 @@ struct cxgbi_device *cxgbi_device_find_by_lldev(void *lldev)
}
}
mutex_unlock(&cdev_mutex);
log_debug(1 << CXGBI_DBG_DEV,
"lldev 0x%p, NO match found.\n", lldev);
return NULL;
......@@ -230,6 +242,39 @@ struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
}
EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev);
struct cxgbi_device *cxgbi_device_find_by_netdev_rcu(struct net_device *ndev,
int *port)
{
struct net_device *vdev = NULL;
struct cxgbi_device *cdev;
int i;
if (ndev->priv_flags & IFF_802_1Q_VLAN) {
vdev = ndev;
ndev = vlan_dev_real_dev(ndev);
pr_info("vlan dev %s -> %s.\n", vdev->name, ndev->name);
}
rcu_read_lock();
list_for_each_entry_rcu(cdev, &cdev_rcu_list, rcu_node) {
for (i = 0; i < cdev->nports; i++) {
if (ndev == cdev->ports[i]) {
cdev->hbas[i]->vdev = vdev;
rcu_read_unlock();
if (port)
*port = i;
return cdev;
}
}
}
rcu_read_unlock();
log_debug(1 << CXGBI_DBG_DEV,
"ndev 0x%p, %s, NO match found.\n", ndev, ndev->name);
return NULL;
}
EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev_rcu);
static struct cxgbi_device *cxgbi_device_find_by_mac(struct net_device *ndev,
int *port)
{
......
......@@ -527,6 +527,7 @@ struct cxgbi_ports_map {
#define CXGBI_FLAG_IPV4_SET 0x10
struct cxgbi_device {
struct list_head list_head;
struct list_head rcu_node;
unsigned int flags;
struct net_device **ports;
void *lldev;
......@@ -709,6 +710,8 @@ void cxgbi_device_unregister(struct cxgbi_device *);
void cxgbi_device_unregister_all(unsigned int flag);
struct cxgbi_device *cxgbi_device_find_by_lldev(void *);
struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *, int *);
struct cxgbi_device *cxgbi_device_find_by_netdev_rcu(struct net_device *,
int *);
int cxgbi_hbas_add(struct cxgbi_device *, u64, unsigned int,
struct scsi_host_template *,
struct scsi_transport_template *);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment