Skip to content

Commit 9e641bd

Browse files
Xi WangEric Dumazet
authored andcommitted
net-tun: restructure tun_do_read for better sleep/wakeup efficiency
tun_do_read always adds current thread to wait queue, even if a packet is ready to read. This is inefficient because both sleeper and waker want to acquire the wait queue spin lock when packet rate is high. We restructure the read function and use common kernel networking routines to handle receive, sleep and wakeup. With the change available packets are checked first before the reading thread is added to the wait queue. Ran performance tests with the following configuration: - my packet generator -> tap1 -> br0 -> tap0 -> my packet consumer - sender pinned to one core and receiver pinned to another core - sender send small UDP packets (64 bytes total) as fast as it can - sandy bridge cores - throughput are receiver side goodput numbers The results are baseline: 731k pkts/sec, cpu utilization at 1.50 cpus changed: 783k pkts/sec, cpu utilization at 1.53 cpus The performance difference is largely determined by packet rate and inter-cpu communication cost. For example, if the sender and receiver are pinned to different cpu sockets, the results are baseline: 558k pkts/sec, cpu utilization at 1.71 cpus changed: 690k pkts/sec, cpu utilization at 1.67 cpus Co-authored-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Xi Wang <xii@google.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent f98f89a commit 9e641bd

File tree

1 file changed

+16
-38
lines changed

1 file changed

+16
-38
lines changed

drivers/net/tun.c

Lines changed: 16 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -498,12 +498,12 @@ static void tun_detach_all(struct net_device *dev)
498498
for (i = 0; i < n; i++) {
499499
tfile = rtnl_dereference(tun->tfiles[i]);
500500
BUG_ON(!tfile);
501-
wake_up_all(&tfile->wq.wait);
501+
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
502502
RCU_INIT_POINTER(tfile->tun, NULL);
503503
--tun->numqueues;
504504
}
505505
list_for_each_entry(tfile, &tun->disabled, next) {
506-
wake_up_all(&tfile->wq.wait);
506+
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
507507
RCU_INIT_POINTER(tfile->tun, NULL);
508508
}
509509
BUG_ON(tun->numqueues != 0);
@@ -807,8 +807,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
807807
/* Notify and wake up reader process */
808808
if (tfile->flags & TUN_FASYNC)
809809
kill_fasync(&tfile->fasync, SIGIO, POLL_IN);
810-
wake_up_interruptible_poll(&tfile->wq.wait, POLLIN |
811-
POLLRDNORM | POLLRDBAND);
810+
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
812811

813812
rcu_read_unlock();
814813
return NETDEV_TX_OK;
@@ -965,7 +964,7 @@ static unsigned int tun_chr_poll(struct file *file, poll_table *wait)
965964

966965
tun_debug(KERN_INFO, tun, "tun_chr_poll\n");
967966

968-
poll_wait(file, &tfile->wq.wait, wait);
967+
poll_wait(file, sk_sleep(sk), wait);
969968

970969
if (!skb_queue_empty(&sk->sk_receive_queue))
971970
mask |= POLLIN | POLLRDNORM;
@@ -1330,47 +1329,26 @@ static ssize_t tun_put_user(struct tun_struct *tun,
13301329
static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
13311330
const struct iovec *iv, ssize_t len, int noblock)
13321331
{
1333-
DECLARE_WAITQUEUE(wait, current);
13341332
struct sk_buff *skb;
13351333
ssize_t ret = 0;
1334+
int peeked, err, off = 0;
13361335

13371336
tun_debug(KERN_INFO, tun, "tun_do_read\n");
13381337

1339-
if (unlikely(!noblock))
1340-
add_wait_queue(&tfile->wq.wait, &wait);
1341-
while (len) {
1342-
if (unlikely(!noblock))
1343-
current->state = TASK_INTERRUPTIBLE;
1338+
if (!len)
1339+
return ret;
13441340

1345-
/* Read frames from the queue */
1346-
if (!(skb = skb_dequeue(&tfile->socket.sk->sk_receive_queue))) {
1347-
if (noblock) {
1348-
ret = -EAGAIN;
1349-
break;
1350-
}
1351-
if (signal_pending(current)) {
1352-
ret = -ERESTARTSYS;
1353-
break;
1354-
}
1355-
if (tun->dev->reg_state != NETREG_REGISTERED) {
1356-
ret = -EIO;
1357-
break;
1358-
}
1359-
1360-
/* Nothing to read, let's sleep */
1361-
schedule();
1362-
continue;
1363-
}
1341+
if (tun->dev->reg_state != NETREG_REGISTERED)
1342+
return -EIO;
13641343

1344+
/* Read frames from queue */
1345+
skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0,
1346+
&peeked, &off, &err);
1347+
if (skb) {
13651348
ret = tun_put_user(tun, tfile, skb, iv, len);
13661349
kfree_skb(skb);
1367-
break;
1368-
}
1369-
1370-
if (unlikely(!noblock)) {
1371-
current->state = TASK_RUNNING;
1372-
remove_wait_queue(&tfile->wq.wait, &wait);
1373-
}
1350+
} else
1351+
ret = err;
13741352

13751353
return ret;
13761354
}
@@ -2199,8 +2177,8 @@ static int tun_chr_open(struct inode *inode, struct file * file)
21992177
tfile->flags = 0;
22002178
tfile->ifindex = 0;
22012179

2202-
rcu_assign_pointer(tfile->socket.wq, &tfile->wq);
22032180
init_waitqueue_head(&tfile->wq.wait);
2181+
RCU_INIT_POINTER(tfile->socket.wq, &tfile->wq);
22042182

22052183
tfile->socket.file = file;
22062184
tfile->socket.ops = &tun_socket_ops;

0 commit comments

Comments
 (0)