net: Avoid extra wakeups of threads blocked in wait_for_packet()

In 2.6.25 we added UDP mem accounting.

This unfortunatly added a penalty when a frame is transmitted, since
we have at TX completion time to call sock_wfree() to perform necessary
memory accounting. This calls sock_def_write_space() and utimately
scheduler if any thread is waiting on the socket.
Thread(s) waiting for an incoming frame was scheduled, then had to sleep
again as event was meaningless.

(All threads waiting on a socket are using same sk_sleep anchor)

This adds lot of extra wakeups and increases latencies, as noted
by Christoph Lameter, and slows down softirq handler.

Reference : http://marc.info/?l=linux-netdev&m=124060437012283&w=2 

Fortunatly, Davide Libenzi recently added concept of keyed wakeups
into kernel, and particularly for sockets (see commit
37e5540b3c 
epoll keyed wakeups: make sockets use keyed wakeups)

Davide goal was to optimize epoll, but this new wakeup infrastructure
can help non epoll users as well, if they care to setup an appropriate
handler.

This patch introduces new DEFINE_WAIT_FUNC() helper and uses it
in wait_for_packet(), so that only relevant event can wakeup a thread
blocked in this function.

Trace of function calls from bnx2 TX completion bnx2_poll_work() is :
__kfree_skb()
 skb_release_head_state()
  sock_wfree()
   sock_def_write_space()
    __wake_up_sync_key()
     __wake_up_common()
      receiver_wake_function() : Stops here since thread is waiting for an INPUT


Reported-by: Christoph Lameter <cl@linux.com>
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Eric Dumazet 2009-04-28 02:24:21 -07:00 committed by David S. Miller
parent 37b607c5ac
commit bf368e4e70
2 changed files with 17 additions and 3 deletions

View File

@ -440,13 +440,15 @@ void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait,
int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key); int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key);
int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key);
#define DEFINE_WAIT(name) \ #define DEFINE_WAIT_FUNC(name, function) \
wait_queue_t name = { \ wait_queue_t name = { \
.private = current, \ .private = current, \
.func = autoremove_wake_function, \ .func = function, \
.task_list = LIST_HEAD_INIT((name).task_list), \ .task_list = LIST_HEAD_INIT((name).task_list), \
} }
#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
#define DEFINE_WAIT_BIT(name, word, bit) \ #define DEFINE_WAIT_BIT(name, word, bit) \
struct wait_bit_queue name = { \ struct wait_bit_queue name = { \
.key = __WAIT_BIT_KEY_INITIALIZER(word, bit), \ .key = __WAIT_BIT_KEY_INITIALIZER(word, bit), \

View File

@ -64,13 +64,25 @@ static inline int connection_based(struct sock *sk)
return sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM; return sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM;
} }
static int receiver_wake_function(wait_queue_t *wait, unsigned mode, int sync,
void *key)
{
unsigned long bits = (unsigned long)key;
/*
* Avoid a wakeup if event not interesting for us
*/
if (bits && !(bits & (POLLIN | POLLERR)))
return 0;
return autoremove_wake_function(wait, mode, sync, key);
}
/* /*
* Wait for a packet.. * Wait for a packet..
*/ */
static int wait_for_packet(struct sock *sk, int *err, long *timeo_p) static int wait_for_packet(struct sock *sk, int *err, long *timeo_p)
{ {
int error; int error;
DEFINE_WAIT(wait); DEFINE_WAIT_FUNC(wait, receiver_wake_function);
prepare_to_wait_exclusive(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); prepare_to_wait_exclusive(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);