Skip to content

Commit 562b17b

Browse files
committed
cifs: do not skip link targets when an I/O fails
jira LE-1907 Rebuild_History Non-Buildable kernel-4.18.0-553.16.1.el8_10 commit-author Paulo Alcantara <[email protected]> commit 5d7e282 Empty-Commit: Cherry-Pick Conflicts during history rebuild. Will be included in final tarball splat. Ref for failed cherry-pick at: ciq/ciq_backports/kernel-4.18.0-553.16.1.el8_10/5d7e2825.failed When I/O fails in one of the currently connected DFS targets, retry it from other targets as specified in MS-DFSC "3.1.5.2 I/O Operation to +Target Fails with an Error Other Than STATUS_PATH_NOT_COVERED." Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Cc: [email protected] Signed-off-by: Steve French <[email protected]> (cherry picked from commit 5d7e282) Signed-off-by: Jonathan Maple <[email protected]> # Conflicts: # fs/cifs/connect.c
1 parent ed7a08b commit 562b17b

File tree

1 file changed

+361
-0
lines changed

1 file changed

+361
-0
lines changed
Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
cifs: do not skip link targets when an I/O fails
2+
3+
jira LE-1907
4+
Rebuild_History Non-Buildable kernel-4.18.0-553.16.1.el8_10
5+
commit-author Paulo Alcantara <[email protected]>
6+
commit 5d7e282541fc91b831a5c4477c5d72881c623df9
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.16.1.el8_10/5d7e2825.failed
10+
11+
When I/O fails in one of the currently connected DFS targets, retry it
12+
from other targets as specified in MS-DFSC "3.1.5.2 I/O Operation to
13+
+Target Fails with an Error Other Than STATUS_PATH_NOT_COVERED."
14+
15+
Signed-off-by: Paulo Alcantara (SUSE) <[email protected]>
16+
17+
Signed-off-by: Steve French <[email protected]>
18+
(cherry picked from commit 5d7e282541fc91b831a5c4477c5d72881c623df9)
19+
Signed-off-by: Jonathan Maple <[email protected]>
20+
21+
# Conflicts:
22+
# fs/cifs/connect.c
23+
diff --cc fs/cifs/connect.c
24+
index 6e27e4672066,d6f8ccc7bfe2..000000000000
25+
--- a/fs/cifs/connect.c
26+
+++ b/fs/cifs/connect.c
27+
@@@ -3559,117 -3491,223 +3559,327 @@@ int cifs_mount(struct cifs_sb_info *cif
28+
* to respond with PATH_NOT_COVERED to requests that include the prefix.
29+
*/
30+
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) ||
31+
- dfs_cache_find(mnt_ctx->xid, mnt_ctx->ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
32+
- ctx->UNC + 1, NULL, root_tl)) {
33+
+ dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), ctx->UNC + 1, NULL,
34+
+ NULL)) {
35+
if (rc)
36+
- return rc;
37+
+ goto error;
38+
/* Check if it is fully accessible and then mount it */
39+
- rc = is_path_remote(mnt_ctx);
40+
+ rc = is_path_remote(cifs_sb, ctx, xid, server, tcon);
41+
if (!rc)
42+
++<<<<<<< HEAD
43+
+ goto out;
44+
+ if (rc != -EREMOTE)
45+
+ goto error;
46+
+ }
47+
+
48+
+ mount_put_conns(cifs_sb, xid, server, ses, tcon);
49+
++=======
50+
+ *isdfs = false;
51+
+ else if (rc != -EREMOTE)
52+
+ return rc;
53+
+ }
54+
+ return 0;
55+
+ }
56+
+
57+
+ static int connect_dfs_target(struct mount_ctx *mnt_ctx, const char *full_path,
58+
+ const char *ref_path, struct dfs_cache_tgt_iterator *tit)
59+
+ {
60+
+ int rc;
61+
+ struct dfs_info3_param ref = {};
62+
+ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
63+
+ char *oldmnt = cifs_sb->ctx->mount_options;
64+
+
65+
+ cifs_dbg(FYI, "%s: full_path=%s ref_path=%s target=%s\n", __func__, full_path, ref_path,
66+
+ dfs_cache_get_tgt_name(tit));
67+
+
68+
+ rc = dfs_cache_get_tgt_referral(ref_path, tit, &ref);
69+
+ if (rc)
70+
+ goto out;
71+
+
72+
+ rc = expand_dfs_referral(mnt_ctx, full_path, &ref);
73+
+ if (rc)
74+
+ goto out;
75+
+
76+
+ /* Connect to new target only if we were redirected (e.g. mount options changed) */
77+
+ if (oldmnt != cifs_sb->ctx->mount_options) {
78+
+ mount_put_conns(mnt_ctx);
79+
+ rc = mount_get_dfs_conns(mnt_ctx);
80+
+ }
81+
+ if (!rc) {
82+
+ if (cifs_is_referral_server(mnt_ctx->tcon, &ref))
83+
+ set_root_ses(mnt_ctx);
84+
+ rc = dfs_cache_update_tgthint(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls,
85+
+ cifs_remap(cifs_sb), ref_path, tit);
86+
+ }
87+
+
88+
+ out:
89+
+ free_dfs_info_param(&ref);
90+
+ return rc;
91+
+ }
92+
+
93+
+ static int connect_dfs_root(struct mount_ctx *mnt_ctx, struct dfs_cache_tgt_list *root_tl)
94+
+ {
95+
+ int rc;
96+
+ char *full_path;
97+
+ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
98+
+ struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
99+
+ struct dfs_cache_tgt_iterator *tit;
100+
+
101+
+ /* Put initial connections as they might be shared with other mounts. We need unique dfs
102+
+ * connections per mount to properly failover, so mount_get_dfs_conns() must be used from
103+
+ * now on.
104+
+ */
105+
+ mount_put_conns(mnt_ctx);
106+
+ mount_get_dfs_conns(mnt_ctx);
107+
+ set_root_ses(mnt_ctx);
108+
+
109+
+ full_path = build_unc_path_to_root(ctx, cifs_sb, true);
110+
+ if (IS_ERR(full_path))
111+
+ return PTR_ERR(full_path);
112+
+
113+
+ mnt_ctx->origin_fullpath = dfs_cache_canonical_path(ctx->UNC, cifs_sb->local_nls,
114+
+ cifs_remap(cifs_sb));
115+
+ if (IS_ERR(mnt_ctx->origin_fullpath)) {
116+
+ rc = PTR_ERR(mnt_ctx->origin_fullpath);
117+
+ mnt_ctx->origin_fullpath = NULL;
118+
+ goto out;
119+
+ }
120+
+
121+
+ /* Try all dfs root targets */
122+
+ for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(root_tl);
123+
+ tit; tit = dfs_cache_get_next_tgt(root_tl, tit)) {
124+
+ rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->origin_fullpath + 1, tit);
125+
+ if (!rc) {
126+
+ mnt_ctx->leaf_fullpath = kstrdup(mnt_ctx->origin_fullpath, GFP_KERNEL);
127+
+ if (!mnt_ctx->leaf_fullpath)
128+
+ rc = -ENOMEM;
129+
+ break;
130+
+ }
131+
+ }
132+
+
133+
+ out:
134+
+ kfree(full_path);
135+
+ return rc;
136+
+ }
137+
+
138+
+ static int __follow_dfs_link(struct mount_ctx *mnt_ctx)
139+
+ {
140+
+ int rc;
141+
+ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
142+
+ struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
143+
+ char *full_path;
144+
+ struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
145+
+ struct dfs_cache_tgt_iterator *tit;
146+
+
147+
+ full_path = build_unc_path_to_root(ctx, cifs_sb, true);
148+
+ if (IS_ERR(full_path))
149+
+ return PTR_ERR(full_path);
150+
+
151+
+ kfree(mnt_ctx->leaf_fullpath);
152+
+ mnt_ctx->leaf_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls,
153+
+ cifs_remap(cifs_sb));
154+
+ if (IS_ERR(mnt_ctx->leaf_fullpath)) {
155+
+ rc = PTR_ERR(mnt_ctx->leaf_fullpath);
156+
+ mnt_ctx->leaf_fullpath = NULL;
157+
+ goto out;
158+
+ }
159+
+
160+
+ /* Get referral from dfs link */
161+
+ rc = dfs_cache_find(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls,
162+
+ cifs_remap(cifs_sb), mnt_ctx->leaf_fullpath + 1, NULL, &tl);
163+
+ if (rc)
164+
+ goto out;
165+
+
166+
+ /* Try all dfs link targets. If an I/O fails from currently connected DFS target with an
167+
+ * error other than STATUS_PATH_NOT_COVERED (-EREMOTE), then retry it from other targets as
168+
+ * specified in MS-DFSC "3.1.5.2 I/O Operation to Target Fails with an Error Other Than
169+
+ * STATUS_PATH_NOT_COVERED."
170+
+ */
171+
+ for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(&tl);
172+
+ tit; tit = dfs_cache_get_next_tgt(&tl, tit)) {
173+
+ rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->leaf_fullpath + 1, tit);
174+
+ if (!rc) {
175+
+ rc = is_path_remote(mnt_ctx);
176+
+ if (!rc || rc == -EREMOTE)
177+
+ break;
178+
+ }
179+
+ }
180+
+
181+
+ out:
182+
+ kfree(full_path);
183+
+ dfs_cache_free_tgts(&tl);
184+
+ return rc;
185+
+ }
186+
+
187+
+ static int follow_dfs_link(struct mount_ctx *mnt_ctx)
188+
+ {
189+
+ int rc;
190+
+ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
191+
+ struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
192+
+ char *full_path;
193+
+ int num_links = 0;
194+
+
195+
+ full_path = build_unc_path_to_root(ctx, cifs_sb, true);
196+
+ if (IS_ERR(full_path))
197+
+ return PTR_ERR(full_path);
198+
+
199+
+ kfree(mnt_ctx->origin_fullpath);
200+
+ mnt_ctx->origin_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls,
201+
+ cifs_remap(cifs_sb));
202+
+ kfree(full_path);
203+
+
204+
+ if (IS_ERR(mnt_ctx->origin_fullpath)) {
205+
+ rc = PTR_ERR(mnt_ctx->origin_fullpath);
206+
+ mnt_ctx->origin_fullpath = NULL;
207+
+ return rc;
208+
+ }
209+
+
210+
+ do {
211+
+ rc = __follow_dfs_link(mnt_ctx);
212+
+ if (!rc || rc != -EREMOTE)
213+
+ break;
214+
+ } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
215+
+
216+
+ return rc;
217+
+ }
218+
+
219+
+ /* Set up DFS referral paths for failover */
220+
+ static void setup_server_referral_paths(struct mount_ctx *mnt_ctx)
221+
+ {
222+
+ struct TCP_Server_Info *server = mnt_ctx->server;
223+
+
224+
+ server->origin_fullpath = mnt_ctx->origin_fullpath;
225+
+ server->leaf_fullpath = mnt_ctx->leaf_fullpath;
226+
+ server->current_fullpath = mnt_ctx->leaf_fullpath;
227+
+ mnt_ctx->origin_fullpath = mnt_ctx->leaf_fullpath = NULL;
228+
+ }
229+
+
230+
+ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
231+
+ {
232+
+ int rc;
233+
+ struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, };
234+
+ struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
235+
+ bool isdfs;
236+
+
237+
+ rc = is_dfs_mount(&mnt_ctx, &isdfs, &tl);
238+
+ if (rc)
239+
+ goto error;
240+
+ if (!isdfs)
241+
+ goto out;
242+
+
243+
+ uuid_gen(&mnt_ctx.mount_id);
244+
+ rc = connect_dfs_root(&mnt_ctx, &tl);
245+
+ dfs_cache_free_tgts(&tl);
246+
+
247+
+ if (rc)
248+
+ goto error;
249+
+
250+
+ rc = is_path_remote(&mnt_ctx);
251+
+ if (rc)
252+
+ rc = follow_dfs_link(&mnt_ctx);
253+
+ if (rc)
254+
+ goto error;
255+
+
256+
+ setup_server_referral_paths(&mnt_ctx);
257+
++>>>>>>> 5d7e282541fc (cifs: do not skip link targets when an I/O fails)
258+
/*
259+
- * After reconnecting to a different server, unique ids won't match anymore, so we disable
260+
- * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).
261+
+ * Ignore error check here because we may failover to other targets from cached a
262+
+ * referral.
263+
+ */
264+
+ (void)mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
265+
+
266+
+ /* Get path of DFS root */
267+
+ ref_path = build_unc_path_to_root(ctx, cifs_sb, false);
268+
+ if (IS_ERR(ref_path)) {
269+
+ rc = PTR_ERR(ref_path);
270+
+ ref_path = NULL;
271+
+ goto error;
272+
+ }
273+
+
274+
+ uuid_gen(&mount_id);
275+
+ set_root_ses(cifs_sb, &mount_id, ses, &root_ses);
276+
+ do {
277+
+ /* Save full path of last DFS path we used to resolve final target server */
278+
+ kfree(full_path);
279+
+ full_path = build_unc_path_to_root(ctx, cifs_sb, !!count);
280+
+ if (IS_ERR(full_path)) {
281+
+ rc = PTR_ERR(full_path);
282+
+ full_path = NULL;
283+
+ break;
284+
+ }
285+
+ /* Chase referral */
286+
+ oldmnt = cifs_sb->ctx->mount_options;
287+
+ rc = expand_dfs_referral(xid, root_ses, ctx, cifs_sb, ref_path + 1);
288+
+ if (rc)
289+
+ break;
290+
+ /* Connect to new DFS target only if we were redirected */
291+
+ if (oldmnt != cifs_sb->ctx->mount_options) {
292+
+ mount_put_conns(cifs_sb, xid, server, ses, tcon);
293+
+ rc = mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
294+
+ }
295+
+ if (rc && !server && !ses) {
296+
+ /* Failed to connect. Try to connect to other targets in the referral. */
297+
+ rc = do_dfs_failover(ref_path + 1, full_path, cifs_sb, ctx, root_ses, &xid,
298+
+ &server, &ses, &tcon);
299+
+ }
300+
+ if (rc == -EACCES || rc == -EOPNOTSUPP || !server || !ses)
301+
+ break;
302+
+ if (!tcon)
303+
+ continue;
304+
+
305+
+ /* Make sure that requests go through new root servers */
306+
+ rc = is_referral_server(ref_path + 1, cifs_sb, tcon, &ref_server);
307+
+ if (rc)
308+
+ break;
309+
+ if (ref_server)
310+
+ set_root_ses(cifs_sb, &mount_id, ses, &root_ses);
311+
+
312+
+ /* Get next dfs path and then continue chasing them if -EREMOTE */
313+
+ rc = next_dfs_prepath(cifs_sb, ctx, xid, server, tcon, &ref_path);
314+
+ /* Prevent recursion on broken link referrals */
315+
+ if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS)
316+
+ rc = -ELOOP;
317+
+ } while (rc == -EREMOTE);
318+
+
319+
+ if (rc || !tcon) {
320+
+ rc = rc ? rc : -ENOENT;
321+
+ goto error;
322+
+ }
323+
+
324+
+ kfree(ref_path);
325+
+ /*
326+
+ * Store DFS full path in both superblock and tree connect structures.
327+
+ *
328+
+ * For DFS root mounts, the prefix path (cifs_sb->prepath) is preserved during reconnect so
329+
+ * only the root path is set in cifs_sb->origin_fullpath and tcon->dfs_path. And for DFS
330+
+ * links, the prefix path is included in both and may be changed during reconnect. See
331+
+ * cifs_tree_connect().
332+
+ */
333+
+ ref_path = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, cifs_remap(cifs_sb));
334+
+ kfree(full_path);
335+
+ full_path = NULL;
336+
+
337+
+ if (IS_ERR(ref_path)) {
338+
+ rc = PTR_ERR(ref_path);
339+
+ ref_path = NULL;
340+
+ goto error;
341+
+ }
342+
+ cifs_sb->origin_fullpath = ref_path;
343+
+
344+
+ ref_path = kstrdup(cifs_sb->origin_fullpath, GFP_KERNEL);
345+
+ if (!ref_path) {
346+
+ rc = -ENOMEM;
347+
+ goto error;
348+
+ }
349+
+ spin_lock(&cifs_tcp_ses_lock);
350+
+ tcon->dfs_path = ref_path;
351+
+ ref_path = NULL;
352+
+ spin_unlock(&cifs_tcp_ses_lock);
353+
+
354+
+ /*
355+
+ * After reconnecting to a different server, unique ids won't
356+
+ * match anymore, so we disable serverino. This prevents
357+
+ * dentry revalidation to think the dentry are stale (ESTALE).
358+
*/
359+
cifs_autodisable_serverino(cifs_sb);
360+
/*
361+
* Unmerged path fs/cifs/connect.c

0 commit comments

Comments
 (0)