*/
static void netlink_table_grab(void)
+ __acquires(nl_table_lock)
{
write_lock_irq(&nl_table_lock);
}
}
-static inline void netlink_table_ungrab(void)
+static void netlink_table_ungrab(void)
+ __releases(nl_table_lock)
{
write_unlock_irq(&nl_table_lock);
wake_up(&nl_table_wait);
return sk;
}
-static inline struct hlist_head *nl_pid_hash_alloc(size_t size)
+static inline struct hlist_head *nl_pid_hash_zalloc(size_t size)
{
if (size <= PAGE_SIZE)
- return kmalloc(size, GFP_ATOMIC);
+ return kzalloc(size, GFP_ATOMIC);
else
return (struct hlist_head *)
- __get_free_pages(GFP_ATOMIC, get_order(size));
+ __get_free_pages(GFP_ATOMIC | __GFP_ZERO,
+ get_order(size));
}
static inline void nl_pid_hash_free(struct hlist_head *table, size_t size)
size *= 2;
}
- table = nl_pid_hash_alloc(size);
+ table = nl_pid_hash_zalloc(size);
if (!table)
return 0;
- memset(table, 0, size);
otable = hash->table;
hash->table = table;
hash->mask = mask;
netlink_table_grab();
if (netlink_is_kernel(sk)) {
- kfree(nl_table[sk->sk_protocol].listeners);
- nl_table[sk->sk_protocol].module = NULL;
- nl_table[sk->sk_protocol].registered = 0;
+ BUG_ON(nl_table[sk->sk_protocol].registered == 0);
+ if (--nl_table[sk->sk_protocol].registered == 0) {
+ kfree(nl_table[sk->sk_protocol].listeners);
+ nl_table[sk->sk_protocol].module = NULL;
+ nl_table[sk->sk_protocol].registered = 0;
+ }
} else if (nlk->subscriptions)
netlink_update_listeners(sk);
netlink_table_ungrab();
nl_table[unit].registered = 1;
} else {
kfree(listeners);
+ nl_table[unit].registered++;
}
netlink_table_ungrab();
+ /* Do not hold an extra referrence to a namespace as this socket is
+ * internal to a namespace and does not prevent it to stop. */
+ put_net(net);
return sk;
out_sock_release:
}
EXPORT_SYMBOL(netlink_kernel_create);
+
+void
+netlink_kernel_release(struct sock *sk)
+{
+ if (sk == NULL || sk->sk_socket == NULL)
+ return;
+
+ /*
+ * Last sock_put should drop referrence to sk->sk_net. It has already
+ * been dropped in netlink_kernel_create. Taking referrence to stopping
+ * namespace is not an option.
+ * Take referrence to a socket to remove it from netlink lookup table
+ * _alive_ and after that destroy it in the context of init_net.
+ */
+ sock_hold(sk);
+ sock_release(sk->sk_socket);
+
+ sk->sk_net = get_net(&init_net);
+ sock_put(sk);
+}
+EXPORT_SYMBOL(netlink_kernel_release);
+
+
/**
* netlink_change_ngroups - change number of multicast groups
*
}
static void *netlink_seq_start(struct seq_file *seq, loff_t *pos)
+ __acquires(nl_table_lock)
{
read_lock(&nl_table_lock);
return *pos ? netlink_seq_socket_idx(seq, *pos - 1) : SEQ_START_TOKEN;
}
static void netlink_seq_stop(struct seq_file *seq, void *v)
+ __releases(nl_table_lock)
{
read_unlock(&nl_table_lock);
}
for (i = 0; i < MAX_LINKS; i++) {
struct nl_pid_hash *hash = &nl_table[i].hash;
- hash->table = nl_pid_hash_alloc(1 * sizeof(*hash->table));
+ hash->table = nl_pid_hash_zalloc(1 * sizeof(*hash->table));
if (!hash->table) {
while (i-- > 0)
nl_pid_hash_free(nl_table[i].hash.table,
kfree(nl_table);
goto panic;
}
- memset(hash->table, 0, 1 * sizeof(*hash->table));
hash->max_shift = order;
hash->shift = 0;
hash->mask = 0;