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
6 From: Ritvik Budhiraja <rbudhiraja@microsoft.com>
8 commit 173217bd73365867378b5e75a86f0049e1069ee8 upstream.
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.
15 This can help avoid handle leaks
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>
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(-)
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)
35 struct cached_fid *cfid = container_of(ref, struct cached_fid,
39 spin_lock(&cfid->cfids->cfid_list_lock);
41 @@ -430,9 +431,10 @@ smb2_close_cached_fid(struct kref *ref)
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);
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;
64 @@ -1893,6 +1894,13 @@ init_cifs(void)
65 goto out_destroy_cifsoplockd_wq;
68 + serverclose_wq = alloc_workqueue("serverclose",
69 + WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
70 + if (!serverclose_wq) {
72 + goto out_destroy_serverclose_wq;
75 rc = cifs_init_inodecache();
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);
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);
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);
101 - void (*close)(const unsigned int, struct cifs_tcon *,
102 + int (*close)(const unsigned int, struct cifs_tcon *,
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 */
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 */
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;
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
139 static void cifsFileInfo_put_work(struct work_struct *work);
140 +void serverclose_work(struct work_struct *work);
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);
156 +void serverclose_work(struct work_struct *work)
158 + struct cifsFileInfo *cifs_file = container_of(work,
159 + struct cifsFileInfo, serverclose);
161 + struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
163 + struct TCP_Server_Info *server = tcon->ses->server;
166 + int MAX_RETRIES = 4;
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);
174 + if (rc == -EBUSY || rc == -EAGAIN) {
178 + } while ((rc == -EBUSY || rc == -EAGAIN) && (retries < MAX_RETRIES)
181 + if (retries == MAX_RETRIES)
182 + pr_warn("Serverclose failed %d times, giving up\n", MAX_RETRIES);
184 + if (cifs_file->offload)
185 + queue_work(fileinfo_put_wq, &cifs_file->put);
187 + cifsFileInfo_put_final(cifs_file);
191 * cifsFileInfo_put - release a reference of file priv data
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;
199 spin_lock(&tcon->open_file_lock);
200 spin_lock(&cifsi->open_file_lock);
201 spin_lock(&cifs_file->file_info_lock);
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;
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);
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;
229 if (oplock_break_cancelled)
230 @@ -695,10 +741,15 @@ void _cifsFileInfo_put(struct cifsFileIn
232 cifs_del_pending_open(&open);
235 - queue_work(fileinfo_put_wq, &cifs_file->put);
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) {
243 + queue_work(fileinfo_put_wq, &cifs_file->put);
245 + cifsFileInfo_put_final(cifs_file);
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);
258 cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon,
259 struct cifs_fid *fid)
261 - CIFSSMBClose(xid, tcon, fid->netfid);
262 + return CIFSSMBClose(xid, tcon, fid->netfid);
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);
274 smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
275 struct cifs_fid *fid)
277 - SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
278 + return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
283 smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
284 struct cifsFileInfo *cfile)
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);
293 inode = d_inode(cfile->dentry);
295 @@ -1458,6 +1458,7 @@ smb2_close_getattr(const unsigned int xi
297 /* End of file and Attributes should not have to be updated on close */
298 spin_unlock(&inode->i_lock);
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);
312 - atomic_dec(&tcon->num_remote_opens);
314 SMB2_close_free(&rqst);
315 free_rsp_buf(resp_buftype, rsp);