]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - queue-6.8/smb3-retrying-on-failed-server-close.patch
6.8-stable patches
[thirdparty/kernel/stable-queue.git] / queue-6.8 / smb3-retrying-on-failed-server-close.patch
1 From 173217bd73365867378b5e75a86f0049e1069ee8 Mon Sep 17 00:00:00 2001
2 From: Ritvik Budhiraja <rbudhiraja@microsoft.com>
3 Date: Tue, 2 Apr 2024 14:01:28 -0500
4 Subject: smb3: retrying on failed server close
5
6 From: Ritvik Budhiraja <rbudhiraja@microsoft.com>
7
8 commit 173217bd73365867378b5e75a86f0049e1069ee8 upstream.
9
10 In the current implementation, CIFS close sends a close to the
11 server and does not check for the success of the server close.
12 This patch adds functionality to check for server close return
13 status and retries in case of an EBUSY or EAGAIN error.
14
15 This can help avoid handle leaks
16
17 Cc: stable@vger.kernel.org
18 Signed-off-by: Ritvik Budhiraja <rbudhiraja@microsoft.com>
19 Signed-off-by: Steve French <stfrench@microsoft.com>
20 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
21 ---
22 fs/smb/client/cached_dir.c | 6 ++--
23 fs/smb/client/cifsfs.c | 11 +++++++
24 fs/smb/client/cifsglob.h | 7 +++--
25 fs/smb/client/file.c | 63 ++++++++++++++++++++++++++++++++++++++++-----
26 fs/smb/client/smb1ops.c | 4 +-
27 fs/smb/client/smb2ops.c | 9 +++---
28 fs/smb/client/smb2pdu.c | 2 -
29 7 files changed, 85 insertions(+), 17 deletions(-)
30
31 --- a/fs/smb/client/cached_dir.c
32 +++ b/fs/smb/client/cached_dir.c
33 @@ -417,6 +417,7 @@ smb2_close_cached_fid(struct kref *ref)
34 {
35 struct cached_fid *cfid = container_of(ref, struct cached_fid,
36 refcount);
37 + int rc;
38
39 spin_lock(&cfid->cfids->cfid_list_lock);
40 if (cfid->on_list) {
41 @@ -430,9 +431,10 @@ smb2_close_cached_fid(struct kref *ref)
42 cfid->dentry = NULL;
43
44 if (cfid->is_open) {
45 - SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
46 + rc = SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
47 cfid->fid.volatile_fid);
48 - atomic_dec(&cfid->tcon->num_remote_opens);
49 + if (rc != -EBUSY && rc != -EAGAIN)
50 + atomic_dec(&cfid->tcon->num_remote_opens);
51 }
52
53 free_cached_dir(cfid);
54 --- a/fs/smb/client/cifsfs.c
55 +++ b/fs/smb/client/cifsfs.c
56 @@ -160,6 +160,7 @@ struct workqueue_struct *decrypt_wq;
57 struct workqueue_struct *fileinfo_put_wq;
58 struct workqueue_struct *cifsoplockd_wq;
59 struct workqueue_struct *deferredclose_wq;
60 +struct workqueue_struct *serverclose_wq;
61 __u32 cifs_lock_secret;
62
63 /*
64 @@ -1893,6 +1894,13 @@ init_cifs(void)
65 goto out_destroy_cifsoplockd_wq;
66 }
67
68 + serverclose_wq = alloc_workqueue("serverclose",
69 + WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
70 + if (!serverclose_wq) {
71 + rc = -ENOMEM;
72 + goto out_destroy_serverclose_wq;
73 + }
74 +
75 rc = cifs_init_inodecache();
76 if (rc)
77 goto out_destroy_deferredclose_wq;
78 @@ -1967,6 +1975,8 @@ out_destroy_decrypt_wq:
79 destroy_workqueue(decrypt_wq);
80 out_destroy_cifsiod_wq:
81 destroy_workqueue(cifsiod_wq);
82 +out_destroy_serverclose_wq:
83 + destroy_workqueue(serverclose_wq);
84 out_clean_proc:
85 cifs_proc_clean();
86 return rc;
87 @@ -1996,6 +2006,7 @@ exit_cifs(void)
88 destroy_workqueue(cifsoplockd_wq);
89 destroy_workqueue(decrypt_wq);
90 destroy_workqueue(fileinfo_put_wq);
91 + destroy_workqueue(serverclose_wq);
92 destroy_workqueue(cifsiod_wq);
93 cifs_proc_clean();
94 }
95 --- a/fs/smb/client/cifsglob.h
96 +++ b/fs/smb/client/cifsglob.h
97 @@ -432,10 +432,10 @@ struct smb_version_operations {
98 /* set fid protocol-specific info */
99 void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
100 /* close a file */
101 - void (*close)(const unsigned int, struct cifs_tcon *,
102 + int (*close)(const unsigned int, struct cifs_tcon *,
103 struct cifs_fid *);
104 /* close a file, returning file attributes and timestamps */
105 - void (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon,
106 + int (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon,
107 struct cifsFileInfo *pfile_info);
108 /* send a flush request to the server */
109 int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *);
110 @@ -1423,6 +1423,7 @@ struct cifsFileInfo {
111 bool invalidHandle:1; /* file closed via session abend */
112 bool swapfile:1;
113 bool oplock_break_cancelled:1;
114 + bool offload:1; /* offload final part of _put to a wq */
115 unsigned int oplock_epoch; /* epoch from the lease break */
116 __u32 oplock_level; /* oplock/lease level from the lease break */
117 int count;
118 @@ -1431,6 +1432,7 @@ struct cifsFileInfo {
119 struct cifs_search_info srch_inf;
120 struct work_struct oplock_break; /* work for oplock breaks */
121 struct work_struct put; /* work for the final part of _put */
122 + struct work_struct serverclose; /* work for serverclose */
123 struct delayed_work deferred;
124 bool deferred_close_scheduled; /* Flag to indicate close is scheduled */
125 char *symlink_target;
126 @@ -2087,6 +2089,7 @@ extern struct workqueue_struct *decrypt_
127 extern struct workqueue_struct *fileinfo_put_wq;
128 extern struct workqueue_struct *cifsoplockd_wq;
129 extern struct workqueue_struct *deferredclose_wq;
130 +extern struct workqueue_struct *serverclose_wq;
131 extern __u32 cifs_lock_secret;
132
133 extern mempool_t *cifs_mid_poolp;
134 --- a/fs/smb/client/file.c
135 +++ b/fs/smb/client/file.c
136 @@ -459,6 +459,7 @@ cifs_down_write(struct rw_semaphore *sem
137 }
138
139 static void cifsFileInfo_put_work(struct work_struct *work);
140 +void serverclose_work(struct work_struct *work);
141
142 struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
143 struct tcon_link *tlink, __u32 oplock,
144 @@ -505,6 +506,7 @@ struct cifsFileInfo *cifs_new_fileinfo(s
145 cfile->tlink = cifs_get_tlink(tlink);
146 INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
147 INIT_WORK(&cfile->put, cifsFileInfo_put_work);
148 + INIT_WORK(&cfile->serverclose, serverclose_work);
149 INIT_DELAYED_WORK(&cfile->deferred, smb2_deferred_work_close);
150 mutex_init(&cfile->fh_mutex);
151 spin_lock_init(&cfile->file_info_lock);
152 @@ -596,6 +598,40 @@ static void cifsFileInfo_put_work(struct
153 cifsFileInfo_put_final(cifs_file);
154 }
155
156 +void serverclose_work(struct work_struct *work)
157 +{
158 + struct cifsFileInfo *cifs_file = container_of(work,
159 + struct cifsFileInfo, serverclose);
160 +
161 + struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
162 +
163 + struct TCP_Server_Info *server = tcon->ses->server;
164 + int rc = 0;
165 + int retries = 0;
166 + int MAX_RETRIES = 4;
167 +
168 + do {
169 + if (server->ops->close_getattr)
170 + rc = server->ops->close_getattr(0, tcon, cifs_file);
171 + else if (server->ops->close)
172 + rc = server->ops->close(0, tcon, &cifs_file->fid);
173 +
174 + if (rc == -EBUSY || rc == -EAGAIN) {
175 + retries++;
176 + msleep(250);
177 + }
178 + } while ((rc == -EBUSY || rc == -EAGAIN) && (retries < MAX_RETRIES)
179 + );
180 +
181 + if (retries == MAX_RETRIES)
182 + pr_warn("Serverclose failed %d times, giving up\n", MAX_RETRIES);
183 +
184 + if (cifs_file->offload)
185 + queue_work(fileinfo_put_wq, &cifs_file->put);
186 + else
187 + cifsFileInfo_put_final(cifs_file);
188 +}
189 +
190 /**
191 * cifsFileInfo_put - release a reference of file priv data
192 *
193 @@ -636,10 +672,13 @@ void _cifsFileInfo_put(struct cifsFileIn
194 struct cifs_fid fid = {};
195 struct cifs_pending_open open;
196 bool oplock_break_cancelled;
197 + bool serverclose_offloaded = false;
198
199 spin_lock(&tcon->open_file_lock);
200 spin_lock(&cifsi->open_file_lock);
201 spin_lock(&cifs_file->file_info_lock);
202 +
203 + cifs_file->offload = offload;
204 if (--cifs_file->count > 0) {
205 spin_unlock(&cifs_file->file_info_lock);
206 spin_unlock(&cifsi->open_file_lock);
207 @@ -681,13 +720,20 @@ void _cifsFileInfo_put(struct cifsFileIn
208 if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
209 struct TCP_Server_Info *server = tcon->ses->server;
210 unsigned int xid;
211 + int rc = 0;
212
213 xid = get_xid();
214 if (server->ops->close_getattr)
215 - server->ops->close_getattr(xid, tcon, cifs_file);
216 + rc = server->ops->close_getattr(xid, tcon, cifs_file);
217 else if (server->ops->close)
218 - server->ops->close(xid, tcon, &cifs_file->fid);
219 + rc = server->ops->close(xid, tcon, &cifs_file->fid);
220 _free_xid(xid);
221 +
222 + if (rc == -EBUSY || rc == -EAGAIN) {
223 + // Server close failed, hence offloading it as an async op
224 + queue_work(serverclose_wq, &cifs_file->serverclose);
225 + serverclose_offloaded = true;
226 + }
227 }
228
229 if (oplock_break_cancelled)
230 @@ -695,10 +741,15 @@ void _cifsFileInfo_put(struct cifsFileIn
231
232 cifs_del_pending_open(&open);
233
234 - if (offload)
235 - queue_work(fileinfo_put_wq, &cifs_file->put);
236 - else
237 - cifsFileInfo_put_final(cifs_file);
238 + // if serverclose has been offloaded to wq (on failure), it will
239 + // handle offloading put as well. If serverclose not offloaded,
240 + // we need to handle offloading put here.
241 + if (!serverclose_offloaded) {
242 + if (offload)
243 + queue_work(fileinfo_put_wq, &cifs_file->put);
244 + else
245 + cifsFileInfo_put_final(cifs_file);
246 + }
247 }
248
249 int cifs_open(struct inode *inode, struct file *file)
250 --- a/fs/smb/client/smb1ops.c
251 +++ b/fs/smb/client/smb1ops.c
252 @@ -753,11 +753,11 @@ cifs_set_fid(struct cifsFileInfo *cfile,
253 cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
254 }
255
256 -static void
257 +static int
258 cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon,
259 struct cifs_fid *fid)
260 {
261 - CIFSSMBClose(xid, tcon, fid->netfid);
262 + return CIFSSMBClose(xid, tcon, fid->netfid);
263 }
264
265 static int
266 --- a/fs/smb/client/smb2ops.c
267 +++ b/fs/smb/client/smb2ops.c
268 @@ -1411,14 +1411,14 @@ smb2_set_fid(struct cifsFileInfo *cfile,
269 memcpy(cfile->fid.create_guid, fid->create_guid, 16);
270 }
271
272 -static void
273 +static int
274 smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
275 struct cifs_fid *fid)
276 {
277 - SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
278 + return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
279 }
280
281 -static void
282 +static int
283 smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
284 struct cifsFileInfo *cfile)
285 {
286 @@ -1429,7 +1429,7 @@ smb2_close_getattr(const unsigned int xi
287 rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid,
288 cfile->fid.volatile_fid, &file_inf);
289 if (rc)
290 - return;
291 + return rc;
292
293 inode = d_inode(cfile->dentry);
294
295 @@ -1458,6 +1458,7 @@ smb2_close_getattr(const unsigned int xi
296
297 /* End of file and Attributes should not have to be updated on close */
298 spin_unlock(&inode->i_lock);
299 + return rc;
300 }
301
302 static int
303 --- a/fs/smb/client/smb2pdu.c
304 +++ b/fs/smb/client/smb2pdu.c
305 @@ -3606,9 +3606,9 @@ replay_again:
306 memcpy(&pbuf->network_open_info,
307 &rsp->network_open_info,
308 sizeof(pbuf->network_open_info));
309 + atomic_dec(&tcon->num_remote_opens);
310 }
311
312 - atomic_dec(&tcon->num_remote_opens);
313 close_exit:
314 SMB2_close_free(&rqst);
315 free_rsp_buf(resp_buftype, rsp);