--- a/net/netlink/af_netlink.c 2004-08-28 05:35:35.000000000 +0200 +++ b/net/netlink/af_netlink.c 2004-08-29 16:00:46.000000000 +0200 @@ -42,6 +42,7 @@ #include #include #include +#include #define Nprintk(a...) @@ -63,6 +64,15 @@ void (*data_ready)(struct sock *sk, int bytes); }; +struct netlink_work +{ + struct sock *sk; + int len; + struct tq_struct work; +}; + +static DECLARE_TASK_QUEUE(tq_netlink); +static DECLARE_WAIT_QUEUE_HEAD(netlink_thread_wait); static struct sock *nl_table[MAX_LINKS]; static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait); static unsigned nl_nonroot[MAX_LINKS]; @@ -81,6 +91,15 @@ static struct notifier_block *netlink_chain; +void netlink_tq_handler(void *data) +{ + struct netlink_work *work = data; + + work->sk->data_ready(work->sk, work->len); + sock_put(work->sk); + kfree(work); +} + static void netlink_sock_destruct(struct sock *sk) { skb_queue_purge(&sk->receive_queue); @@ -437,6 +456,8 @@ if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf || test_bit(0, &sk->protinfo.af_netlink->state)) { + struct task_struct *client; + if (!timeo) { if (ssk->protinfo.af_netlink->pid == 0) netlink_overrun(sk); @@ -445,6 +466,19 @@ return -EAGAIN; } + if (!sk->protinfo.af_netlink->pid) { + /* Kernel is sending information to user space + * and socket buffer is full: Wake up user */ + + client = find_task_by_pid(sk->protinfo.af_netlink->pid); + if (!client) { + sock_put(sk); + kfree_skb(skb); + return -EAGAIN; + } + wake_up_process(client); + } + __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&sk->protinfo.af_netlink->wait, &wait); @@ -467,8 +501,26 @@ skb_orphan(skb); skb_set_owner_r(skb, sk); skb_queue_tail(&sk->receive_queue, skb); - sk->data_ready(sk, len); - sock_put(sk); + + if (!sk->protinfo.af_netlink->pid) { + struct netlink_work *nlwork = + kmalloc(sizeof(struct netlink_work), GFP_KERNEL); + + if (!nlwork) { + sock_put(sk); + return -EAGAIN; + } + + INIT_TQUEUE(&nlwork->work, netlink_tq_handler, nlwork); + nlwork->sk = sk; + nlwork->len = len; + queue_task(&nlwork->work, &tq_netlink); + wake_up(&netlink_thread_wait); + } else { + sk->data_ready(sk, len); + sock_put(sk); + } + return len; no_dst: @@ -490,7 +542,22 @@ skb_orphan(skb); skb_set_owner_r(skb, sk); skb_queue_tail(&sk->receive_queue, skb); - sk->data_ready(sk, skb->len); + + if (!sk->protinfo.af_netlink->pid) { + struct netlink_work *nlwork = + kmalloc(sizeof(struct netlink_work), GFP_KERNEL); + + if (!nlwork) + return -EAGAIN; + + INIT_TQUEUE(&nlwork->work, netlink_tq_handler, nlwork); + nlwork->sk = sk; + nlwork->len = skb->len; + queue_task(&nlwork->work, &tq_netlink); + wake_up(&netlink_thread_wait); + } else + sk->data_ready(sk, skb->len); + return 0; } return -1; @@ -534,11 +601,12 @@ netlink_overrun(sk); /* Clone failed. Notify ALL listeners. */ failure = 1; + sock_put(sk); } else if (netlink_broadcast_deliver(sk, skb2)) { netlink_overrun(sk); + sock_put(sk); } else skb2 = NULL; - sock_put(sk); } netlink_unlock_table(); @@ -868,6 +936,26 @@ netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); } +int netlink_thread(void *unused) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + daemonize(); + strcpy(tsk->comm, "netlink"); + sigfillset(&tsk->blocked); + mb(); + + for (;;) { + run_task_queue(&tq_netlink); + + __set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&netlink_thread_wait, &wait); + schedule(); + __set_current_state(TASK_RUNNING); + remove_wait_queue(&netlink_thread_wait, &wait); + } +} #ifdef NL_EMULATE_DEV @@ -1027,6 +1115,8 @@ #ifdef CONFIG_PROC_FS create_proc_read_entry("net/netlink", 0, 0, netlink_read_proc, NULL); #endif + kernel_thread(netlink_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL); + return 0; }