aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/macvtap.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2010-02-11 00:55:39 -0500
committerDavid S. Miller <davem@davemloft.net>2010-02-16 00:49:49 -0500
commit564517e804c9c6d4e29c270bfc1517404d27107b (patch)
treef1912327050508a1d2902dbba7cbfcf7d054cd0f /drivers/net/macvtap.c
parente9449d85c67127d6f9d01aad8963d567ab02cb96 (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/macvtap.c')
-rw-r--r--drivers/net/macvtap.c35
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
160static inline struct macvtap_queue *macvtap_file_get_queue(struct file *file) 161static 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
166static inline void macvtap_file_put_queue(void) 174static 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);
317out: 327out:
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);
369out: 379out:
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
439out: 446out:
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: