#define NETLINK_KERNEL_SOCKET 0x1
#define NETLINK_RECV_PKTINFO 0x2
+#define NETLINK_BROADCAST_SEND_ERROR 0x4
+#define NETLINK_RECV_NO_ENOBUFS 0x8
static inline struct netlink_sock *nlk_sk(struct sock *sk)
{
static void netlink_overrun(struct sock *sk)
{
- if (!test_and_set_bit(0, &nlk_sk(sk)->state)) {
- sk->sk_err = ENOBUFS;
- sk->sk_error_report(sk);
+ struct netlink_sock *nlk = nlk_sk(sk);
+
+ if (!(nlk->flags & NETLINK_RECV_NO_ENOBUFS)) {
+ if (!test_and_set_bit(0, &nlk_sk(sk)->state)) {
+ sk->sk_err = ENOBUFS;
+ sk->sk_error_report(sk);
+ }
}
+ atomic_inc(&sk->sk_drops);
}
static struct sock *netlink_getsockbypid(struct sock *ssk, u32 pid)
u32 pid;
u32 group;
int failure;
+ int delivery_failure;
int congested;
int delivered;
gfp_t allocation;
netlink_overrun(sk);
/* Clone failed. Notify ALL listeners. */
p->failure = 1;
+ if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
+ p->delivery_failure = 1;
} else if (sk_filter(sk, p->skb2)) {
kfree_skb(p->skb2);
p->skb2 = NULL;
} else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) {
netlink_overrun(sk);
+ if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
+ p->delivery_failure = 1;
} else {
p->congested |= val;
p->delivered = 1;
info.pid = pid;
info.group = group;
info.failure = 0;
+ info.delivery_failure = 0;
info.congested = 0;
info.delivered = 0;
info.allocation = allocation;
netlink_unlock_table();
- if (info.skb2)
- kfree_skb(info.skb2);
+ kfree_skb(info.skb2);
+
+ if (info.delivery_failure)
+ return -ENOBUFS;
if (info.delivered) {
if (info.congested && (allocation & __GFP_WAIT))
yield();
return 0;
}
- if (info.failure)
- return -ENOBUFS;
return -ESRCH;
}
EXPORT_SYMBOL(netlink_broadcast);
read_unlock(&nl_table_lock);
}
+EXPORT_SYMBOL(netlink_set_err);
/* must be called with netlink table grabbed */
static void netlink_update_socket_mc(struct netlink_sock *nlk,
err = 0;
break;
}
+ case NETLINK_BROADCAST_ERROR:
+ if (val)
+ nlk->flags |= NETLINK_BROADCAST_SEND_ERROR;
+ else
+ nlk->flags &= ~NETLINK_BROADCAST_SEND_ERROR;
+ err = 0;
+ break;
+ case NETLINK_NO_ENOBUFS:
+ if (val) {
+ nlk->flags |= NETLINK_RECV_NO_ENOBUFS;
+ clear_bit(0, &nlk->state);
+ wake_up_interruptible(&nlk->wait);
+ } else
+ nlk->flags &= ~NETLINK_RECV_NO_ENOBUFS;
+ err = 0;
+ break;
default:
err = -ENOPROTOOPT;
}
return -EFAULT;
err = 0;
break;
+ case NETLINK_BROADCAST_ERROR:
+ if (len < sizeof(int))
+ return -EINVAL;
+ len = sizeof(int);
+ val = nlk->flags & NETLINK_BROADCAST_SEND_ERROR ? 1 : 0;
+ if (put_user(len, optlen) ||
+ put_user(val, optval))
+ return -EFAULT;
+ err = 0;
+ break;
+ case NETLINK_NO_ENOBUFS:
+ if (len < sizeof(int))
+ return -EINVAL;
+ len = sizeof(int);
+ val = nlk->flags & NETLINK_RECV_NO_ENOBUFS ? 1 : 0;
+ if (put_user(len, optlen) ||
+ put_user(val, optval))
+ return -EFAULT;
+ err = 0;
+ break;
default:
err = -ENOPROTOOPT;
}
static void netlink_destroy_callback(struct netlink_callback *cb)
{
- if (cb->skb)
- kfree_skb(cb->skb);
+ kfree_skb(cb->skb);
kfree(cb);
}
exclude_pid = pid;
}
- /* errors reported via destination sk->sk_err */
- nlmsg_multicast(sk, skb, exclude_pid, group, flags);
+ /* errors reported via destination sk->sk_err, but propagate
+ * delivery errors if NETLINK_BROADCAST_ERROR flag is set */
+ err = nlmsg_multicast(sk, skb, exclude_pid, group, flags);
}
- if (report)
- err = nlmsg_unicast(sk, skb, pid);
+ if (report) {
+ int err2;
+
+ err2 = nlmsg_unicast(sk, skb, pid);
+ if (!err || err == -ESRCH)
+ err = err2;
+ }
return err;
}
if (v == SEQ_START_TOKEN)
seq_puts(seq,
"sk Eth Pid Groups "
- "Rmem Wmem Dump Locks\n");
+ "Rmem Wmem Dump Locks Drops\n");
else {
struct sock *s = v;
struct netlink_sock *nlk = nlk_sk(s);
- seq_printf(seq, "%p %-3d %-6d %08x %-8d %-8d %p %d\n",
+ seq_printf(seq, "%p %-3d %-6d %08x %-8d %-8d %p %-8d %-8d\n",
s,
s->sk_protocol,
nlk->pid,
nlk->groups ? (u32)nlk->groups[0] : 0,
- atomic_read(&s->sk_rmem_alloc),
- atomic_read(&s->sk_wmem_alloc),
+ sk_rmem_alloc_get(s),
+ sk_wmem_alloc_get(s),
nlk->cb,
- atomic_read(&s->sk_refcnt)
+ atomic_read(&s->sk_refcnt),
+ atomic_read(&s->sk_drops)
);
}