* queueing.
*/
+static void __netlink_release(struct sock *sk)
+{
+ /*
+ * 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);
+}
+
struct sock *
netlink_kernel_create(struct net *net, int unit, unsigned int groups,
void (*input)(struct sk_buff *skb),
if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))
return NULL;
- if (__netlink_create(net, sock, cb_mutex, unit) < 0)
- goto out_sock_release;
+ /*
+ * We have to just have a reference on the net from sk, but don't
+ * get_net it. Besides, we cannot get and then put the net here.
+ * So we create one inside init_net and the move it to net.
+ */
+
+ if (__netlink_create(&init_net, sock, cb_mutex, unit) < 0)
+ goto out_sock_release_nosk;
+
+ sk = sock->sk;
+ put_net(sk->sk_net);
+ sk->sk_net = net;
if (groups < 32)
groups = 32;
if (!listeners)
goto out_sock_release;
- sk = sock->sk;
sk->sk_data_ready = netlink_data_ready;
if (input)
nlk_sk(sk)->netlink_rcv = input;
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:
kfree(listeners);
+ __netlink_release(sk);
+ return NULL;
+
+out_sock_release_nosk:
sock_release(sock);
return NULL;
}
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);
+ __netlink_release(sk);
}
EXPORT_SYMBOL(netlink_kernel_release);