aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeil Horman <nhorman@tuxdriver.com>2011-09-06 13:59:13 -0400
committerJames Bottomley <JBottomley@Parallels.com>2011-09-26 10:28:01 -0400
commite48f129c2f200dde8899f6ea5c6e7173674fc482 (patch)
tree9f5acdec1e31c12a7382e26dd173e904fc7fb2a3
parent3538a001ea7db13fa1be2966b71f69d808acff01 (diff)
[SCSI] cxgb3i: convert cdev->l2opt to use rcu to prevent NULL dereference
This oops was reported recently: d:mon> e cpu 0xd: Vector: 300 (Data Access) at [c0000000fd4c7120] pc: d00000000076f194: .t3_l2t_get+0x44/0x524 [cxgb3] lr: d000000000b02108: .init_act_open+0x150/0x3d4 [cxgb3i] sp: c0000000fd4c73a0 msr: 8000000000009032 dar: 0 dsisr: 40000000 current = 0xc0000000fd640d40 paca = 0xc00000000054ff80 pid = 5085, comm = iscsid d:mon> t [c0000000fd4c7450] d000000000b02108 .init_act_open+0x150/0x3d4 [cxgb3i] [c0000000fd4c7500] d000000000e45378 .cxgbi_ep_connect+0x784/0x8e8 [libcxgbi] [c0000000fd4c7650] d000000000db33f0 .iscsi_if_rx+0x71c/0xb18 [scsi_transport_iscsi2] [c0000000fd4c7740] c000000000370c9c .netlink_data_ready+0x40/0xa4 [c0000000fd4c77c0] c00000000036f010 .netlink_sendskb+0x4c/0x9c [c0000000fd4c7850] c000000000370c18 .netlink_sendmsg+0x358/0x39c [c0000000fd4c7950] c00000000033be24 .sock_sendmsg+0x114/0x1b8 [c0000000fd4c7b50] c00000000033d208 .sys_sendmsg+0x218/0x2ac [c0000000fd4c7d70] c00000000033f55c .sys_socketcall+0x228/0x27c [c0000000fd4c7e30] c0000000000086a4 syscall_exit+0x0/0x40 --- Exception: c01 (System Call) at 00000080da560cfc The root cause was an EEH error, which sent us down the offload_close path in the cxgb3 driver, which in turn sets cdev->l2opt to NULL, without regard for upper layer driver (like the cxgbi drivers) which might have execution contexts in the middle of its use. The result is the oops above, when t3_l2t_get attempts to dereference L2DATA(cdev)->nentries in arp_hash right after the EEH error handler sets it to NULL. The fix is to prevent the setting of the NULL pointer until after there are no further users of it. The t3cdev->l2opt pointer is now converted to be an rcu pointer and the L2DATA macro is now called under the protection of the rcu_read_lock(). When the EEH error path: t3_adapter_error->offload_close->cxgb3_offload_deactivate Is exectured, setting of that l2opt pointer to NULL, is now gated on an rcu quiescence point, preventing, allowing L2DATA callers to safely check for a NULL pointer without concern that the underlying data will be freeded before the pointer is dereferenced. This has been tested by the reporter and shown to fix the reproted oops [nhorman: fix up unitinialised variable reported by Dan Carpenter] Signed-off-by: Neil Horman <nhorman@tuxdriver.com> Reviewed-by: Karen Xie <kxie@chelsio.com> Cc: stable@kernel.org Signed-off-by: James Bottomley <JBottomley@Parallels.com>
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_cm.c10
-rw-r--r--drivers/net/cxgb3/cxgb3_offload.c23
-rw-r--r--drivers/net/cxgb3/l2t.c15
-rw-r--r--drivers/net/cxgb3/l2t.h16
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/cxgb3i.c2
5 files changed, 48 insertions, 18 deletions
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index 17bf9d95463c..6cd642aaa4de 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -287,7 +287,7 @@ void __free_ep(struct kref *kref)
287 if (test_bit(RELEASE_RESOURCES, &ep->com.flags)) { 287 if (test_bit(RELEASE_RESOURCES, &ep->com.flags)) {
288 cxgb3_remove_tid(ep->com.tdev, (void *)ep, ep->hwtid); 288 cxgb3_remove_tid(ep->com.tdev, (void *)ep, ep->hwtid);
289 dst_release(ep->dst); 289 dst_release(ep->dst);
290 l2t_release(L2DATA(ep->com.tdev), ep->l2t); 290 l2t_release(ep->com.tdev, ep->l2t);
291 } 291 }
292 kfree(ep); 292 kfree(ep);
293} 293}
@@ -1178,7 +1178,7 @@ static int act_open_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
1178 release_tid(ep->com.tdev, GET_TID(rpl), NULL); 1178 release_tid(ep->com.tdev, GET_TID(rpl), NULL);
1179 cxgb3_free_atid(ep->com.tdev, ep->atid); 1179 cxgb3_free_atid(ep->com.tdev, ep->atid);
1180 dst_release(ep->dst); 1180 dst_release(ep->dst);
1181 l2t_release(L2DATA(ep->com.tdev), ep->l2t); 1181 l2t_release(ep->com.tdev, ep->l2t);
1182 put_ep(&ep->com); 1182 put_ep(&ep->com);
1183 return CPL_RET_BUF_DONE; 1183 return CPL_RET_BUF_DONE;
1184} 1184}
@@ -1377,7 +1377,7 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
1377 if (!child_ep) { 1377 if (!child_ep) {
1378 printk(KERN_ERR MOD "%s - failed to allocate ep entry!\n", 1378 printk(KERN_ERR MOD "%s - failed to allocate ep entry!\n",
1379 __func__); 1379 __func__);
1380 l2t_release(L2DATA(tdev), l2t); 1380 l2t_release(tdev, l2t);
1381 dst_release(dst); 1381 dst_release(dst);
1382 goto reject; 1382 goto reject;
1383 } 1383 }
@@ -1956,7 +1956,7 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
1956 if (!err) 1956 if (!err)
1957 goto out; 1957 goto out;
1958 1958
1959 l2t_release(L2DATA(h->rdev.t3cdev_p), ep->l2t); 1959 l2t_release(h->rdev.t3cdev_p, ep->l2t);
1960fail4: 1960fail4:
1961 dst_release(ep->dst); 1961 dst_release(ep->dst);
1962fail3: 1962fail3:
@@ -2127,7 +2127,7 @@ int iwch_ep_redirect(void *ctx, struct dst_entry *old, struct dst_entry *new,
2127 PDBG("%s ep %p redirect to dst %p l2t %p\n", __func__, ep, new, 2127 PDBG("%s ep %p redirect to dst %p l2t %p\n", __func__, ep, new,
2128 l2t); 2128 l2t);
2129 dst_hold(new); 2129 dst_hold(new);
2130 l2t_release(L2DATA(ep->com.tdev), ep->l2t); 2130 l2t_release(ep->com.tdev, ep->l2t);
2131 ep->l2t = l2t; 2131 ep->l2t = l2t;
2132 dst_release(old); 2132 dst_release(old);
2133 ep->dst = new; 2133 ep->dst = new;
diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c
index 805076c54f1b..da5a5d9b8aff 100644
--- a/drivers/net/cxgb3/cxgb3_offload.c
+++ b/drivers/net/cxgb3/cxgb3_offload.c
@@ -1146,12 +1146,14 @@ static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new)
1146 if (te && te->ctx && te->client && te->client->redirect) { 1146 if (te && te->ctx && te->client && te->client->redirect) {
1147 update_tcb = te->client->redirect(te->ctx, old, new, e); 1147 update_tcb = te->client->redirect(te->ctx, old, new, e);
1148 if (update_tcb) { 1148 if (update_tcb) {
1149 rcu_read_lock();
1149 l2t_hold(L2DATA(tdev), e); 1150 l2t_hold(L2DATA(tdev), e);
1151 rcu_read_unlock();
1150 set_l2t_ix(tdev, tid, e); 1152 set_l2t_ix(tdev, tid, e);
1151 } 1153 }
1152 } 1154 }
1153 } 1155 }
1154 l2t_release(L2DATA(tdev), e); 1156 l2t_release(tdev, e);
1155} 1157}
1156 1158
1157/* 1159/*
@@ -1264,7 +1266,7 @@ int cxgb3_offload_activate(struct adapter *adapter)
1264 goto out_free; 1266 goto out_free;
1265 1267
1266 err = -ENOMEM; 1268 err = -ENOMEM;
1267 L2DATA(dev) = t3_init_l2t(l2t_capacity); 1269 RCU_INIT_POINTER(dev->l2opt, t3_init_l2t(l2t_capacity));
1268 if (!L2DATA(dev)) 1270 if (!L2DATA(dev))
1269 goto out_free; 1271 goto out_free;
1270 1272
@@ -1298,16 +1300,24 @@ int cxgb3_offload_activate(struct adapter *adapter)
1298 1300
1299out_free_l2t: 1301out_free_l2t:
1300 t3_free_l2t(L2DATA(dev)); 1302 t3_free_l2t(L2DATA(dev));
1301 L2DATA(dev) = NULL; 1303 rcu_assign_pointer(dev->l2opt, NULL);
1302out_free: 1304out_free:
1303 kfree(t); 1305 kfree(t);
1304 return err; 1306 return err;
1305} 1307}
1306 1308
1309static void clean_l2_data(struct rcu_head *head)
1310{
1311 struct l2t_data *d = container_of(head, struct l2t_data, rcu_head);
1312 t3_free_l2t(d);
1313}
1314
1315
1307void cxgb3_offload_deactivate(struct adapter *adapter) 1316void cxgb3_offload_deactivate(struct adapter *adapter)
1308{ 1317{
1309 struct t3cdev *tdev = &adapter->tdev; 1318 struct t3cdev *tdev = &adapter->tdev;
1310 struct t3c_data *t = T3C_DATA(tdev); 1319 struct t3c_data *t = T3C_DATA(tdev);
1320 struct l2t_data *d;
1311 1321
1312 remove_adapter(adapter); 1322 remove_adapter(adapter);
1313 if (list_empty(&adapter_list)) 1323 if (list_empty(&adapter_list))
@@ -1315,8 +1325,11 @@ void cxgb3_offload_deactivate(struct adapter *adapter)
1315 1325
1316 free_tid_maps(&t->tid_maps); 1326 free_tid_maps(&t->tid_maps);
1317 T3C_DATA(tdev) = NULL; 1327 T3C_DATA(tdev) = NULL;
1318 t3_free_l2t(L2DATA(tdev)); 1328 rcu_read_lock();
1319 L2DATA(tdev) = NULL; 1329 d = L2DATA(tdev);
1330 rcu_read_unlock();
1331 rcu_assign_pointer(tdev->l2opt, NULL);
1332 call_rcu(&d->rcu_head, clean_l2_data);
1320 if (t->nofail_skb) 1333 if (t->nofail_skb)
1321 kfree_skb(t->nofail_skb); 1334 kfree_skb(t->nofail_skb);
1322 kfree(t); 1335 kfree(t);
diff --git a/drivers/net/cxgb3/l2t.c b/drivers/net/cxgb3/l2t.c
index f452c4003253..41540978a173 100644
--- a/drivers/net/cxgb3/l2t.c
+++ b/drivers/net/cxgb3/l2t.c
@@ -300,14 +300,21 @@ static inline void reuse_entry(struct l2t_entry *e, struct neighbour *neigh)
300struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct neighbour *neigh, 300struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct neighbour *neigh,
301 struct net_device *dev) 301 struct net_device *dev)
302{ 302{
303 struct l2t_entry *e; 303 struct l2t_entry *e = NULL;
304 struct l2t_data *d = L2DATA(cdev); 304 struct l2t_data *d;
305 int hash;
305 u32 addr = *(u32 *) neigh->primary_key; 306 u32 addr = *(u32 *) neigh->primary_key;
306 int ifidx = neigh->dev->ifindex; 307 int ifidx = neigh->dev->ifindex;
307 int hash = arp_hash(addr, ifidx, d);
308 struct port_info *p = netdev_priv(dev); 308 struct port_info *p = netdev_priv(dev);
309 int smt_idx = p->port_id; 309 int smt_idx = p->port_id;
310 310
311 rcu_read_lock();
312 d = L2DATA(cdev);
313 if (!d)
314 goto done_rcu;
315
316 hash = arp_hash(addr, ifidx, d);
317
311 write_lock_bh(&d->lock); 318 write_lock_bh(&d->lock);
312 for (e = d->l2tab[hash].first; e; e = e->next) 319 for (e = d->l2tab[hash].first; e; e = e->next)
313 if (e->addr == addr && e->ifindex == ifidx && 320 if (e->addr == addr && e->ifindex == ifidx &&
@@ -338,6 +345,8 @@ struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct neighbour *neigh,
338 } 345 }
339done: 346done:
340 write_unlock_bh(&d->lock); 347 write_unlock_bh(&d->lock);
348done_rcu:
349 rcu_read_unlock();
341 return e; 350 return e;
342} 351}
343 352
diff --git a/drivers/net/cxgb3/l2t.h b/drivers/net/cxgb3/l2t.h
index 7a12d52ed4fc..c5f54796e2cb 100644
--- a/drivers/net/cxgb3/l2t.h
+++ b/drivers/net/cxgb3/l2t.h
@@ -76,6 +76,7 @@ struct l2t_data {
76 atomic_t nfree; /* number of free entries */ 76 atomic_t nfree; /* number of free entries */
77 rwlock_t lock; 77 rwlock_t lock;
78 struct l2t_entry l2tab[0]; 78 struct l2t_entry l2tab[0];
79 struct rcu_head rcu_head; /* to handle rcu cleanup */
79}; 80};
80 81
81typedef void (*arp_failure_handler_func)(struct t3cdev * dev, 82typedef void (*arp_failure_handler_func)(struct t3cdev * dev,
@@ -99,7 +100,7 @@ static inline void set_arp_failure_handler(struct sk_buff *skb,
99/* 100/*
100 * Getting to the L2 data from an offload device. 101 * Getting to the L2 data from an offload device.
101 */ 102 */
102#define L2DATA(dev) ((dev)->l2opt) 103#define L2DATA(cdev) (rcu_dereference((cdev)->l2opt))
103 104
104#define W_TCB_L2T_IX 0 105#define W_TCB_L2T_IX 0
105#define S_TCB_L2T_IX 7 106#define S_TCB_L2T_IX 7
@@ -126,15 +127,22 @@ static inline int l2t_send(struct t3cdev *dev, struct sk_buff *skb,
126 return t3_l2t_send_slow(dev, skb, e); 127 return t3_l2t_send_slow(dev, skb, e);
127} 128}
128 129
129static inline void l2t_release(struct l2t_data *d, struct l2t_entry *e) 130static inline void l2t_release(struct t3cdev *t, struct l2t_entry *e)
130{ 131{
131 if (atomic_dec_and_test(&e->refcnt)) 132 struct l2t_data *d;
133
134 rcu_read_lock();
135 d = L2DATA(t);
136
137 if (atomic_dec_and_test(&e->refcnt) && d)
132 t3_l2e_free(d, e); 138 t3_l2e_free(d, e);
139
140 rcu_read_unlock();
133} 141}
134 142
135static inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e) 143static inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e)
136{ 144{
137 if (atomic_add_return(1, &e->refcnt) == 1) /* 0 -> 1 transition */ 145 if (d && atomic_add_return(1, &e->refcnt) == 1) /* 0 -> 1 transition */
138 atomic_dec(&d->nfree); 146 atomic_dec(&d->nfree);
139} 147}
140 148
diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
index bd22041e2789..f58644850333 100644
--- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
+++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
@@ -913,7 +913,7 @@ static void l2t_put(struct cxgbi_sock *csk)
913 struct t3cdev *t3dev = (struct t3cdev *)csk->cdev->lldev; 913 struct t3cdev *t3dev = (struct t3cdev *)csk->cdev->lldev;
914 914
915 if (csk->l2t) { 915 if (csk->l2t) {
916 l2t_release(L2DATA(t3dev), csk->l2t); 916 l2t_release(t3dev, csk->l2t);
917 csk->l2t = NULL; 917 csk->l2t = NULL;
918 cxgbi_sock_put(csk); 918 cxgbi_sock_put(csk);
919 } 919 }