diff options
author | Olof Johansson <olof@lixom.net> | 2016-06-19 01:59:07 -0400 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2016-06-19 01:59:07 -0400 |
commit | 8fd0976702f05042c776848819e5fd2a835f23c9 (patch) | |
tree | 8a87b0e33bf39adcc53b3ee4be61155dab86417b /net/rds/tcp.c | |
parent | 58935f24a996cb55595c29dd5303bd9b778c8b00 (diff) | |
parent | 8f50b8e57442d28e41bb736c173d8a2490549a82 (diff) |
Merge tag 'gpmc-omap-fixes-for-v4.7' of https://github.com/rogerq/linux into fixes
OMAP-GPMC: Fixes for for v4.7-rc cycle:
- Fix omap gpmc EXTRADELAY timing. The DT provided timings
were wrongly used causing devices requiring extra delay timing
to fail.
* tag 'gpmc-omap-fixes-for-v4.7' of https://github.com/rogerq/linux:
memory: omap-gpmc: Fix omap gpmc EXTRADELAY timing
+ Linux 4.7-rc3
Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'net/rds/tcp.c')
-rw-r--r-- | net/rds/tcp.c | 78 |
1 files changed, 75 insertions, 3 deletions
diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 86187dad1440..74ee126a6fe6 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c | |||
@@ -126,9 +126,81 @@ void rds_tcp_restore_callbacks(struct socket *sock, | |||
126 | } | 126 | } |
127 | 127 | ||
128 | /* | 128 | /* |
129 | * This is the only path that sets tc->t_sock. Send and receive trust that | 129 | * rds_tcp_reset_callbacks() switches the to the new sock and |
130 | * it is set. The RDS_CONN_UP bit protects those paths from being | 130 | * returns the existing tc->t_sock. |
131 | * called while it isn't set. | 131 | * |
132 | * The only functions that set tc->t_sock are rds_tcp_set_callbacks | ||
133 | * and rds_tcp_reset_callbacks. Send and receive trust that | ||
134 | * it is set. The absence of RDS_CONN_UP bit protects those paths | ||
135 | * from being called while it isn't set. | ||
136 | */ | ||
137 | void rds_tcp_reset_callbacks(struct socket *sock, | ||
138 | struct rds_connection *conn) | ||
139 | { | ||
140 | struct rds_tcp_connection *tc = conn->c_transport_data; | ||
141 | struct socket *osock = tc->t_sock; | ||
142 | |||
143 | if (!osock) | ||
144 | goto newsock; | ||
145 | |||
146 | /* Need to resolve a duelling SYN between peers. | ||
147 | * We have an outstanding SYN to this peer, which may | ||
148 | * potentially have transitioned to the RDS_CONN_UP state, | ||
149 | * so we must quiesce any send threads before resetting | ||
150 | * c_transport_data. We quiesce these threads by setting | ||
151 | * c_state to something other than RDS_CONN_UP, and then | ||
152 | * waiting for any existing threads in rds_send_xmit to | ||
153 | * complete release_in_xmit(). (Subsequent threads entering | ||
154 | * rds_send_xmit() will bail on !rds_conn_up(). | ||
155 | * | ||
156 | * However an incoming syn-ack at this point would end up | ||
157 | * marking the conn as RDS_CONN_UP, and would again permit | ||
158 | * rds_send_xmi() threads through, so ideally we would | ||
159 | * synchronize on RDS_CONN_UP after lock_sock(), but cannot | ||
160 | * do that: waiting on !RDS_IN_XMIT after lock_sock() may | ||
161 | * end up deadlocking with tcp_sendmsg(), and the RDS_IN_XMIT | ||
162 | * would not get set. As a result, we set c_state to | ||
163 | * RDS_CONN_RESETTTING, to ensure that rds_tcp_state_change | ||
164 | * cannot mark rds_conn_path_up() in the window before lock_sock() | ||
165 | */ | ||
166 | atomic_set(&conn->c_state, RDS_CONN_RESETTING); | ||
167 | wait_event(conn->c_waitq, !test_bit(RDS_IN_XMIT, &conn->c_flags)); | ||
168 | lock_sock(osock->sk); | ||
169 | /* reset receive side state for rds_tcp_data_recv() for osock */ | ||
170 | if (tc->t_tinc) { | ||
171 | rds_inc_put(&tc->t_tinc->ti_inc); | ||
172 | tc->t_tinc = NULL; | ||
173 | } | ||
174 | tc->t_tinc_hdr_rem = sizeof(struct rds_header); | ||
175 | tc->t_tinc_data_rem = 0; | ||
176 | tc->t_sock = NULL; | ||
177 | |||
178 | write_lock_bh(&osock->sk->sk_callback_lock); | ||
179 | |||
180 | osock->sk->sk_user_data = NULL; | ||
181 | osock->sk->sk_data_ready = tc->t_orig_data_ready; | ||
182 | osock->sk->sk_write_space = tc->t_orig_write_space; | ||
183 | osock->sk->sk_state_change = tc->t_orig_state_change; | ||
184 | write_unlock_bh(&osock->sk->sk_callback_lock); | ||
185 | release_sock(osock->sk); | ||
186 | sock_release(osock); | ||
187 | newsock: | ||
188 | rds_send_reset(conn); | ||
189 | lock_sock(sock->sk); | ||
190 | write_lock_bh(&sock->sk->sk_callback_lock); | ||
191 | tc->t_sock = sock; | ||
192 | sock->sk->sk_user_data = conn; | ||
193 | sock->sk->sk_data_ready = rds_tcp_data_ready; | ||
194 | sock->sk->sk_write_space = rds_tcp_write_space; | ||
195 | sock->sk->sk_state_change = rds_tcp_state_change; | ||
196 | |||
197 | write_unlock_bh(&sock->sk->sk_callback_lock); | ||
198 | release_sock(sock->sk); | ||
199 | } | ||
200 | |||
201 | /* Add tc to rds_tcp_tc_list and set tc->t_sock. See comments | ||
202 | * above rds_tcp_reset_callbacks for notes about synchronization | ||
203 | * with data path | ||
132 | */ | 204 | */ |
133 | void rds_tcp_set_callbacks(struct socket *sock, struct rds_connection *conn) | 205 | void rds_tcp_set_callbacks(struct socket *sock, struct rds_connection *conn) |
134 | { | 206 | { |