Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 ebeeb1ad9b8a ("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: Santosh Shilimkar <santosh.shilimkar@oracle.com>
Signed-off-by: Sowmini Varadhan <sowmini.varadhan@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Sowmini Varadhan and committed by
David S. Miller
c809195f 094bdadd

+68 -1
+10 -1
net/rds/connection.c
··· 659 659 660 660 int rds_conn_init(void) 661 661 { 662 + int ret; 663 + 664 + ret = rds_loop_net_init(); /* register pernet callback */ 665 + if (ret) 666 + return ret; 667 + 662 668 rds_conn_slab = kmem_cache_create("rds_connection", 663 669 sizeof(struct rds_connection), 664 670 0, 0, NULL); 665 - if (!rds_conn_slab) 671 + if (!rds_conn_slab) { 672 + rds_loop_net_exit(); 666 673 return -ENOMEM; 674 + } 667 675 668 676 rds_info_register_func(RDS_INFO_CONNECTIONS, rds_conn_info); 669 677 rds_info_register_func(RDS_INFO_SEND_MESSAGES, ··· 684 676 685 677 void rds_conn_exit(void) 686 678 { 679 + rds_loop_net_exit(); /* unregister pernet callback */ 687 680 rds_loop_exit(); 688 681 689 682 WARN_ON(!hlist_empty(rds_conn_hash));
+56
net/rds/loop.c
··· 33 33 #include <linux/kernel.h> 34 34 #include <linux/slab.h> 35 35 #include <linux/in.h> 36 + #include <net/net_namespace.h> 37 + #include <net/netns/generic.h> 36 38 37 39 #include "rds_single_path.h" 38 40 #include "rds.h" ··· 42 40 43 41 static DEFINE_SPINLOCK(loop_conns_lock); 44 42 static LIST_HEAD(loop_conns); 43 + static atomic_t rds_loop_unloading = ATOMIC_INIT(0); 44 + 45 + static void rds_loop_set_unloading(void) 46 + { 47 + atomic_set(&rds_loop_unloading, 1); 48 + } 49 + 50 + static bool rds_loop_is_unloading(struct rds_connection *conn) 51 + { 52 + return atomic_read(&rds_loop_unloading) != 0; 53 + } 45 54 46 55 /* 47 56 * This 'loopback' transport is a special case for flows that originate ··· 178 165 struct rds_loop_connection *lc, *_lc; 179 166 LIST_HEAD(tmp_list); 180 167 168 + rds_loop_set_unloading(); 169 + synchronize_rcu(); 181 170 /* avoid calling conn_destroy with irqs off */ 182 171 spin_lock_irq(&loop_conns_lock); 183 172 list_splice(&loop_conns, &tmp_list); ··· 190 175 WARN_ON(lc->conn->c_passive); 191 176 rds_conn_destroy(lc->conn); 192 177 } 178 + } 179 + 180 + static void rds_loop_kill_conns(struct net *net) 181 + { 182 + struct rds_loop_connection *lc, *_lc; 183 + LIST_HEAD(tmp_list); 184 + 185 + spin_lock_irq(&loop_conns_lock); 186 + list_for_each_entry_safe(lc, _lc, &loop_conns, loop_node) { 187 + struct net *c_net = read_pnet(&lc->conn->c_net); 188 + 189 + if (net != c_net) 190 + continue; 191 + list_move_tail(&lc->loop_node, &tmp_list); 192 + } 193 + spin_unlock_irq(&loop_conns_lock); 194 + 195 + list_for_each_entry_safe(lc, _lc, &tmp_list, loop_node) { 196 + WARN_ON(lc->conn->c_passive); 197 + rds_conn_destroy(lc->conn); 198 + } 199 + } 200 + 201 + static void __net_exit rds_loop_exit_net(struct net *net) 202 + { 203 + rds_loop_kill_conns(net); 204 + } 205 + 206 + static struct pernet_operations rds_loop_net_ops = { 207 + .exit = rds_loop_exit_net, 208 + }; 209 + 210 + int rds_loop_net_init(void) 211 + { 212 + return register_pernet_device(&rds_loop_net_ops); 213 + } 214 + 215 + void rds_loop_net_exit(void) 216 + { 217 + unregister_pernet_device(&rds_loop_net_ops); 193 218 } 194 219 195 220 /* ··· 249 194 .inc_free = rds_loop_inc_free, 250 195 .t_name = "loopback", 251 196 .t_type = RDS_TRANS_LOOP, 197 + .t_unloading = rds_loop_is_unloading, 252 198 };
+2
net/rds/loop.h
··· 5 5 /* loop.c */ 6 6 extern struct rds_transport rds_loop_transport; 7 7 8 + int rds_loop_net_init(void); 9 + void rds_loop_net_exit(void); 8 10 void rds_loop_exit(void); 9 11 10 12 #endif