Commit e7a9eee5 authored by David S. Miller's avatar David S. Miller

Merge branch 'tipc_netns_leak'

Ying Xue says:

====================
tipc: fix netns refcnt leak

The series aims to eliminate the issue of netns refcount leak. But
during fixing it, another two additional problems are found. So all
of known issues associated with the netns refcnt leak are resolved
at the same time in the patchset.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 52841430 2b9bb7f3
......@@ -811,8 +811,8 @@ static void tipc_purge_publications(struct net *net, struct name_seq *seq)
sseq = seq->sseqs;
info = sseq->info;
list_for_each_entry_safe(publ, safe, &info->zone_list, zone_list) {
tipc_nametbl_remove_publ(net, publ->type, publ->lower,
publ->node, publ->ref, publ->key);
tipc_nameseq_remove_publ(net, seq, publ->lower, publ->node,
publ->ref, publ->key);
kfree_rcu(publ, rcu);
}
hlist_del_init_rcu(&seq->ns_list);
......
......@@ -37,11 +37,13 @@
#include "core.h"
#include "socket.h"
#include <net/sock.h>
#include <linux/module.h>
/* Number of messages to send before rescheduling */
#define MAX_SEND_MSG_COUNT 25
#define MAX_RECV_MSG_COUNT 25
#define CF_CONNECTED 1
#define CF_SERVER 2
#define sock2con(x) ((struct tipc_conn *)(x)->sk_user_data)
......@@ -88,9 +90,19 @@ static void tipc_clean_outqueues(struct tipc_conn *con);
static void tipc_conn_kref_release(struct kref *kref)
{
struct tipc_conn *con = container_of(kref, struct tipc_conn, kref);
struct sockaddr_tipc *saddr = con->server->saddr;
struct socket *sock = con->sock;
struct sock *sk;
if (con->sock) {
tipc_sock_release_local(con->sock);
if (sock) {
sk = sock->sk;
if (test_bit(CF_SERVER, &con->flags)) {
__module_get(sock->ops->owner);
__module_get(sk->sk_prot_creator->owner);
}
saddr->scope = -TIPC_NODE_SCOPE;
kernel_bind(sock, (struct sockaddr *)saddr, sizeof(*saddr));
sk_release_kernel(sk);
con->sock = NULL;
}
......@@ -281,7 +293,7 @@ static int tipc_accept_from_sock(struct tipc_conn *con)
struct tipc_conn *newcon;
int ret;
ret = tipc_sock_accept_local(sock, &newsock, O_NONBLOCK);
ret = kernel_accept(sock, &newsock, O_NONBLOCK);
if (ret < 0)
return ret;
......@@ -309,9 +321,12 @@ static struct socket *tipc_create_listen_sock(struct tipc_conn *con)
struct socket *sock = NULL;
int ret;
ret = tipc_sock_create_local(s->net, s->type, &sock);
ret = sock_create_kern(AF_TIPC, SOCK_SEQPACKET, 0, &sock);
if (ret < 0)
return NULL;
sk_change_net(sock->sk, s->net);
ret = kernel_setsockopt(sock, SOL_TIPC, TIPC_IMPORTANCE,
(char *)&s->imp, sizeof(s->imp));
if (ret < 0)
......@@ -337,11 +352,31 @@ static struct socket *tipc_create_listen_sock(struct tipc_conn *con)
pr_err("Unknown socket type %d\n", s->type);
goto create_err;
}
/* As server's listening socket owner and creator is the same module,
* we have to decrease TIPC module reference count to guarantee that
* it remains zero after the server socket is created, otherwise,
* executing "rmmod" command is unable to make TIPC module deleted
* after TIPC module is inserted successfully.
*
* However, the reference count is ever increased twice in
* sock_create_kern(): one is to increase the reference count of owner
* of TIPC socket's proto_ops struct; another is to increment the
* reference count of owner of TIPC proto struct. Therefore, we must
* decrement the module reference count twice to ensure that it keeps
* zero after server's listening socket is created. Of course, we
* must bump the module reference count twice as well before the socket
* is closed.
*/
module_put(sock->ops->owner);
module_put(sock->sk->sk_prot_creator->owner);
set_bit(CF_SERVER, &con->flags);
return sock;
create_err:
sock_release(sock);
con->sock = NULL;
kernel_sock_shutdown(sock, SHUT_RDWR);
sk_release_kernel(sock->sk);
return NULL;
}
......
......@@ -121,9 +121,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz);
static const struct proto_ops packet_ops;
static const struct proto_ops stream_ops;
static const struct proto_ops msg_ops;
static struct proto tipc_proto;
static struct proto tipc_proto_kern;
static const struct nla_policy tipc_nl_sock_policy[TIPC_NLA_SOCK_MAX + 1] = {
[TIPC_NLA_SOCK_UNSPEC] = { .type = NLA_UNSPEC },
......@@ -341,11 +339,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
}
/* Allocate socket's protocol area */
if (!kern)
sk = sk_alloc(net, AF_TIPC, GFP_KERNEL, &tipc_proto);
else
sk = sk_alloc(net, AF_TIPC, GFP_KERNEL, &tipc_proto_kern);
sk = sk_alloc(net, AF_TIPC, GFP_KERNEL, &tipc_proto);
if (sk == NULL)
return -ENOMEM;
......@@ -383,75 +377,6 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
return 0;
}
/**
* tipc_sock_create_local - create TIPC socket from inside TIPC module
* @type: socket type - SOCK_RDM or SOCK_SEQPACKET
*
* We cannot use sock_creat_kern here because it bumps module user count.
* Since socket owner and creator is the same module we must make sure
* that module count remains zero for module local sockets, otherwise
* we cannot do rmmod.
*
* Returns 0 on success, errno otherwise
*/
int tipc_sock_create_local(struct net *net, int type, struct socket **res)
{
int rc;
rc = sock_create_lite(AF_TIPC, type, 0, res);
if (rc < 0) {
pr_err("Failed to create kernel socket\n");
return rc;
}
tipc_sk_create(net, *res, 0, 1);
return 0;
}
/**
* tipc_sock_release_local - release socket created by tipc_sock_create_local
* @sock: the socket to be released.
*
* Module reference count is not incremented when such sockets are created,
* so we must keep it from being decremented when they are released.
*/
void tipc_sock_release_local(struct socket *sock)
{
tipc_release(sock);
sock->ops = NULL;
sock_release(sock);
}
/**
* tipc_sock_accept_local - accept a connection on a socket created
* with tipc_sock_create_local. Use this function to avoid that
* module reference count is inadvertently incremented.
*
* @sock: the accepting socket
* @newsock: reference to the new socket to be created
* @flags: socket flags
*/
int tipc_sock_accept_local(struct socket *sock, struct socket **newsock,
int flags)
{
struct sock *sk = sock->sk;
int ret;
ret = sock_create_lite(sk->sk_family, sk->sk_type,
sk->sk_protocol, newsock);
if (ret < 0)
return ret;
ret = tipc_accept(sock, *newsock, flags);
if (ret < 0) {
sock_release(*newsock);
return ret;
}
(*newsock)->ops = sock->ops;
return ret;
}
static void tipc_sk_callback(struct rcu_head *head)
{
struct tipc_sock *tsk = container_of(head, struct tipc_sock, rcu);
......@@ -2608,12 +2533,6 @@ static struct proto tipc_proto = {
.sysctl_rmem = sysctl_tipc_rmem
};
static struct proto tipc_proto_kern = {
.name = "TIPC",
.obj_size = sizeof(struct tipc_sock),
.sysctl_rmem = sysctl_tipc_rmem
};
/**
* tipc_socket_init - initialize TIPC socket interface
*
......
......@@ -44,10 +44,6 @@
SKB_TRUESIZE(TIPC_MAX_USER_MSG_SIZE))
int tipc_socket_init(void);
void tipc_socket_stop(void);
int tipc_sock_create_local(struct net *net, int type, struct socket **res);
void tipc_sock_release_local(struct socket *sock);
int tipc_sock_accept_local(struct socket *sock, struct socket **newsock,
int flags);
int tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq);
void tipc_sk_mcast_rcv(struct net *net, struct sk_buff_head *arrvq,
struct sk_buff_head *inputq);
......
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