diff options
author | Zach Brown <zach.brown@oracle.com> | 2010-07-23 13:32:31 -0400 |
---|---|---|
committer | Andy Grover <andy.grover@oracle.com> | 2010-09-08 21:16:47 -0400 |
commit | 5adb5bc65f93e52341c3fc9d03d4030dd375e256 (patch) | |
tree | 55b19c7757ccd64f58169a05cd63e91bee409bad /net/rds | |
parent | 77510481c0c3980c8979ed236d63e59221fb8ce5 (diff) |
RDS: have sockets get transport module references
Right now there's nothing to stop the various paths that use
rs->rs_transport from racing with rmmod and executing freed transport
code. The simple fix is to have binding to a transport also hold a
reference to the transport's module, removing this class of races.
We already had an unused t_owner field which was set for the modular
transports and which wasn't set for the built-in loop transport.
Signed-off-by: Zach Brown <zach.brown@oracle.com>
Diffstat (limited to 'net/rds')
-rw-r--r-- | net/rds/af_rds.c | 2 | ||||
-rw-r--r-- | net/rds/connection.c | 5 | ||||
-rw-r--r-- | net/rds/rds.h | 1 | ||||
-rw-r--r-- | net/rds/transport.c | 19 |
4 files changed, 21 insertions, 6 deletions
diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index 57ef0ec4f03d..8e3886d353b5 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c | |||
@@ -90,6 +90,8 @@ static int rds_release(struct socket *sock) | |||
90 | rds_sock_count--; | 90 | rds_sock_count--; |
91 | spin_unlock_irqrestore(&rds_sock_lock, flags); | 91 | spin_unlock_irqrestore(&rds_sock_lock, flags); |
92 | 92 | ||
93 | rds_trans_put(rs->rs_transport); | ||
94 | |||
93 | sock->sk = NULL; | 95 | sock->sk = NULL; |
94 | sock_put(sk); | 96 | sock_put(sk); |
95 | out: | 97 | out: |
diff --git a/net/rds/connection.c b/net/rds/connection.c index 75a1a37d64d3..968b7a798398 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c | |||
@@ -117,6 +117,7 @@ static struct rds_connection *__rds_conn_create(__be32 laddr, __be32 faddr, | |||
117 | { | 117 | { |
118 | struct rds_connection *conn, *parent = NULL; | 118 | struct rds_connection *conn, *parent = NULL; |
119 | struct hlist_head *head = rds_conn_bucket(laddr, faddr); | 119 | struct hlist_head *head = rds_conn_bucket(laddr, faddr); |
120 | struct rds_transport *loop_trans; | ||
120 | unsigned long flags; | 121 | unsigned long flags; |
121 | int ret; | 122 | int ret; |
122 | 123 | ||
@@ -163,7 +164,9 @@ static struct rds_connection *__rds_conn_create(__be32 laddr, __be32 faddr, | |||
163 | * can bind to the destination address then we'd rather the messages | 164 | * can bind to the destination address then we'd rather the messages |
164 | * flow through loopback rather than either transport. | 165 | * flow through loopback rather than either transport. |
165 | */ | 166 | */ |
166 | if (rds_trans_get_preferred(faddr)) { | 167 | loop_trans = rds_trans_get_preferred(faddr); |
168 | if (loop_trans) { | ||
169 | rds_trans_put(loop_trans); | ||
167 | conn->c_loopback = 1; | 170 | conn->c_loopback = 1; |
168 | if (is_outgoing && trans->t_prefer_loopback) { | 171 | if (is_outgoing && trans->t_prefer_loopback) { |
169 | /* "outgoing" connection - and the transport | 172 | /* "outgoing" connection - and the transport |
diff --git a/net/rds/rds.h b/net/rds/rds.h index 2ff7fc9f0539..aab5e949fa93 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h | |||
@@ -798,6 +798,7 @@ void rds_connect_complete(struct rds_connection *conn); | |||
798 | int rds_trans_register(struct rds_transport *trans); | 798 | int rds_trans_register(struct rds_transport *trans); |
799 | void rds_trans_unregister(struct rds_transport *trans); | 799 | void rds_trans_unregister(struct rds_transport *trans); |
800 | struct rds_transport *rds_trans_get_preferred(__be32 addr); | 800 | struct rds_transport *rds_trans_get_preferred(__be32 addr); |
801 | void rds_trans_put(struct rds_transport *trans); | ||
801 | unsigned int rds_trans_stats_info_copy(struct rds_info_iterator *iter, | 802 | unsigned int rds_trans_stats_info_copy(struct rds_info_iterator *iter, |
802 | unsigned int avail); | 803 | unsigned int avail); |
803 | int rds_trans_init(void); | 804 | int rds_trans_init(void); |
diff --git a/net/rds/transport.c b/net/rds/transport.c index 7e1067901353..7f2ac4fec367 100644 --- a/net/rds/transport.c +++ b/net/rds/transport.c | |||
@@ -71,19 +71,28 @@ void rds_trans_unregister(struct rds_transport *trans) | |||
71 | } | 71 | } |
72 | EXPORT_SYMBOL_GPL(rds_trans_unregister); | 72 | EXPORT_SYMBOL_GPL(rds_trans_unregister); |
73 | 73 | ||
74 | void rds_trans_put(struct rds_transport *trans) | ||
75 | { | ||
76 | if (trans && trans->t_owner) | ||
77 | module_put(trans->t_owner); | ||
78 | } | ||
79 | |||
74 | struct rds_transport *rds_trans_get_preferred(__be32 addr) | 80 | struct rds_transport *rds_trans_get_preferred(__be32 addr) |
75 | { | 81 | { |
76 | struct rds_transport *ret = NULL; | 82 | struct rds_transport *ret = NULL; |
77 | int i; | 83 | struct rds_transport *trans; |
84 | unsigned int i; | ||
78 | 85 | ||
79 | if (IN_LOOPBACK(ntohl(addr))) | 86 | if (IN_LOOPBACK(ntohl(addr))) |
80 | return &rds_loop_transport; | 87 | return &rds_loop_transport; |
81 | 88 | ||
82 | down_read(&rds_trans_sem); | 89 | down_read(&rds_trans_sem); |
83 | for (i = 0; i < RDS_TRANS_COUNT; i++) | 90 | for (i = 0; i < RDS_TRANS_COUNT; i++) { |
84 | { | 91 | trans = transports[i]; |
85 | if (transports[i] && (transports[i]->laddr_check(addr) == 0)) { | 92 | |
86 | ret = transports[i]; | 93 | if (trans && (trans->laddr_check(addr) == 0) && |
94 | (!trans->t_owner || try_module_get(trans->t_owner))) { | ||
95 | ret = trans; | ||
87 | break; | 96 | break; |
88 | } | 97 | } |
89 | } | 98 | } |