Commit c809195f authored by Sowmini Varadhan's avatar Sowmini Varadhan Committed by David S. Miller

rds: clean up loopback rds_connections on netns deletion

The RDS core module creates rds_connections based on callbacks
from rds_loop_transport when sending/receiving packets to local
addresses.

These connections will need to be cleaned up when they are
created from a netns that is not init_net, and that netns is deleted.

Add the changes aligned with the changes from
commit ebeeb1ad ("rds: tcp: use rds_destroy_pending() to synchronize
netns/module teardown and rds connection/workq management") for
rds_loop_transport

Reported-and-tested-by: syzbot+4c20b3866171ce8441d2@syzkaller.appspotmail.com
Acked-by: default avatarSantosh Shilimkar <santosh.shilimkar@oracle.com>
Signed-off-by: default avatarSowmini Varadhan <sowmini.varadhan@oracle.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 094bdadd
...@@ -659,11 +659,19 @@ static void rds_conn_info(struct socket *sock, unsigned int len, ...@@ -659,11 +659,19 @@ static void rds_conn_info(struct socket *sock, unsigned int len,
int rds_conn_init(void) int rds_conn_init(void)
{ {
int ret;
ret = rds_loop_net_init(); /* register pernet callback */
if (ret)
return ret;
rds_conn_slab = kmem_cache_create("rds_connection", rds_conn_slab = kmem_cache_create("rds_connection",
sizeof(struct rds_connection), sizeof(struct rds_connection),
0, 0, NULL); 0, 0, NULL);
if (!rds_conn_slab) if (!rds_conn_slab) {
rds_loop_net_exit();
return -ENOMEM; return -ENOMEM;
}
rds_info_register_func(RDS_INFO_CONNECTIONS, rds_conn_info); rds_info_register_func(RDS_INFO_CONNECTIONS, rds_conn_info);
rds_info_register_func(RDS_INFO_SEND_MESSAGES, rds_info_register_func(RDS_INFO_SEND_MESSAGES,
...@@ -676,6 +684,7 @@ int rds_conn_init(void) ...@@ -676,6 +684,7 @@ int rds_conn_init(void)
void rds_conn_exit(void) void rds_conn_exit(void)
{ {
rds_loop_net_exit(); /* unregister pernet callback */
rds_loop_exit(); rds_loop_exit();
WARN_ON(!hlist_empty(rds_conn_hash)); WARN_ON(!hlist_empty(rds_conn_hash));
......
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/in.h> #include <linux/in.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include "rds_single_path.h" #include "rds_single_path.h"
#include "rds.h" #include "rds.h"
...@@ -40,6 +42,17 @@ ...@@ -40,6 +42,17 @@
static DEFINE_SPINLOCK(loop_conns_lock); static DEFINE_SPINLOCK(loop_conns_lock);
static LIST_HEAD(loop_conns); static LIST_HEAD(loop_conns);
static atomic_t rds_loop_unloading = ATOMIC_INIT(0);
static void rds_loop_set_unloading(void)
{
atomic_set(&rds_loop_unloading, 1);
}
static bool rds_loop_is_unloading(struct rds_connection *conn)
{
return atomic_read(&rds_loop_unloading) != 0;
}
/* /*
* This 'loopback' transport is a special case for flows that originate * This 'loopback' transport is a special case for flows that originate
...@@ -165,6 +178,8 @@ void rds_loop_exit(void) ...@@ -165,6 +178,8 @@ void rds_loop_exit(void)
struct rds_loop_connection *lc, *_lc; struct rds_loop_connection *lc, *_lc;
LIST_HEAD(tmp_list); LIST_HEAD(tmp_list);
rds_loop_set_unloading();
synchronize_rcu();
/* avoid calling conn_destroy with irqs off */ /* avoid calling conn_destroy with irqs off */
spin_lock_irq(&loop_conns_lock); spin_lock_irq(&loop_conns_lock);
list_splice(&loop_conns, &tmp_list); list_splice(&loop_conns, &tmp_list);
...@@ -177,6 +192,46 @@ void rds_loop_exit(void) ...@@ -177,6 +192,46 @@ void rds_loop_exit(void)
} }
} }
static void rds_loop_kill_conns(struct net *net)
{
struct rds_loop_connection *lc, *_lc;
LIST_HEAD(tmp_list);
spin_lock_irq(&loop_conns_lock);
list_for_each_entry_safe(lc, _lc, &loop_conns, loop_node) {
struct net *c_net = read_pnet(&lc->conn->c_net);
if (net != c_net)
continue;
list_move_tail(&lc->loop_node, &tmp_list);
}
spin_unlock_irq(&loop_conns_lock);
list_for_each_entry_safe(lc, _lc, &tmp_list, loop_node) {
WARN_ON(lc->conn->c_passive);
rds_conn_destroy(lc->conn);
}
}
static void __net_exit rds_loop_exit_net(struct net *net)
{
rds_loop_kill_conns(net);
}
static struct pernet_operations rds_loop_net_ops = {
.exit = rds_loop_exit_net,
};
int rds_loop_net_init(void)
{
return register_pernet_device(&rds_loop_net_ops);
}
void rds_loop_net_exit(void)
{
unregister_pernet_device(&rds_loop_net_ops);
}
/* /*
* This is missing .xmit_* because loop doesn't go through generic * This is missing .xmit_* because loop doesn't go through generic
* rds_send_xmit() and doesn't call rds_recv_incoming(). .listen_stop and * rds_send_xmit() and doesn't call rds_recv_incoming(). .listen_stop and
...@@ -194,4 +249,5 @@ struct rds_transport rds_loop_transport = { ...@@ -194,4 +249,5 @@ struct rds_transport rds_loop_transport = {
.inc_free = rds_loop_inc_free, .inc_free = rds_loop_inc_free,
.t_name = "loopback", .t_name = "loopback",
.t_type = RDS_TRANS_LOOP, .t_type = RDS_TRANS_LOOP,
.t_unloading = rds_loop_is_unloading,
}; };
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
/* loop.c */ /* loop.c */
extern struct rds_transport rds_loop_transport; extern struct rds_transport rds_loop_transport;
int rds_loop_net_init(void);
void rds_loop_net_exit(void);
void rds_loop_exit(void); void rds_loop_exit(void);
#endif #endif
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