From b0de7f2c41386679896ecd411c4adf82baecbc8d Mon Sep 17 00:00:00 2001 From: Bang Nguyen Date: Sat, 16 Apr 2016 11:47:05 -0700 Subject: [PATCH] RDS: Fix the rds_conn_destroy panic due to pending messages In corner cases, there could be pending messages on connection which needs to be detsroyed. Make sure those messages are purged before the connection is torned down. Orabug: 23222944 Signed-off-by: Bang Nguyen Signed-off-by: Santosh Shilimkar --- net/rds/connection.c | 19 +++++++++++++++---- net/rds/ib_cm.c | 2 +- net/rds/ib_rdma.c | 2 +- net/rds/loop.c | 2 +- net/rds/rds.h | 3 ++- net/rds/send.c | 5 +++++ net/rds/tcp.c | 8 ++++---- 7 files changed, 29 insertions(+), 12 deletions(-) diff --git a/net/rds/connection.c b/net/rds/connection.c index c4b885c87b63a..f2b02bacba0af 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -402,15 +402,18 @@ void rds_conn_shutdown(struct rds_connection *conn, int restart) * the conn has been shutdown that no one else is referencing the connection. * We can only ensure this in the rmmod path in the current code. */ -void rds_conn_destroy(struct rds_connection *conn) +void rds_conn_destroy(struct rds_connection *conn, int shutdown) { struct rds_message *rm, *rtmp; unsigned long flags; + LIST_HEAD(to_be_dropped); rds_rtd(RDS_RTD_CM, "freeing conn %p <%u.%u.%u.%u,%u.%u.%u.%u,%d>\n", conn, NIPQUAD(conn->c_laddr), NIPQUAD(conn->c_faddr), conn->c_tos); + set_bit(RDS_DESTROY_PENDING, &conn->c_flags); + /* Ensure conn will not be scheduled for reconnect */ spin_lock_irq(&rds_conn_lock); hlist_del_init_rcu(&conn->c_hash_node); @@ -437,10 +440,18 @@ void rds_conn_destroy(struct rds_connection *conn) list_for_each_entry_safe(rm, rtmp, &conn->c_send_queue, m_conn_item) { - list_del_init(&rm->m_conn_item); - BUG_ON(!list_empty(&rm->m_sock_item)); - rds_message_put(rm); + if (shutdown) { + list_del_init(&rm->m_conn_item); + BUG_ON(!list_empty(&rm->m_sock_item)); + rds_message_put(rm); + } else { + list_move_tail(&rm->m_conn_item, &to_be_dropped); + } } + + if (!list_empty(&to_be_dropped)) + rds_send_remove_from_sock(&to_be_dropped, RDS_RDMA_SEND_DROPPED); + if (conn->c_xmit_rm) rds_message_put(conn->c_xmit_rm); diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 4b3c9deb72661..a1e25d70825cd 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -1054,7 +1054,7 @@ void rds_ib_conn_destroy_worker(struct work_struct *_work) container_of(_work, struct rds_ib_conn_destroy_work, work.work); struct rds_connection *conn = work->conn; - rds_conn_destroy(conn); + rds_conn_destroy(conn, 0); kfree(work); } diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c index 090877d97208c..dac3a35620c02 100644 --- a/net/rds/ib_rdma.c +++ b/net/rds/ib_rdma.c @@ -225,7 +225,7 @@ void rds_ib_destroy_nodev_conns(void) spin_unlock_irq(&ib_nodev_conns_lock); list_for_each_entry_safe(ic, _ic, &tmp_list, ib_node) - rds_conn_destroy(ic->conn); + rds_conn_destroy(ic->conn, 1); } static unsigned int get_unmap_fmr_cpu(struct rds_ib_device *rds_ibdev, diff --git a/net/rds/loop.c b/net/rds/loop.c index e0eac736d8b36..be0ff94071f91 100644 --- a/net/rds/loop.c +++ b/net/rds/loop.c @@ -185,7 +185,7 @@ void rds_loop_exit(void) list_for_each_entry_safe(lc, _lc, &tmp_list, loop_node) { WARN_ON(lc->conn->c_passive); - rds_conn_destroy(lc->conn); + rds_conn_destroy(lc->conn, 1); } } diff --git a/net/rds/rds.h b/net/rds/rds.h index 6737208fddf56..9d13cf9f89b9b 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -134,6 +134,7 @@ enum { #define RDS_RECONNECT_PENDING 1 #define RDS_IN_XMIT 2 #define RDS_RECV_REFILL 3 +#define RDS_DESTROY_PENDING 4 #define RDS_RDMA_RESOLVE_TO_MAX_INDEX 5 #define RDS_ADDR_RES_TM_INDEX_MAX 5 @@ -848,7 +849,7 @@ struct rds_connection *rds_conn_find(struct net *net, __be32 laddr, __be32 faddr, struct rds_transport *trans, u8 tos); void rds_conn_shutdown(struct rds_connection *conn, int restart); -void rds_conn_destroy(struct rds_connection *conn); +void rds_conn_destroy(struct rds_connection *conn, int shutdown); void rds_conn_reset(struct rds_connection *conn); void rds_conn_drop(struct rds_connection *conn); void rds_conn_laddr_list(__be32 laddr, struct list_head *laddr_conns); diff --git a/net/rds/send.c b/net/rds/send.c index edc3083e7a0ee..245a3d7ea17e3 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -1320,6 +1320,11 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) goto out; } + if (test_bit(RDS_DESTROY_PENDING, &conn->c_flags)) { + ret = -EAGAIN; + goto out; + } + /* Not accepting new sends until all the failed ops have been reaped */ if (rds_async_send_enabled && conn->c_pending_flush) { ret = -EAGAIN; diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 65eb17fcba69a..fe0aaa71a4823 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -251,8 +251,8 @@ static void rds_tcp_destroy_conns(void) list_for_each_entry_safe(tc, _tc, &tmp_list, t_tcp_node) { if (tc->conn->c_passive) - rds_conn_destroy(tc->conn->c_passive); - rds_conn_destroy(tc->conn); + rds_conn_destroy(tc->conn->c_passive, 1); + rds_conn_destroy(tc->conn, 1); } } @@ -434,8 +434,8 @@ static void rds_tcp_kill_sock(struct net *net) sk->sk_prot->disconnect(sk, 0); tcp_done(sk); if (tc->conn->c_passive) - rds_conn_destroy(tc->conn->c_passive); - rds_conn_destroy(tc->conn); + rds_conn_destroy(tc->conn->c_passive, 1); + rds_conn_destroy(tc->conn, 1); } } -- 2.51.0