aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnish Bhatt <anish@chelsio.com>2014-09-15 20:44:18 -0400
committerJames Bottomley <JBottomley@Parallels.com>2014-10-01 13:40:22 -0400
commit078efae00ffc76381c3248006e9cf0988163488f (patch)
tree59f80cbdbd8f9468c1594eef93f89f90926060b7
parent7d1311b93e58ed55f3a31cc8f94c4b8fe988a2b9 (diff)
[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: fc8d0590d914 ("libcxgbi: Add ipv6 api to driver") Fixes: 759a0cc5a3e1 ("cxgb4i: Add ipv6 code to driver, call into libcxgbi ipv6 api") Signed-off-by: Anish Bhatt <anish@chelsio.com> Signed-off-by: Karen Xie <kxie@chelsio.com> Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/cxgb4i.c2
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.c57
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.h3
3 files changed, 55 insertions, 7 deletions
diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
index 79788a12712d..02e69e7ee4a3 100644
--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
+++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
@@ -1647,7 +1647,7 @@ static int cxgbi_inet6addr_handler(struct notifier_block *this,
1647 if (event_dev->priv_flags & IFF_802_1Q_VLAN) 1647 if (event_dev->priv_flags & IFF_802_1Q_VLAN)
1648 event_dev = vlan_dev_real_dev(event_dev); 1648 event_dev = vlan_dev_real_dev(event_dev);
1649 1649
1650 cdev = cxgbi_device_find_by_netdev(event_dev, NULL); 1650 cdev = cxgbi_device_find_by_netdev_rcu(event_dev, NULL);
1651 1651
1652 if (!cdev) 1652 if (!cdev)
1653 return ret; 1653 return ret;
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index d65df6dc106f..addd1dddce14 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -57,6 +57,9 @@ MODULE_PARM_DESC(dbg_level, "libiscsi debug level (default=0)");
57static LIST_HEAD(cdev_list); 57static LIST_HEAD(cdev_list);
58static DEFINE_MUTEX(cdev_mutex); 58static DEFINE_MUTEX(cdev_mutex);
59 59
60static LIST_HEAD(cdev_rcu_list);
61static DEFINE_SPINLOCK(cdev_rcu_lock);
62
60int cxgbi_device_portmap_create(struct cxgbi_device *cdev, unsigned int base, 63int cxgbi_device_portmap_create(struct cxgbi_device *cdev, unsigned int base,
61 unsigned int max_conn) 64 unsigned int max_conn)
62{ 65{
@@ -142,6 +145,10 @@ struct cxgbi_device *cxgbi_device_register(unsigned int extra,
142 list_add_tail(&cdev->list_head, &cdev_list); 145 list_add_tail(&cdev->list_head, &cdev_list);
143 mutex_unlock(&cdev_mutex); 146 mutex_unlock(&cdev_mutex);
144 147
148 spin_lock(&cdev_rcu_lock);
149 list_add_tail_rcu(&cdev->rcu_node, &cdev_rcu_list);
150 spin_unlock(&cdev_rcu_lock);
151
145 log_debug(1 << CXGBI_DBG_DEV, 152 log_debug(1 << CXGBI_DBG_DEV,
146 "cdev 0x%p, p# %u.\n", cdev, nports); 153 "cdev 0x%p, p# %u.\n", cdev, nports);
147 return cdev; 154 return cdev;
@@ -153,9 +160,16 @@ void cxgbi_device_unregister(struct cxgbi_device *cdev)
153 log_debug(1 << CXGBI_DBG_DEV, 160 log_debug(1 << CXGBI_DBG_DEV,
154 "cdev 0x%p, p# %u,%s.\n", 161 "cdev 0x%p, p# %u,%s.\n",
155 cdev, cdev->nports, cdev->nports ? cdev->ports[0]->name : ""); 162 cdev, cdev->nports, cdev->nports ? cdev->ports[0]->name : "");
163
156 mutex_lock(&cdev_mutex); 164 mutex_lock(&cdev_mutex);
157 list_del(&cdev->list_head); 165 list_del(&cdev->list_head);
158 mutex_unlock(&cdev_mutex); 166 mutex_unlock(&cdev_mutex);
167
168 spin_lock(&cdev_rcu_lock);
169 list_del_rcu(&cdev->rcu_node);
170 spin_unlock(&cdev_rcu_lock);
171 synchronize_rcu();
172
159 cxgbi_device_destroy(cdev); 173 cxgbi_device_destroy(cdev);
160} 174}
161EXPORT_SYMBOL_GPL(cxgbi_device_unregister); 175EXPORT_SYMBOL_GPL(cxgbi_device_unregister);
@@ -167,12 +181,9 @@ void cxgbi_device_unregister_all(unsigned int flag)
167 mutex_lock(&cdev_mutex); 181 mutex_lock(&cdev_mutex);
168 list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) { 182 list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) {
169 if ((cdev->flags & flag) == flag) { 183 if ((cdev->flags & flag) == flag) {
170 log_debug(1 << CXGBI_DBG_DEV, 184 mutex_unlock(&cdev_mutex);
171 "cdev 0x%p, p# %u,%s.\n", 185 cxgbi_device_unregister(cdev);
172 cdev, cdev->nports, cdev->nports ? 186 mutex_lock(&cdev_mutex);
173 cdev->ports[0]->name : "");
174 list_del(&cdev->list_head);
175 cxgbi_device_destroy(cdev);
176 } 187 }
177 } 188 }
178 mutex_unlock(&cdev_mutex); 189 mutex_unlock(&cdev_mutex);
@@ -191,6 +202,7 @@ struct cxgbi_device *cxgbi_device_find_by_lldev(void *lldev)
191 } 202 }
192 } 203 }
193 mutex_unlock(&cdev_mutex); 204 mutex_unlock(&cdev_mutex);
205
194 log_debug(1 << CXGBI_DBG_DEV, 206 log_debug(1 << CXGBI_DBG_DEV,
195 "lldev 0x%p, NO match found.\n", lldev); 207 "lldev 0x%p, NO match found.\n", lldev);
196 return NULL; 208 return NULL;
@@ -230,6 +242,39 @@ struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
230} 242}
231EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev); 243EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev);
232 244
245struct cxgbi_device *cxgbi_device_find_by_netdev_rcu(struct net_device *ndev,
246 int *port)
247{
248 struct net_device *vdev = NULL;
249 struct cxgbi_device *cdev;
250 int i;
251
252 if (ndev->priv_flags & IFF_802_1Q_VLAN) {
253 vdev = ndev;
254 ndev = vlan_dev_real_dev(ndev);
255 pr_info("vlan dev %s -> %s.\n", vdev->name, ndev->name);
256 }
257
258 rcu_read_lock();
259 list_for_each_entry_rcu(cdev, &cdev_rcu_list, rcu_node) {
260 for (i = 0; i < cdev->nports; i++) {
261 if (ndev == cdev->ports[i]) {
262 cdev->hbas[i]->vdev = vdev;
263 rcu_read_unlock();
264 if (port)
265 *port = i;
266 return cdev;
267 }
268 }
269 }
270 rcu_read_unlock();
271
272 log_debug(1 << CXGBI_DBG_DEV,
273 "ndev 0x%p, %s, NO match found.\n", ndev, ndev->name);
274 return NULL;
275}
276EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev_rcu);
277
233static struct cxgbi_device *cxgbi_device_find_by_mac(struct net_device *ndev, 278static struct cxgbi_device *cxgbi_device_find_by_mac(struct net_device *ndev,
234 int *port) 279 int *port)
235{ 280{
diff --git a/drivers/scsi/cxgbi/libcxgbi.h b/drivers/scsi/cxgbi/libcxgbi.h
index b3e6e7541cc5..1d98fad6a0ab 100644
--- a/drivers/scsi/cxgbi/libcxgbi.h
+++ b/drivers/scsi/cxgbi/libcxgbi.h
@@ -527,6 +527,7 @@ struct cxgbi_ports_map {
527#define CXGBI_FLAG_IPV4_SET 0x10 527#define CXGBI_FLAG_IPV4_SET 0x10
528struct cxgbi_device { 528struct cxgbi_device {
529 struct list_head list_head; 529 struct list_head list_head;
530 struct list_head rcu_node;
530 unsigned int flags; 531 unsigned int flags;
531 struct net_device **ports; 532 struct net_device **ports;
532 void *lldev; 533 void *lldev;
@@ -709,6 +710,8 @@ void cxgbi_device_unregister(struct cxgbi_device *);
709void cxgbi_device_unregister_all(unsigned int flag); 710void cxgbi_device_unregister_all(unsigned int flag);
710struct cxgbi_device *cxgbi_device_find_by_lldev(void *); 711struct cxgbi_device *cxgbi_device_find_by_lldev(void *);
711struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *, int *); 712struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *, int *);
713struct cxgbi_device *cxgbi_device_find_by_netdev_rcu(struct net_device *,
714 int *);
712int cxgbi_hbas_add(struct cxgbi_device *, u64, unsigned int, 715int cxgbi_hbas_add(struct cxgbi_device *, u64, unsigned int,
713 struct scsi_host_template *, 716 struct scsi_host_template *,
714 struct scsi_transport_template *); 717 struct scsi_transport_template *);