aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
authorJason Wang <jasowang@redhat.com>2012-10-31 15:45:58 -0400
committerDavid S. Miller <davem@davemloft.net>2012-11-01 11:14:07 -0400
commit6e914fc70793f540015bd99744cd456b8d7fdfbd (patch)
tree4f140c57638a42b7969b158e4fd0b6e4df8ecc11 /drivers/net/tun.c
parent54f968d6efdbf7dec36faa44fc11f01b0e4d1990 (diff)
tuntap: RCUify dereferencing between tun_struct and tun_file
RCU were introduced in this patch to synchronize the dereferences between tun_struct and tun_file. All tun_{get|put} were replaced with RCU, the dereference from one to other must be done under rtnl lock or rcu read critical region. This is needed for the following patches since the one of the goal of multiqueue tuntap is to allow adding or removing queues during workload. Without RCU, control path would hold tx locks when adding or removing queues (which may cause sme delay) and it's hard to change the number of queues without stopping the net device. With the help of rcu, there's also no need for tun_file hold an refcnt to tun_struct. Signed-off-by: Jason Wang <jasowang@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c95
1 files changed, 47 insertions, 48 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index d52ad2438e26..bdbb526eca7b 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -115,13 +115,16 @@ struct tap_filter {
115 * tap_filter were kept in tun_struct since they were used for filtering for the 115 * tap_filter were kept in tun_struct since they were used for filtering for the
116 * netdevice not for a specific queue (at least I didn't see the reqirement for 116 * netdevice not for a specific queue (at least I didn't see the reqirement for
117 * this). 117 * this).
118 *
119 * RCU usage:
120 * The tun_file and tun_struct are loosely coupled, the pointer from on to the
121 * other can only be read while rcu_read_lock or rtnl_lock is held.
118 */ 122 */
119struct tun_file { 123struct tun_file {
120 struct sock sk; 124 struct sock sk;
121 struct socket socket; 125 struct socket socket;
122 struct socket_wq wq; 126 struct socket_wq wq;
123 atomic_t count; 127 struct tun_struct __rcu *tun;
124 struct tun_struct *tun;
125 struct net *net; 128 struct net *net;
126 struct fasync_struct *fasync; 129 struct fasync_struct *fasync;
127 /* only used for fasnyc */ 130 /* only used for fasnyc */
@@ -133,7 +136,7 @@ struct tun_file {
133 * file were attached to a persist device. 136 * file were attached to a persist device.
134 */ 137 */
135struct tun_struct { 138struct tun_struct {
136 struct tun_file *tfile; 139 struct tun_file __rcu *tfile;
137 unsigned int flags; 140 unsigned int flags;
138 kuid_t owner; 141 kuid_t owner;
139 kgid_t group; 142 kgid_t group;
@@ -179,13 +182,11 @@ static int tun_attach(struct tun_struct *tun, struct file *file)
179 if (!err) 182 if (!err)
180 goto out; 183 goto out;
181 } 184 }
182 tfile->tun = tun; 185 rcu_assign_pointer(tfile->tun, tun);
183 tfile->socket.sk->sk_sndbuf = tun->sndbuf; 186 tfile->socket.sk->sk_sndbuf = tun->sndbuf;
184 tun->tfile = tfile; 187 rcu_assign_pointer(tun->tfile, tfile);
185 netif_carrier_on(tun->dev); 188 netif_carrier_on(tun->dev);
186 dev_hold(tun->dev);
187 sock_hold(&tfile->sk); 189 sock_hold(&tfile->sk);
188 atomic_inc(&tfile->count);
189 190
190out: 191out:
191 netif_tx_unlock_bh(tun->dev); 192 netif_tx_unlock_bh(tun->dev);
@@ -194,34 +195,29 @@ out:
194 195
195static void __tun_detach(struct tun_struct *tun) 196static void __tun_detach(struct tun_struct *tun)
196{ 197{
197 struct tun_file *tfile = tun->tfile; 198 struct tun_file *tfile = rcu_dereference_protected(tun->tfile,
199 lockdep_rtnl_is_held());
198 /* Detach from net device */ 200 /* Detach from net device */
199 netif_tx_lock_bh(tun->dev);
200 netif_carrier_off(tun->dev); 201 netif_carrier_off(tun->dev);
201 tun->tfile = NULL; 202 rcu_assign_pointer(tun->tfile, NULL);
202 tfile->tun = NULL; 203 if (tfile) {
203 netif_tx_unlock_bh(tun->dev); 204 rcu_assign_pointer(tfile->tun, NULL);
204
205 /* Drop read queue */
206 skb_queue_purge(&tfile->socket.sk->sk_receive_queue);
207
208 /* Drop the extra count on the net device */
209 dev_put(tun->dev);
210}
211 205
212static void tun_detach(struct tun_struct *tun) 206 synchronize_net();
213{ 207 /* Drop read queue */
214 rtnl_lock(); 208 skb_queue_purge(&tfile->socket.sk->sk_receive_queue);
215 __tun_detach(tun); 209 }
216 rtnl_unlock();
217} 210}
218 211
219static struct tun_struct *__tun_get(struct tun_file *tfile) 212static struct tun_struct *__tun_get(struct tun_file *tfile)
220{ 213{
221 struct tun_struct *tun = NULL; 214 struct tun_struct *tun;
222 215
223 if (atomic_inc_not_zero(&tfile->count)) 216 rcu_read_lock();
224 tun = tfile->tun; 217 tun = rcu_dereference(tfile->tun);
218 if (tun)
219 dev_hold(tun->dev);
220 rcu_read_unlock();
225 221
226 return tun; 222 return tun;
227} 223}
@@ -233,10 +229,7 @@ static struct tun_struct *tun_get(struct file *file)
233 229
234static void tun_put(struct tun_struct *tun) 230static void tun_put(struct tun_struct *tun)
235{ 231{
236 struct tun_file *tfile = tun->tfile; 232 dev_put(tun->dev);
237
238 if (atomic_dec_and_test(&tfile->count))
239 tun_detach(tfile->tun);
240} 233}
241 234
242/* TAP filtering */ 235/* TAP filtering */
@@ -357,14 +350,15 @@ static const struct ethtool_ops tun_ethtool_ops;
357static void tun_net_uninit(struct net_device *dev) 350static void tun_net_uninit(struct net_device *dev)
358{ 351{
359 struct tun_struct *tun = netdev_priv(dev); 352 struct tun_struct *tun = netdev_priv(dev);
360 struct tun_file *tfile = tun->tfile; 353 struct tun_file *tfile = rcu_dereference_protected(tun->tfile,
354 lockdep_rtnl_is_held());
361 355
362 /* Inform the methods they need to stop using the dev. 356 /* Inform the methods they need to stop using the dev.
363 */ 357 */
364 if (tfile) { 358 if (tfile) {
365 wake_up_all(&tfile->wq.wait); 359 wake_up_all(&tfile->wq.wait);
366 if (atomic_dec_and_test(&tfile->count)) 360 __tun_detach(tun);
367 __tun_detach(tun); 361 synchronize_net();
368 } 362 }
369} 363}
370 364
@@ -386,14 +380,16 @@ static int tun_net_close(struct net_device *dev)
386static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) 380static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
387{ 381{
388 struct tun_struct *tun = netdev_priv(dev); 382 struct tun_struct *tun = netdev_priv(dev);
389 struct tun_file *tfile = tun->tfile; 383 struct tun_file *tfile;
390
391 tun_debug(KERN_INFO, tun, "tun_net_xmit %d\n", skb->len);
392 384
385 rcu_read_lock();
386 tfile = rcu_dereference(tun->tfile);
393 /* Drop packet if interface is not attached */ 387 /* Drop packet if interface is not attached */
394 if (!tfile) 388 if (!tfile)
395 goto drop; 389 goto drop;
396 390
391 tun_debug(KERN_INFO, tun, "tun_net_xmit %d\n", skb->len);
392
397 /* Drop if the filter does not like it. 393 /* Drop if the filter does not like it.
398 * This is a noop if the filter is disabled. 394 * This is a noop if the filter is disabled.
399 * Filter can be enabled only for the TAP devices. */ 395 * Filter can be enabled only for the TAP devices. */
@@ -435,11 +431,14 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
435 kill_fasync(&tfile->fasync, SIGIO, POLL_IN); 431 kill_fasync(&tfile->fasync, SIGIO, POLL_IN);
436 wake_up_interruptible_poll(&tfile->wq.wait, POLLIN | 432 wake_up_interruptible_poll(&tfile->wq.wait, POLLIN |
437 POLLRDNORM | POLLRDBAND); 433 POLLRDNORM | POLLRDBAND);
434
435 rcu_read_unlock();
438 return NETDEV_TX_OK; 436 return NETDEV_TX_OK;
439 437
440drop: 438drop:
441 dev->stats.tx_dropped++; 439 dev->stats.tx_dropped++;
442 kfree_skb(skb); 440 kfree_skb(skb);
441 rcu_read_unlock();
443 return NETDEV_TX_OK; 442 return NETDEV_TX_OK;
444} 443}
445 444
@@ -1089,7 +1088,6 @@ static int tun_sendmsg(struct kiocb *iocb, struct socket *sock,
1089 1088
1090 if (!tun) 1089 if (!tun)
1091 return -EBADFD; 1090 return -EBADFD;
1092
1093 ret = tun_get_user(tun, tfile, m->msg_control, m->msg_iov, total_len, 1091 ret = tun_get_user(tun, tfile, m->msg_control, m->msg_iov, total_len,
1094 m->msg_iovlen, m->msg_flags & MSG_DONTWAIT); 1092 m->msg_iovlen, m->msg_flags & MSG_DONTWAIT);
1095 tun_put(tun); 1093 tun_put(tun);
@@ -1662,8 +1660,7 @@ static int tun_chr_open(struct inode *inode, struct file * file)
1662 &tun_proto); 1660 &tun_proto);
1663 if (!tfile) 1661 if (!tfile)
1664 return -ENOMEM; 1662 return -ENOMEM;
1665 atomic_set(&tfile->count, 0); 1663 rcu_assign_pointer(tfile->tun, NULL);
1666 tfile->tun = NULL;
1667 tfile->net = get_net(current->nsproxy->net_ns); 1664 tfile->net = get_net(current->nsproxy->net_ns);
1668 tfile->flags = 0; 1665 tfile->flags = 0;
1669 1666
@@ -1691,7 +1688,9 @@ static int tun_chr_close(struct inode *inode, struct file *file)
1691 struct tun_struct *tun; 1688 struct tun_struct *tun;
1692 struct net *net = tfile->net; 1689 struct net *net = tfile->net;
1693 1690
1694 tun = __tun_get(tfile); 1691 rtnl_lock();
1692
1693 tun = rcu_dereference_protected(tfile->tun, lockdep_rtnl_is_held());
1695 if (tun) { 1694 if (tun) {
1696 struct net_device *dev = tun->dev; 1695 struct net_device *dev = tun->dev;
1697 1696
@@ -1699,18 +1698,20 @@ static int tun_chr_close(struct inode *inode, struct file *file)
1699 1698
1700 __tun_detach(tun); 1699 __tun_detach(tun);
1701 1700
1701 synchronize_net();
1702
1702 /* If desirable, unregister the netdevice. */ 1703 /* If desirable, unregister the netdevice. */
1703 if (!(tun->flags & TUN_PERSIST)) { 1704 if (!(tun->flags & TUN_PERSIST)) {
1704 rtnl_lock();
1705 if (dev->reg_state == NETREG_REGISTERED) 1705 if (dev->reg_state == NETREG_REGISTERED)
1706 unregister_netdevice(dev); 1706 unregister_netdevice(dev);
1707 rtnl_unlock();
1708 } 1707 }
1709 1708
1710 /* drop the reference that netdevice holds */ 1709 /* drop the reference that netdevice holds */
1711 sock_put(&tfile->sk); 1710 sock_put(&tfile->sk);
1712 } 1711 }
1713 1712
1713 rtnl_unlock();
1714
1714 /* drop the reference that file holds */ 1715 /* drop the reference that file holds */
1715 BUG_ON(!test_bit(SOCK_EXTERNALLY_ALLOCATED, 1716 BUG_ON(!test_bit(SOCK_EXTERNALLY_ALLOCATED,
1716 &tfile->socket.flags)); 1717 &tfile->socket.flags));
@@ -1842,14 +1843,12 @@ static void tun_cleanup(void)
1842 * holding a reference to the file for as long as the socket is in use. */ 1843 * holding a reference to the file for as long as the socket is in use. */
1843struct socket *tun_get_socket(struct file *file) 1844struct socket *tun_get_socket(struct file *file)
1844{ 1845{
1845 struct tun_struct *tun; 1846 struct tun_file *tfile;
1846 struct tun_file *tfile = file->private_data;
1847 if (file->f_op != &tun_fops) 1847 if (file->f_op != &tun_fops)
1848 return ERR_PTR(-EINVAL); 1848 return ERR_PTR(-EINVAL);
1849 tun = tun_get(file); 1849 tfile = file->private_data;
1850 if (!tun) 1850 if (!tfile)
1851 return ERR_PTR(-EBADFD); 1851 return ERR_PTR(-EBADFD);
1852 tun_put(tun);
1853 return &tfile->socket; 1852 return &tfile->socket;
1854} 1853}
1855EXPORT_SYMBOL_GPL(tun_get_socket); 1854EXPORT_SYMBOL_GPL(tun_get_socket);