Commit ed6dcf4a authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] split netlink_unicast

From: Manfred Spraul <manfred@colorfullife.com>

The attached patch splits netlink_unicast into three steps:

- netlink_getsock{bypid,byfilp}: lookup the destination socket.

- netlink_attachskb: perform the nonblock checks, sleep if the socket
  queue is longer than the limit, etc.

- netlink_sendskb: actually send the skb.

jamal looked over it and didn't see a problem with the netlink change.  The
actual use from ipc/mqueue.c is still open (just send back whatever the C
library passed to mq_notify, add an nlmsghdr or perhaps even make it a
specialized netlink protocol), but the attached patch is independant from
the the message queue change.

(acked by davem)
parent b06d7b4c
...@@ -119,6 +119,13 @@ extern void netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code); ...@@ -119,6 +119,13 @@ extern void netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code);
extern int netlink_register_notifier(struct notifier_block *nb); extern int netlink_register_notifier(struct notifier_block *nb);
extern int netlink_unregister_notifier(struct notifier_block *nb); extern int netlink_unregister_notifier(struct notifier_block *nb);
/* finegrained unicast helpers: */
struct sock *netlink_getsockbypid(struct sock *ssk, u32 pid);
struct sock *netlink_getsockbyfilp(struct file *filp);
int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock, long timeo);
void netlink_detachskb(struct sock *sk, struct sk_buff *skb);
int netlink_sendskb(struct sock *sk, struct sk_buff *skb, int protocol);
/* /*
* skb should fit one page. This choice is good for headerless malloc. * skb should fit one page. This choice is good for headerless malloc.
* *
......
...@@ -415,38 +415,65 @@ static void netlink_overrun(struct sock *sk) ...@@ -415,38 +415,65 @@ static void netlink_overrun(struct sock *sk)
} }
} }
int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock) struct sock *netlink_getsockbypid(struct sock *ssk, u32 pid)
{ {
struct sock *sk;
struct netlink_opt *nlk;
int len = skb->len;
int protocol = ssk->sk_protocol; int protocol = ssk->sk_protocol;
long timeo; struct sock *sock;
DECLARE_WAITQUEUE(wait, current); struct netlink_opt *nlk;
timeo = sock_sndtimeo(ssk, nonblock);
retry: sock = netlink_lookup(protocol, pid);
sk = netlink_lookup(protocol, pid); if (!sock)
if (sk == NULL) return ERR_PTR(-ECONNREFUSED);
goto no_dst;
nlk = nlk_sk(sk);
/* Don't bother queuing skb if kernel socket has no input function */ /* Don't bother queuing skb if kernel socket has no input function */
if (nlk->pid == 0 && !nlk->data_ready) nlk = nlk_sk(sock);
goto no_dst; if (nlk->pid == 0 && !nlk->data_ready) {
sock_put(sock);
return ERR_PTR(-ECONNREFUSED);
}
return sock;
}
struct sock *netlink_getsockbyfilp(struct file *filp)
{
struct inode *inode = filp->f_dentry->d_inode;
struct socket *socket;
struct sock *sock;
if (!inode->i_sock || !(socket = SOCKET_I(inode)))
return ERR_PTR(-ENOTSOCK);
sock = socket->sk;
if (sock->sk_family != AF_NETLINK)
return ERR_PTR(-EINVAL);
sock_hold(sock);
return sock;
}
/*
* Attach a skb to a netlink socket.
* The caller must hold a reference to the destination socket. On error, the
* reference is dropped. The skb is not send to the destination, just all
* all error checks are performed and memory in the queue is reserved.
* Return values:
* < 0: error. skb freed, reference to sock dropped.
* 0: continue
* 1: repeat lookup - reference dropped while waiting for socket memory.
*/
int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock, long timeo)
{
struct netlink_opt *nlk;
nlk = nlk_sk(sk);
#ifdef NL_EMULATE_DEV #ifdef NL_EMULATE_DEV
if (nlk->handler) { if (nlk->handler)
skb_orphan(skb); return 0;
len = nlk->handler(protocol, skb);
sock_put(sk);
return len;
}
#endif #endif
if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
test_bit(0, &nlk->state)) { test_bit(0, &nlk->state)) {
DECLARE_WAITQUEUE(wait, current);
if (!timeo) { if (!timeo) {
if (!nlk->pid) if (!nlk->pid)
netlink_overrun(sk); netlink_overrun(sk);
...@@ -471,19 +498,60 @@ int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock ...@@ -471,19 +498,60 @@ int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock
kfree_skb(skb); kfree_skb(skb);
return sock_intr_errno(timeo); return sock_intr_errno(timeo);
} }
goto retry; return 1;
} }
skb_orphan(skb); skb_orphan(skb);
skb_set_owner_r(skb, sk); skb_set_owner_r(skb, sk);
return 0;
}
int netlink_sendskb(struct sock *sk, struct sk_buff *skb, int protocol)
{
struct netlink_opt *nlk;
int len = skb->len;
nlk = nlk_sk(sk);
#ifdef NL_EMULATE_DEV
if (nlk->handler) {
skb_orphan(skb);
len = nlk->handler(protocol, skb);
sock_put(sk);
return len;
}
#endif
skb_queue_tail(&sk->sk_receive_queue, skb); skb_queue_tail(&sk->sk_receive_queue, skb);
sk->sk_data_ready(sk, len); sk->sk_data_ready(sk, len);
sock_put(sk); sock_put(sk);
return len; return len;
}
no_dst: void netlink_detachskb(struct sock *sk, struct sk_buff *skb)
{
kfree_skb(skb); kfree_skb(skb);
return -ECONNREFUSED; sock_put(sk);
}
int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock)
{
struct sock *sk;
int err;
long timeo;
timeo = sock_sndtimeo(ssk, nonblock);
retry:
sk = netlink_getsockbypid(ssk, pid);
if (IS_ERR(sk)) {
kfree_skb(skb);
return PTR_ERR(skb);
}
err = netlink_attachskb(sk, skb, nonblock, timeo);
if (err == 1)
goto retry;
if (err)
return err;
return netlink_sendskb(sk, skb, ssk->sk_protocol);
} }
static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment