/* Get message from skb (based on rtnetlink_rcv_skb).  Each message is
  * processed by audit_receive_msg.  Malformed skbs with wrong length are
  * discarded silently.  */
-static int audit_receive_skb(struct sk_buff *skb)
+static void audit_receive_skb(struct sk_buff *skb)
 {
        int             err;
        struct nlmsghdr *nlh;
        while (skb->len >= NLMSG_SPACE(0)) {
                nlh = (struct nlmsghdr *)skb->data;
                if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
-                       return 0;
+                       return;
                rlen = NLMSG_ALIGN(nlh->nlmsg_len);
                if (rlen > skb->len)
                        rlen = skb->len;
                        netlink_ack(skb, nlh, 0);
                skb_pull(skb, rlen);
        }
-       return 0;
 }
 
 /* Receive messages from netlink socket. */
 static void audit_receive(struct sock *sk, int length)
 {
        struct sk_buff  *skb;
+       unsigned int qlen;
 
-       if (down_trylock(&audit_netlink_sem))
-               return;
+       down(&audit_netlink_sem);
 
-                               /* FIXME: this must not cause starvation */
-       while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
-               if (audit_receive_skb(skb) && skb->len)
-                       skb_queue_head(&sk->sk_receive_queue, skb);
-               else
-                       kfree_skb(skb);
+       for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
+               skb = skb_dequeue(&sk->sk_receive_queue);
+               audit_receive_skb(skb);
+               kfree_skb(skb);
        }
        up(&audit_netlink_sem);
 }
 
 
 /*
  *  rtnetlink input queue processing routine:
- *     - try to acquire shared lock. If it is failed, defer processing.
+ *     - process as much as there was in the queue upon entry.
  *     - feed skbs to rtnetlink_rcv_skb, until it refuse a message,
- *       that will occur, when a dump started and/or acquisition of
- *       exclusive lock failed.
+ *       that will occur, when a dump started.
  */
 
 static void rtnetlink_rcv(struct sock *sk, int len)
 {
+       unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
+
        do {
                struct sk_buff *skb;
 
-               if (rtnl_shlock_nowait())
-                       return;
+               rtnl_lock();
+
+               if (qlen > skb_queue_len(&sk->sk_receive_queue))
+                       qlen = skb_queue_len(&sk->sk_receive_queue);
 
-               while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+               while (qlen--) {
+                       skb = skb_dequeue(&sk->sk_receive_queue);
                        if (rtnetlink_rcv_skb(skb)) {
-                               if (skb->len)
+                               if (skb->len) {
                                        skb_queue_head(&sk->sk_receive_queue,
                                                       skb);
-                               else
+                                       qlen++;
+                               } else
                                        kfree_skb(skb);
                                break;
                        }
                up(&rtnl_sem);
 
                netdev_run_todo();
-       } while (rtnl && rtnl->sk_receive_queue.qlen);
+       } while (qlen);
 }
 
 static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] =
 
 static void dnrmg_receive_user_sk(struct sock *sk, int len)
 {
        struct sk_buff *skb;
+       unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
 
-       while((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+       while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) {
                dnrmg_receive_user_skb(skb);
                kfree_skb(skb);
        }
 
 static void
 ipq_rcv_sk(struct sock *sk, int len)
 {
-       do {
-               struct sk_buff *skb;
+       struct sk_buff *skb;
+       unsigned int qlen;
 
-               if (down_trylock(&ipqnl_sem))
-                       return;
+       down(&ipqnl_sem);
                        
-               while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
-                       ipq_rcv_skb(skb);
-                       kfree_skb(skb);
-               }
+       for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
+               skb = skb_dequeue(&sk->sk_receive_queue);
+               ipq_rcv_skb(skb);
+               kfree_skb(skb);
+       }
                
-               up(&ipqnl_sem);
-
-       } while (ipqnl && ipqnl->sk_receive_queue.qlen);
+       up(&ipqnl_sem);
 }
 
 static int
 
 static void tcpdiag_rcv(struct sock *sk, int len)
 {
        struct sk_buff *skb;
+       unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
 
-       while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+       while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) {
                tcpdiag_rcv_skb(skb);
                kfree_skb(skb);
        }
 
 static void
 ipq_rcv_sk(struct sock *sk, int len)
 {
-       do {
-               struct sk_buff *skb;
+       struct sk_buff *skb;
+       unsigned int qlen;
 
-               if (down_trylock(&ipqnl_sem))
-                       return;
+       down(&ipqnl_sem);
                        
-               while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
-                       ipq_rcv_skb(skb);
-                       kfree_skb(skb);
-               }
+       for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
+               skb = skb_dequeue(&sk->sk_receive_queue);
+               ipq_rcv_skb(skb);
+               kfree_skb(skb);
+       }
                
-               up(&ipqnl_sem);
-
-       } while (ipqnl && ipqnl->sk_receive_queue.qlen);
+       up(&ipqnl_sem);
 }
 
 static int
 
 
 static void xfrm_netlink_rcv(struct sock *sk, int len)
 {
+       unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
+
        do {
                struct sk_buff *skb;
 
                down(&xfrm_cfg_sem);
 
-               while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+               if (qlen > skb_queue_len(&sk->sk_receive_queue))
+                       qlen = skb_queue_len(&sk->sk_receive_queue);
+
+               while (qlen--) {
+                       skb = skb_dequeue(&sk->sk_receive_queue);
                        if (xfrm_user_rcv_skb(skb)) {
-                               if (skb->len)
+                               if (skb->len) {
                                        skb_queue_head(&sk->sk_receive_queue,
                                                       skb);
-                               else
+                                       qlen++;
+                               } else
                                        kfree_skb(skb);
                                break;
                        }
 
                up(&xfrm_cfg_sem);
 
-       } while (xfrm_nl && xfrm_nl->sk_receive_queue.qlen);
+       } while (qlen);
 }
 
 static int build_expire(struct sk_buff *skb, struct xfrm_state *x, int hard)