diff options
author | Arnd Bergmann <arnd@arndb.de> | 2010-02-11 00:55:39 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-16 00:49:49 -0500 |
commit | 564517e804c9c6d4e29c270bfc1517404d27107b (patch) | |
tree | f1912327050508a1d2902dbba7cbfcf7d054cd0f /drivers/net | |
parent | e9449d85c67127d6f9d01aad8963d567ab02cb96 (diff) |
net/macvtap: fix reference counting
The RCU usage in the original code was broken because
there are cases where we possibly sleep with rcu_read_lock
held. As a fix, change the macvtap_file_get_queue to
get a reference on the socket and the netdev instead of
taking the full rcu_read_lock.
Also, change macvtap_file_get_queue failure case to
not require a subsequent macvtap_file_put_queue, as
pointed out by Ed Swierk.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Ed Swierk <eswierk@aristanetworks.com>
Cc: Sridhar Samudrala <sri@us.ibm.com>
Acked-by: Sridhar Samudrala <sri@us.ibm.com>
Acked-by: Ed Swierk <eswierk@aristanetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/macvtap.c | 35 |
1 files changed, 22 insertions, 13 deletions
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index ad1f6ef89308..fe7656bf68c6 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c | |||
@@ -70,7 +70,8 @@ static struct cdev macvtap_cdev; | |||
70 | * exists. | 70 | * exists. |
71 | * | 71 | * |
72 | * The callbacks from macvlan are always done with rcu_read_lock held | 72 | * The callbacks from macvlan are always done with rcu_read_lock held |
73 | * already, while in the file_operations, we get it ourselves. | 73 | * already. For calls from file_operations, we use the rcu_read_lock_bh |
74 | * to get a reference count on the socket and the device. | ||
74 | * | 75 | * |
75 | * When destroying a queue, we remove the pointers from the file and | 76 | * When destroying a queue, we remove the pointers from the file and |
76 | * from the dev and then synchronize_rcu to make sure no thread is | 77 | * from the dev and then synchronize_rcu to make sure no thread is |
@@ -159,13 +160,21 @@ static void macvtap_del_queues(struct net_device *dev) | |||
159 | 160 | ||
160 | static inline struct macvtap_queue *macvtap_file_get_queue(struct file *file) | 161 | static inline struct macvtap_queue *macvtap_file_get_queue(struct file *file) |
161 | { | 162 | { |
163 | struct macvtap_queue *q; | ||
162 | rcu_read_lock_bh(); | 164 | rcu_read_lock_bh(); |
163 | return rcu_dereference(file->private_data); | 165 | q = rcu_dereference(file->private_data); |
166 | if (q) { | ||
167 | sock_hold(&q->sk); | ||
168 | dev_hold(q->vlan->dev); | ||
169 | } | ||
170 | rcu_read_unlock_bh(); | ||
171 | return q; | ||
164 | } | 172 | } |
165 | 173 | ||
166 | static inline void macvtap_file_put_queue(void) | 174 | static inline void macvtap_file_put_queue(struct macvtap_queue *q) |
167 | { | 175 | { |
168 | rcu_read_unlock_bh(); | 176 | sock_put(&q->sk); |
177 | dev_put(q->vlan->dev); | ||
169 | } | 178 | } |
170 | 179 | ||
171 | /* | 180 | /* |
@@ -314,8 +323,8 @@ static unsigned int macvtap_poll(struct file *file, poll_table * wait) | |||
314 | sock_writeable(&q->sk))) | 323 | sock_writeable(&q->sk))) |
315 | mask |= POLLOUT | POLLWRNORM; | 324 | mask |= POLLOUT | POLLWRNORM; |
316 | 325 | ||
326 | macvtap_file_put_queue(q); | ||
317 | out: | 327 | out: |
318 | macvtap_file_put_queue(); | ||
319 | return mask; | 328 | return mask; |
320 | } | 329 | } |
321 | 330 | ||
@@ -366,8 +375,8 @@ static ssize_t macvtap_aio_write(struct kiocb *iocb, const struct iovec *iv, | |||
366 | 375 | ||
367 | result = macvtap_get_user(q, iv, iov_length(iv, count), | 376 | result = macvtap_get_user(q, iv, iov_length(iv, count), |
368 | file->f_flags & O_NONBLOCK); | 377 | file->f_flags & O_NONBLOCK); |
378 | macvtap_file_put_queue(q); | ||
369 | out: | 379 | out: |
370 | macvtap_file_put_queue(); | ||
371 | return result; | 380 | return result; |
372 | } | 381 | } |
373 | 382 | ||
@@ -398,10 +407,8 @@ static ssize_t macvtap_aio_read(struct kiocb *iocb, const struct iovec *iv, | |||
398 | struct sk_buff *skb; | 407 | struct sk_buff *skb; |
399 | ssize_t len, ret = 0; | 408 | ssize_t len, ret = 0; |
400 | 409 | ||
401 | if (!q) { | 410 | if (!q) |
402 | ret = -ENOLINK; | 411 | return -ENOLINK; |
403 | goto out; | ||
404 | } | ||
405 | 412 | ||
406 | len = iov_length(iv, count); | 413 | len = iov_length(iv, count); |
407 | if (len < 0) { | 414 | if (len < 0) { |
@@ -437,7 +444,7 @@ static ssize_t macvtap_aio_read(struct kiocb *iocb, const struct iovec *iv, | |||
437 | remove_wait_queue(q->sk.sk_sleep, &wait); | 444 | remove_wait_queue(q->sk.sk_sleep, &wait); |
438 | 445 | ||
439 | out: | 446 | out: |
440 | macvtap_file_put_queue(); | 447 | macvtap_file_put_queue(q); |
441 | return ret; | 448 | return ret; |
442 | } | 449 | } |
443 | 450 | ||
@@ -468,7 +475,7 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, | |||
468 | if (!q) | 475 | if (!q) |
469 | return -ENOLINK; | 476 | return -ENOLINK; |
470 | memcpy(devname, q->vlan->dev->name, sizeof(devname)); | 477 | memcpy(devname, q->vlan->dev->name, sizeof(devname)); |
471 | macvtap_file_put_queue(); | 478 | macvtap_file_put_queue(q); |
472 | 479 | ||
473 | if (copy_to_user(&ifr->ifr_name, q->vlan->dev->name, IFNAMSIZ) || | 480 | if (copy_to_user(&ifr->ifr_name, q->vlan->dev->name, IFNAMSIZ) || |
474 | put_user((TUN_TAP_DEV | TUN_NO_PI), &ifr->ifr_flags)) | 481 | put_user((TUN_TAP_DEV | TUN_NO_PI), &ifr->ifr_flags)) |
@@ -485,8 +492,10 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, | |||
485 | return -EFAULT; | 492 | return -EFAULT; |
486 | 493 | ||
487 | q = macvtap_file_get_queue(file); | 494 | q = macvtap_file_get_queue(file); |
495 | if (!q) | ||
496 | return -ENOLINK; | ||
488 | q->sk.sk_sndbuf = u; | 497 | q->sk.sk_sndbuf = u; |
489 | macvtap_file_put_queue(); | 498 | macvtap_file_put_queue(q); |
490 | return 0; | 499 | return 0; |
491 | 500 | ||
492 | case TUNSETOFFLOAD: | 501 | case TUNSETOFFLOAD: |