Commit 16b304f3 authored by Pravin B Shelar's avatar Pravin B Shelar Committed by David S. Miller

netlink: Eliminate kmalloc in netlink dump operation.

Following patch stores struct netlink_callback in netlink_sock
to avoid allocating and freeing it on every netlink dump msg.
Only one dump operation is allowed for a given socket at a time
therefore we can safely convert cb pointer to cb struct inside
netlink_sock.
Signed-off-by: default avatarPravin B Shelar <pshelar@nicira.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent de98ac5e
...@@ -595,7 +595,7 @@ static unsigned int netlink_poll(struct file *file, struct socket *sock, ...@@ -595,7 +595,7 @@ static unsigned int netlink_poll(struct file *file, struct socket *sock,
* for dumps is performed here. A dump is allowed to continue * for dumps is performed here. A dump is allowed to continue
* if at least half the ring is unused. * if at least half the ring is unused.
*/ */
while (nlk->cb != NULL && netlink_dump_space(nlk)) { while (nlk->cb_running && netlink_dump_space(nlk)) {
err = netlink_dump(sk); err = netlink_dump(sk);
if (err < 0) { if (err < 0) {
sk->sk_err = err; sk->sk_err = err;
...@@ -802,18 +802,6 @@ static void netlink_ring_set_copied(struct sock *sk, struct sk_buff *skb) ...@@ -802,18 +802,6 @@ static void netlink_ring_set_copied(struct sock *sk, struct sk_buff *skb)
#define netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, siocb) 0 #define netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, siocb) 0
#endif /* CONFIG_NETLINK_MMAP */ #endif /* CONFIG_NETLINK_MMAP */
static void netlink_destroy_callback(struct netlink_callback *cb)
{
kfree_skb(cb->skb);
kfree(cb);
}
static void netlink_consume_callback(struct netlink_callback *cb)
{
consume_skb(cb->skb);
kfree(cb);
}
static void netlink_skb_destructor(struct sk_buff *skb) static void netlink_skb_destructor(struct sk_buff *skb)
{ {
#ifdef CONFIG_NETLINK_MMAP #ifdef CONFIG_NETLINK_MMAP
...@@ -872,12 +860,12 @@ static void netlink_sock_destruct(struct sock *sk) ...@@ -872,12 +860,12 @@ static void netlink_sock_destruct(struct sock *sk)
{ {
struct netlink_sock *nlk = nlk_sk(sk); struct netlink_sock *nlk = nlk_sk(sk);
if (nlk->cb) { if (nlk->cb_running) {
if (nlk->cb->done) if (nlk->cb.done)
nlk->cb->done(nlk->cb); nlk->cb.done(&nlk->cb);
module_put(nlk->cb->module); module_put(nlk->cb.module);
netlink_destroy_callback(nlk->cb); kfree_skb(nlk->cb.skb);
} }
skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_receive_queue);
...@@ -2350,7 +2338,8 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, ...@@ -2350,7 +2338,8 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
skb_free_datagram(sk, skb); skb_free_datagram(sk, skb);
if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) { if (nlk->cb_running &&
atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) {
ret = netlink_dump(sk); ret = netlink_dump(sk);
if (ret) { if (ret) {
sk->sk_err = ret; sk->sk_err = ret;
...@@ -2566,13 +2555,12 @@ static int netlink_dump(struct sock *sk) ...@@ -2566,13 +2555,12 @@ static int netlink_dump(struct sock *sk)
int alloc_size; int alloc_size;
mutex_lock(nlk->cb_mutex); mutex_lock(nlk->cb_mutex);
if (!nlk->cb_running) {
cb = nlk->cb;
if (cb == NULL) {
err = -EINVAL; err = -EINVAL;
goto errout_skb; goto errout_skb;
} }
cb = &nlk->cb;
alloc_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE); alloc_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE);
if (!netlink_rx_is_mmaped(sk) && if (!netlink_rx_is_mmaped(sk) &&
...@@ -2610,11 +2598,11 @@ static int netlink_dump(struct sock *sk) ...@@ -2610,11 +2598,11 @@ static int netlink_dump(struct sock *sk)
if (cb->done) if (cb->done)
cb->done(cb); cb->done(cb);
nlk->cb = NULL;
mutex_unlock(nlk->cb_mutex);
nlk->cb_running = false;
mutex_unlock(nlk->cb_mutex);
module_put(cb->module); module_put(cb->module);
netlink_consume_callback(cb); consume_skb(cb->skb);
return 0; return 0;
errout_skb: errout_skb:
...@@ -2632,59 +2620,51 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, ...@@ -2632,59 +2620,51 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
struct netlink_sock *nlk; struct netlink_sock *nlk;
int ret; int ret;
cb = kzalloc(sizeof(*cb), GFP_KERNEL);
if (cb == NULL)
return -ENOBUFS;
/* Memory mapped dump requests need to be copied to avoid looping /* Memory mapped dump requests need to be copied to avoid looping
* on the pending state in netlink_mmap_sendmsg() while the CB hold * on the pending state in netlink_mmap_sendmsg() while the CB hold
* a reference to the skb. * a reference to the skb.
*/ */
if (netlink_skb_is_mmaped(skb)) { if (netlink_skb_is_mmaped(skb)) {
skb = skb_copy(skb, GFP_KERNEL); skb = skb_copy(skb, GFP_KERNEL);
if (skb == NULL) { if (skb == NULL)
kfree(cb);
return -ENOBUFS; return -ENOBUFS;
}
} else } else
atomic_inc(&skb->users); atomic_inc(&skb->users);
cb->dump = control->dump;
cb->done = control->done;
cb->nlh = nlh;
cb->data = control->data;
cb->module = control->module;
cb->min_dump_alloc = control->min_dump_alloc;
cb->skb = skb;
sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).portid); sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).portid);
if (sk == NULL) { if (sk == NULL) {
netlink_destroy_callback(cb); ret = -ECONNREFUSED;
return -ECONNREFUSED; goto error_free;
} }
nlk = nlk_sk(sk);
nlk = nlk_sk(sk);
mutex_lock(nlk->cb_mutex); mutex_lock(nlk->cb_mutex);
/* A dump is in progress... */ /* A dump is in progress... */
if (nlk->cb) { if (nlk->cb_running) {
mutex_unlock(nlk->cb_mutex);
netlink_destroy_callback(cb);
ret = -EBUSY; ret = -EBUSY;
goto out; goto error_unlock;
} }
/* add reference of module which cb->dump belongs to */ /* add reference of module which cb->dump belongs to */
if (!try_module_get(cb->module)) { if (!try_module_get(control->module)) {
mutex_unlock(nlk->cb_mutex);
netlink_destroy_callback(cb);
ret = -EPROTONOSUPPORT; ret = -EPROTONOSUPPORT;
goto out; goto error_unlock;
} }
nlk->cb = cb; cb = &nlk->cb;
memset(cb, 0, sizeof(*cb));
cb->dump = control->dump;
cb->done = control->done;
cb->nlh = nlh;
cb->data = control->data;
cb->module = control->module;
cb->min_dump_alloc = control->min_dump_alloc;
cb->skb = skb;
nlk->cb_running = true;
mutex_unlock(nlk->cb_mutex); mutex_unlock(nlk->cb_mutex);
ret = netlink_dump(sk); ret = netlink_dump(sk);
out:
sock_put(sk); sock_put(sk);
if (ret) if (ret)
...@@ -2694,6 +2674,13 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, ...@@ -2694,6 +2674,13 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
* signal not to send ACK even if it was requested. * signal not to send ACK even if it was requested.
*/ */
return -EINTR; return -EINTR;
error_unlock:
sock_put(sk);
mutex_unlock(nlk->cb_mutex);
error_free:
kfree_skb(skb);
return ret;
} }
EXPORT_SYMBOL(__netlink_dump_start); EXPORT_SYMBOL(__netlink_dump_start);
...@@ -2916,14 +2903,14 @@ static int netlink_seq_show(struct seq_file *seq, void *v) ...@@ -2916,14 +2903,14 @@ static int netlink_seq_show(struct seq_file *seq, void *v)
struct sock *s = v; struct sock *s = v;
struct netlink_sock *nlk = nlk_sk(s); struct netlink_sock *nlk = nlk_sk(s);
seq_printf(seq, "%pK %-3d %-6u %08x %-8d %-8d %pK %-8d %-8d %-8lu\n", seq_printf(seq, "%pK %-3d %-6u %08x %-8d %-8d %d %-8d %-8d %-8lu\n",
s, s,
s->sk_protocol, s->sk_protocol,
nlk->portid, nlk->portid,
nlk->groups ? (u32)nlk->groups[0] : 0, nlk->groups ? (u32)nlk->groups[0] : 0,
sk_rmem_alloc_get(s), sk_rmem_alloc_get(s),
sk_wmem_alloc_get(s), sk_wmem_alloc_get(s),
nlk->cb, nlk->cb_running,
atomic_read(&s->sk_refcnt), atomic_read(&s->sk_refcnt),
atomic_read(&s->sk_drops), atomic_read(&s->sk_drops),
sock_i_ino(s) sock_i_ino(s)
......
...@@ -32,7 +32,8 @@ struct netlink_sock { ...@@ -32,7 +32,8 @@ struct netlink_sock {
unsigned long *groups; unsigned long *groups;
unsigned long state; unsigned long state;
wait_queue_head_t wait; wait_queue_head_t wait;
struct netlink_callback *cb; bool cb_running;
struct netlink_callback cb;
struct mutex *cb_mutex; struct mutex *cb_mutex;
struct mutex cb_def_mutex; struct mutex cb_def_mutex;
void (*netlink_rcv)(struct sk_buff *skb); void (*netlink_rcv)(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