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: Pravin B Shelar <pshelar@nicira.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
de98ac5eee
commit
16b304f340
|
@ -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)
|
||||||
#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)
|
||||||
{
|
{
|
||||||
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,
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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,23 +2620,38 @@ 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);
|
||||||
|
|
||||||
|
sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).portid);
|
||||||
|
if (sk == NULL) {
|
||||||
|
ret = -ECONNREFUSED;
|
||||||
|
goto error_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlk = nlk_sk(sk);
|
||||||
|
mutex_lock(nlk->cb_mutex);
|
||||||
|
/* A dump is in progress... */
|
||||||
|
if (nlk->cb_running) {
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto error_unlock;
|
||||||
|
}
|
||||||
|
/* add reference of module which cb->dump belongs to */
|
||||||
|
if (!try_module_get(control->module)) {
|
||||||
|
ret = -EPROTONOSUPPORT;
|
||||||
|
goto error_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
cb = &nlk->cb;
|
||||||
|
memset(cb, 0, sizeof(*cb));
|
||||||
cb->dump = control->dump;
|
cb->dump = control->dump;
|
||||||
cb->done = control->done;
|
cb->done = control->done;
|
||||||
cb->nlh = nlh;
|
cb->nlh = nlh;
|
||||||
|
@ -2657,34 +2660,11 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
|
||||||
cb->min_dump_alloc = control->min_dump_alloc;
|
cb->min_dump_alloc = control->min_dump_alloc;
|
||||||
cb->skb = skb;
|
cb->skb = skb;
|
||||||
|
|
||||||
sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).portid);
|
nlk->cb_running = true;
|
||||||
if (sk == NULL) {
|
|
||||||
netlink_destroy_callback(cb);
|
|
||||||
return -ECONNREFUSED;
|
|
||||||
}
|
|
||||||
nlk = nlk_sk(sk);
|
|
||||||
|
|
||||||
mutex_lock(nlk->cb_mutex);
|
|
||||||
/* A dump is in progress... */
|
|
||||||
if (nlk->cb) {
|
|
||||||
mutex_unlock(nlk->cb_mutex);
|
|
||||||
netlink_destroy_callback(cb);
|
|
||||||
ret = -EBUSY;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* add reference of module which cb->dump belongs to */
|
|
||||||
if (!try_module_get(cb->module)) {
|
|
||||||
mutex_unlock(nlk->cb_mutex);
|
|
||||||
netlink_destroy_callback(cb);
|
|
||||||
ret = -EPROTONOSUPPORT;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
nlk->cb = cb;
|
|
||||||
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 @@ out:
|
||||||
* 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)
|
||||||
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 {
|
||||||
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);
|
||||||
|
|
Loading…
Reference in New Issue