mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	rds: tcp: Take explicit refcounts on struct net
It is incorrect for the rds_connection to piggyback on the sock_net() refcount for the netns because this gives rise to a chicken-and-egg problem during rds_conn_destroy. Instead explicitly take a ref on the net, and hold the netns down till the connection tear-down is complete. Reported-by: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Sowmini Varadhan <sowmini.varadhan@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									fa4c7fb2ad
								
							
						
					
					
						commit
						8edc3affc0
					
				
					 3 changed files with 6 additions and 5 deletions
				
			
		| 
						 | 
					@ -429,6 +429,7 @@ void rds_conn_destroy(struct rds_connection *conn)
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	rds_cong_remove_conn(conn);
 | 
						rds_cong_remove_conn(conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						put_net(conn->c_net);
 | 
				
			||||||
	kmem_cache_free(rds_conn_slab, conn);
 | 
						kmem_cache_free(rds_conn_slab, conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock_irqsave(&rds_conn_lock, flags);
 | 
						spin_lock_irqsave(&rds_conn_lock, flags);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -147,7 +147,7 @@ struct rds_connection {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Protocol version */
 | 
						/* Protocol version */
 | 
				
			||||||
	unsigned int		c_version;
 | 
						unsigned int		c_version;
 | 
				
			||||||
	possible_net_t		c_net;
 | 
						struct net		*c_net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct list_head	c_map_item;
 | 
						struct list_head	c_map_item;
 | 
				
			||||||
	unsigned long		c_map_queued;
 | 
						unsigned long		c_map_queued;
 | 
				
			||||||
| 
						 | 
					@ -162,13 +162,13 @@ struct rds_connection {
 | 
				
			||||||
static inline
 | 
					static inline
 | 
				
			||||||
struct net *rds_conn_net(struct rds_connection *conn)
 | 
					struct net *rds_conn_net(struct rds_connection *conn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return read_pnet(&conn->c_net);
 | 
						return conn->c_net;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline
 | 
					static inline
 | 
				
			||||||
void rds_conn_net_set(struct rds_connection *conn, struct net *net)
 | 
					void rds_conn_net_set(struct rds_connection *conn, struct net *net)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	write_pnet(&conn->c_net, net);
 | 
						conn->c_net = get_net(net);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define RDS_FLAG_CONG_BITMAP	0x01
 | 
					#define RDS_FLAG_CONG_BITMAP	0x01
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -529,7 +529,7 @@ static void rds_tcp_kill_sock(struct net *net)
 | 
				
			||||||
	flush_work(&rtn->rds_tcp_accept_w);
 | 
						flush_work(&rtn->rds_tcp_accept_w);
 | 
				
			||||||
	spin_lock_irq(&rds_tcp_conn_lock);
 | 
						spin_lock_irq(&rds_tcp_conn_lock);
 | 
				
			||||||
	list_for_each_entry_safe(tc, _tc, &rds_tcp_conn_list, t_tcp_node) {
 | 
						list_for_each_entry_safe(tc, _tc, &rds_tcp_conn_list, t_tcp_node) {
 | 
				
			||||||
		struct net *c_net = read_pnet(&tc->t_cpath->cp_conn->c_net);
 | 
							struct net *c_net = tc->t_cpath->cp_conn->c_net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (net != c_net || !tc->t_sock)
 | 
							if (net != c_net || !tc->t_sock)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
| 
						 | 
					@ -584,7 +584,7 @@ static void rds_tcp_sysctl_reset(struct net *net)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock_irq(&rds_tcp_conn_lock);
 | 
						spin_lock_irq(&rds_tcp_conn_lock);
 | 
				
			||||||
	list_for_each_entry_safe(tc, _tc, &rds_tcp_conn_list, t_tcp_node) {
 | 
						list_for_each_entry_safe(tc, _tc, &rds_tcp_conn_list, t_tcp_node) {
 | 
				
			||||||
		struct net *c_net = read_pnet(&tc->t_cpath->cp_conn->c_net);
 | 
							struct net *c_net = tc->t_cpath->cp_conn->c_net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (net != c_net || !tc->t_sock)
 | 
							if (net != c_net || !tc->t_sock)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue