|
| 1 | +cifs: handle cases where a channel is closed |
| 2 | + |
| 3 | +jira LE-4669 |
| 4 | +Rebuild_History Non-Buildable kernel-4.18.0-553.82.1.el8_10 |
| 5 | +commit-author Shyam Prasad N <sprasad@microsoft.com> |
| 6 | +commit 0c51cc6f2cb0108e7d49805f6e089cd85caab279 |
| 7 | +Empty-Commit: Cherry-Pick Conflicts during history rebuild. |
| 8 | +Will be included in final tarball splat. Ref for failed cherry-pick at: |
| 9 | +ciq/ciq_backports/kernel-4.18.0-553.82.1.el8_10/0c51cc6f.failed |
| 10 | + |
| 11 | +So far, SMB multichannel could only scale up, but not |
| 12 | +scale down the number of channels. In this series of |
| 13 | +patch, we now allow the client to deal with the case |
| 14 | +of multichannel disabled on the server when the share |
| 15 | +is mounted. With that change, we now need the ability |
| 16 | +to scale down the channels. |
| 17 | + |
| 18 | +This change allows the client to deal with cases of |
| 19 | +missing channels more gracefully. |
| 20 | + |
| 21 | + Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> |
| 22 | + Signed-off-by: Steve French <stfrench@microsoft.com> |
| 23 | +(cherry picked from commit 0c51cc6f2cb0108e7d49805f6e089cd85caab279) |
| 24 | + Signed-off-by: Jonathan Maple <jmaple@ciq.com> |
| 25 | + |
| 26 | +# Conflicts: |
| 27 | +# fs/cifs/connect.c |
| 28 | +# fs/cifs/sess.c |
| 29 | +diff --cc fs/cifs/connect.c |
| 30 | +index ca2926c7b59e,3ff82f0aa00e..000000000000 |
| 31 | +--- a/fs/cifs/connect.c |
| 32 | ++++ b/fs/cifs/connect.c |
| 33 | +@@@ -164,45 -140,70 +164,63 @@@ static void smb2_query_server_interface |
| 34 | + (SMB_INTERFACE_POLL_INTERVAL * HZ)); |
| 35 | + } |
| 36 | + |
| 37 | + -/* |
| 38 | + - * Update the tcpStatus for the server. |
| 39 | + - * This is used to signal the cifsd thread to call cifs_reconnect |
| 40 | + - * ONLY cifsd thread should call cifs_reconnect. For any other |
| 41 | + - * thread, use this function |
| 42 | + - * |
| 43 | + - * @server: the tcp ses for which reconnect is needed |
| 44 | + - * @all_channels: if this needs to be done for all channels |
| 45 | + - */ |
| 46 | + -void |
| 47 | + -cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, |
| 48 | + - bool all_channels) |
| 49 | + +static void cifs_resolve_server(struct work_struct *work) |
| 50 | + { |
| 51 | + - struct TCP_Server_Info *pserver; |
| 52 | + - struct cifs_ses *ses; |
| 53 | + - int i; |
| 54 | + + int rc; |
| 55 | + + struct TCP_Server_Info *server = container_of(work, |
| 56 | + + struct TCP_Server_Info, resolve.work); |
| 57 | + |
| 58 | + - /* If server is a channel, select the primary channel */ |
| 59 | + - pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; |
| 60 | + + mutex_lock(&server->srv_mutex); |
| 61 | + |
| 62 | + - /* if we need to signal just this channel */ |
| 63 | + - if (!all_channels) { |
| 64 | + - spin_lock(&server->srv_lock); |
| 65 | + - if (server->tcpStatus != CifsExiting) |
| 66 | + - server->tcpStatus = CifsNeedReconnect; |
| 67 | + - spin_unlock(&server->srv_lock); |
| 68 | + - return; |
| 69 | + + /* |
| 70 | + + * Resolve the hostname again to make sure that IP address is up-to-date. |
| 71 | + + */ |
| 72 | + + rc = reconn_set_ipaddr_from_hostname(server); |
| 73 | + + if (rc) { |
| 74 | + + cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", |
| 75 | + + __func__, rc); |
| 76 | + } |
| 77 | + |
| 78 | +++<<<<<<< HEAD:fs/cifs/connect.c |
| 79 | + + mutex_unlock(&server->srv_mutex); |
| 80 | +++======= |
| 81 | ++ spin_lock(&cifs_tcp_ses_lock); |
| 82 | ++ list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { |
| 83 | ++ spin_lock(&ses->chan_lock); |
| 84 | ++ for (i = 0; i < ses->chan_count; i++) { |
| 85 | ++ if (!ses->chans[i].server) |
| 86 | ++ continue; |
| 87 | ++ |
| 88 | ++ spin_lock(&ses->chans[i].server->srv_lock); |
| 89 | ++ if (ses->chans[i].server->tcpStatus != CifsExiting) |
| 90 | ++ ses->chans[i].server->tcpStatus = CifsNeedReconnect; |
| 91 | ++ spin_unlock(&ses->chans[i].server->srv_lock); |
| 92 | ++ } |
| 93 | ++ spin_unlock(&ses->chan_lock); |
| 94 | ++ } |
| 95 | ++ spin_unlock(&cifs_tcp_ses_lock); |
| 96 | +++>>>>>>> 0c51cc6f2cb0 (cifs: handle cases where a channel is closed):fs/smb/client/connect.c |
| 97 | + } |
| 98 | + |
| 99 | + -/* |
| 100 | + +/** |
| 101 | + * Mark all sessions and tcons for reconnect. |
| 102 | + - * IMPORTANT: make sure that this gets called only from |
| 103 | + - * cifsd thread. For any other thread, use |
| 104 | + - * cifs_signal_cifsd_for_reconnect |
| 105 | + * |
| 106 | + - * @server: the tcp ses for which reconnect is needed |
| 107 | + * @server needs to be previously set to CifsNeedReconnect. |
| 108 | + - * @mark_smb_session: whether even sessions need to be marked |
| 109 | + */ |
| 110 | + -void |
| 111 | + -cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, |
| 112 | + - bool mark_smb_session) |
| 113 | + +static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server) |
| 114 | + { |
| 115 | + - struct TCP_Server_Info *pserver; |
| 116 | + - struct cifs_ses *ses, *nses; |
| 117 | + + unsigned int num_sessions = 0; |
| 118 | + + struct cifs_ses *ses; |
| 119 | + struct cifs_tcon *tcon; |
| 120 | + + struct mid_q_entry *mid, *nmid; |
| 121 | + + struct list_head retry_list; |
| 122 | + + struct TCP_Server_Info *pserver; |
| 123 | + |
| 124 | + + server->maxBuf = 0; |
| 125 | + + server->max_read = 0; |
| 126 | + + |
| 127 | + + cifs_dbg(FYI, "Mark tcp session as need reconnect\n"); |
| 128 | + + trace_smb3_reconnect(server->CurrentMid, server->conn_id, server->hostname); |
| 129 | + /* |
| 130 | + * before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they |
| 131 | + * are not used until reconnected. |
| 132 | +diff --cc fs/cifs/sess.c |
| 133 | +index b9c9236e1312,d13a24613710..000000000000 |
| 134 | +--- a/fs/cifs/sess.c |
| 135 | ++++ b/fs/cifs/sess.c |
| 136 | +@@@ -65,7 -67,9 +65,13 @@@ bool is_ses_using_iface(struct cifs_se |
| 137 | + return false; |
| 138 | + } |
| 139 | + |
| 140 | +++<<<<<<< HEAD:fs/cifs/sess.c |
| 141 | + +unsigned int |
| 142 | +++======= |
| 143 | ++ /* channel helper functions. assumed that chan_lock is held by caller. */ |
| 144 | ++ |
| 145 | ++ int |
| 146 | +++>>>>>>> 0c51cc6f2cb0 (cifs: handle cases where a channel is closed):fs/smb/client/sess.c |
| 147 | + cifs_ses_get_chan_index(struct cifs_ses *ses, |
| 148 | + struct TCP_Server_Info *server) |
| 149 | + { |
| 150 | +@@@ -81,10 -85,44 +87,47 @@@ |
| 151 | + cifs_dbg(VFS, "unable to get chan index for server: 0x%llx", |
| 152 | + server->conn_id); |
| 153 | + WARN_ON(1); |
| 154 | +- return 0; |
| 155 | ++ return CIFS_INVAL_CHAN_INDEX; |
| 156 | ++ } |
| 157 | ++ |
| 158 | ++ void |
| 159 | +++<<<<<<< HEAD:fs/cifs/sess.c |
| 160 | +++======= |
| 161 | ++ cifs_chan_set_in_reconnect(struct cifs_ses *ses, |
| 162 | ++ struct TCP_Server_Info *server) |
| 163 | ++ { |
| 164 | ++ int chan_index = cifs_ses_get_chan_index(ses, server); |
| 165 | ++ |
| 166 | ++ if (chan_index == CIFS_INVAL_CHAN_INDEX) |
| 167 | ++ return; |
| 168 | ++ |
| 169 | ++ ses->chans[chan_index].in_reconnect = true; |
| 170 | ++ } |
| 171 | ++ |
| 172 | ++ void |
| 173 | ++ cifs_chan_clear_in_reconnect(struct cifs_ses *ses, |
| 174 | ++ struct TCP_Server_Info *server) |
| 175 | ++ { |
| 176 | ++ unsigned int chan_index = cifs_ses_get_chan_index(ses, server); |
| 177 | ++ if (chan_index == CIFS_INVAL_CHAN_INDEX) |
| 178 | ++ return; |
| 179 | ++ |
| 180 | ++ ses->chans[chan_index].in_reconnect = false; |
| 181 | ++ } |
| 182 | ++ |
| 183 | ++ bool |
| 184 | ++ cifs_chan_in_reconnect(struct cifs_ses *ses, |
| 185 | ++ struct TCP_Server_Info *server) |
| 186 | ++ { |
| 187 | ++ unsigned int chan_index = cifs_ses_get_chan_index(ses, server); |
| 188 | ++ if (chan_index == CIFS_INVAL_CHAN_INDEX) |
| 189 | ++ return true; /* err on the safer side */ |
| 190 | ++ |
| 191 | ++ return CIFS_CHAN_IN_RECONNECT(ses, chan_index); |
| 192 | + } |
| 193 | + |
| 194 | + void |
| 195 | +++>>>>>>> 0c51cc6f2cb0 (cifs: handle cases where a channel is closed):fs/smb/client/sess.c |
| 196 | + cifs_chan_set_need_reconnect(struct cifs_ses *ses, |
| 197 | + struct TCP_Server_Info *server) |
| 198 | + { |
| 199 | +@@@ -236,9 -282,12 +287,16 @@@ cifs_chan_update_iface(struct cifs_ses |
| 200 | + struct cifs_server_iface *old_iface = NULL; |
| 201 | + int rc = 0; |
| 202 | + |
| 203 | +++<<<<<<< HEAD:fs/cifs/sess.c |
| 204 | + + /* primary channel. This can never go away */ |
| 205 | + + if (!chan_index) |
| 206 | +++======= |
| 207 | ++ spin_lock(&ses->chan_lock); |
| 208 | ++ chan_index = cifs_ses_get_chan_index(ses, server); |
| 209 | ++ if (chan_index == CIFS_INVAL_CHAN_INDEX) { |
| 210 | ++ spin_unlock(&ses->chan_lock); |
| 211 | +++>>>>>>> 0c51cc6f2cb0 (cifs: handle cases where a channel is closed):fs/smb/client/sess.c |
| 212 | + return 0; |
| 213 | + - } |
| 214 | + |
| 215 | + if (ses->chans[chan_index].iface) { |
| 216 | + old_iface = ses->chans[chan_index].iface; |
| 217 | +@@@ -281,14 -330,25 +339,26 @@@ |
| 218 | + WARN_ON(!iface); |
| 219 | + cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr); |
| 220 | + } |
| 221 | + + |
| 222 | + spin_unlock(&ses->iface_lock); |
| 223 | + |
| 224 | +++<<<<<<< HEAD:fs/cifs/sess.c |
| 225 | +++======= |
| 226 | ++ spin_lock(&ses->chan_lock); |
| 227 | ++ chan_index = cifs_ses_get_chan_index(ses, server); |
| 228 | ++ if (chan_index == CIFS_INVAL_CHAN_INDEX) { |
| 229 | ++ spin_unlock(&ses->chan_lock); |
| 230 | ++ return 0; |
| 231 | ++ } |
| 232 | ++ |
| 233 | ++ ses->chans[chan_index].iface = iface; |
| 234 | ++ |
| 235 | +++>>>>>>> 0c51cc6f2cb0 (cifs: handle cases where a channel is closed):fs/smb/client/sess.c |
| 236 | + /* No iface is found. if secondary chan, drop connection */ |
| 237 | + - if (!iface && SERVER_IS_CHAN(server)) |
| 238 | + - ses->chans[chan_index].server = NULL; |
| 239 | + - |
| 240 | + - spin_unlock(&ses->chan_lock); |
| 241 | + - |
| 242 | + - if (!iface && SERVER_IS_CHAN(server)) |
| 243 | + + if (!iface && CIFS_SERVER_IS_CHAN(server)) { |
| 244 | + cifs_put_tcp_session(server, false); |
| 245 | + + ses->chans[chan_index].server = NULL; |
| 246 | + + } |
| 247 | + |
| 248 | + return rc; |
| 249 | + } |
| 250 | +diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c |
| 251 | +index eb8541c3298e..02c728536d29 100644 |
| 252 | +--- a/fs/cifs/cifs_debug.c |
| 253 | ++++ b/fs/cifs/cifs_debug.c |
| 254 | +@@ -142,6 +142,11 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan) |
| 255 | + { |
| 256 | + struct TCP_Server_Info *server = chan->server; |
| 257 | + |
| 258 | ++ if (!server) { |
| 259 | ++ seq_printf(m, "\n\n\t\tChannel: %d DISABLED", i+1); |
| 260 | ++ return; |
| 261 | ++ } |
| 262 | ++ |
| 263 | + seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx" |
| 264 | + "\n\t\tNumber of credits: %d Dialect 0x%x" |
| 265 | + "\n\t\tTCP status: %d Instance: %d" |
| 266 | +diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h |
| 267 | +index 9e7f8c48e52c..dd0fa3e31591 100644 |
| 268 | +--- a/fs/cifs/cifsglob.h |
| 269 | ++++ b/fs/cifs/cifsglob.h |
| 270 | +@@ -1029,6 +1029,7 @@ struct cifs_ses { |
| 271 | + spinlock_t chan_lock; |
| 272 | + /* ========= begin: protected by chan_lock ======== */ |
| 273 | + #define CIFS_MAX_CHANNELS 16 |
| 274 | ++#define CIFS_INVAL_CHAN_INDEX (-1) |
| 275 | + #define CIFS_ALL_CHANNELS_SET(ses) \ |
| 276 | + ((1UL << (ses)->chan_count) - 1) |
| 277 | + #define CIFS_ALL_CHANS_NEED_RECONNECT(ses) \ |
| 278 | +diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h |
| 279 | +index 76582fa73223..379a00836656 100644 |
| 280 | +--- a/fs/cifs/cifsproto.h |
| 281 | ++++ b/fs/cifs/cifsproto.h |
| 282 | +@@ -599,7 +599,7 @@ bool is_server_using_iface(struct TCP_Server_Info *server, |
| 283 | + bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface); |
| 284 | + void cifs_ses_mark_for_reconnect(struct cifs_ses *ses); |
| 285 | + |
| 286 | +-unsigned int |
| 287 | ++int |
| 288 | + cifs_ses_get_chan_index(struct cifs_ses *ses, |
| 289 | + struct TCP_Server_Info *server); |
| 290 | + void |
| 291 | +* Unmerged path fs/cifs/connect.c |
| 292 | +* Unmerged path fs/cifs/sess.c |
| 293 | +diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c |
| 294 | +index c6bf79f874d4..7e9a760af8b1 100644 |
| 295 | +--- a/fs/cifs/smb2transport.c |
| 296 | ++++ b/fs/cifs/smb2transport.c |
| 297 | +@@ -412,7 +412,13 @@ generate_smb3signingkey(struct cifs_ses *ses, |
| 298 | + spin_lock(&ses->chan_lock); |
| 299 | + is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); |
| 300 | + chan_index = cifs_ses_get_chan_index(ses, server); |
| 301 | +- /* TODO: introduce ref counting for channels when the can be freed */ |
| 302 | ++ if (chan_index == CIFS_INVAL_CHAN_INDEX) { |
| 303 | ++ spin_unlock(&ses->chan_lock); |
| 304 | ++ spin_unlock(&ses->ses_lock); |
| 305 | ++ |
| 306 | ++ return -EINVAL; |
| 307 | ++ } |
| 308 | ++ |
| 309 | + spin_unlock(&ses->chan_lock); |
| 310 | + |
| 311 | + /* |
0 commit comments