diff options
| author | Enzo Matsumiya <ematsumiya@suse.de> | 2025-04-04 11:40:09 -0300 |
|---|---|---|
| committer | Enzo Matsumiya <ematsumiya@suse.de> | 2025-04-04 11:40:09 -0300 |
| commit | d3fef38b24a36c51d0774fd9b87da4393dd16cc2 (patch) | |
| tree | 3ede57359d342547943d69acb6fd82588727913b | |
| parent | 19688fc9e24f4c9be2a6841b37065dead642180f (diff) | |
| download | linux-d3fef38b24a36c51d0774fd9b87da4393dd16cc2.tar.gz linux-d3fef38b24a36c51d0774fd9b87da4393dd16cc2.tar.bz2 linux-d3fef38b24a36c51d0774fd9b87da4393dd16cc2.zip | |
smb: client: cdir no leasebreaks
Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
| -rw-r--r-- | fs/smb/client/cached_dir.c | 320 | ||||
| -rw-r--r-- | fs/smb/client/cached_dir.h | 14 | ||||
| -rw-r--r-- | fs/smb/client/readdir.c | 8 | ||||
| -rw-r--r-- | fs/smb/client/smb2inode.c | 2 | ||||
| -rw-r--r-- | fs/smb/client/smb2ops.c | 26 |
5 files changed, 85 insertions, 285 deletions
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index ddc8e7bd1fd8..a6ca99d62bc8 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -18,134 +18,8 @@ #define CDIR_FIND_LEASEKEY 0x4 #define CDIR_FIND_PTR 0x8 - -static struct cached_lease_break *check_lease_break(struct cached_dirs *cdirs, u8 *lease_key) -{ - struct cached_lease_break *lb; - - if (!cdirs || !lease_key) - return NULL; - - write_seqlock(&cdirs->seq); - list_for_each_entry(lb, &cdirs->lease_breaks, head) { - if (!memcmp(lb->lease_key, lease_key, 16)) { - atomic_inc(&lb->recvs); - write_sequnlock(&cdirs->seq); - - return lb; - } - } - write_sequnlock(&cdirs->seq); - - return NULL; -} - -static void recv_lease_break(struct cached_dirs *cdirs, u8 *lease_key) -{ - struct cached_lease_break *lb; - - if (!cdirs || !lease_key) - return; - - if (check_lease_break(cdirs, lease_key)) - return; - - lb = kzalloc(sizeof(*lb), GFP_ATOMIC); - if (!lb) - return; - - lb->lease_key = kmemdup(lease_key, 16, GFP_ATOMIC); - if (!lb->lease_key) { - kfree(lb); - return; - } - - INIT_LIST_HEAD(&lb->head); - atomic_set(&lb->recvs, 1); - atomic_set(&lb->acks, 0); - lb->time = jiffies; - - write_seqlock(&cdirs->seq); - list_add_tail(&lb->head, &cdirs->lease_breaks); - write_sequnlock(&cdirs->seq); - - pr_err("received leasebreak: %*ph\n", 16, lease_key); -} - -static void ack_lease_break(struct cached_lease_break *lb) -{ - if (!lb) - return; - - atomic_inc(&lb->acks); -} - -static bool drop_lease_break(struct cached_lease_break *lb, bool teardown) -{ - if (!lb) - return true; - - if (teardown || (atomic_read(&lb->acks) > atomic_read(&lb->recvs)) || - time_is_before_jiffies(lb->time + dir_cache_timeout * HZ)) { - list_del(&lb->head); - kfree(lb->lease_key); - kfree(lb); - return true; - } - - return false; -} - -static void cleanup_lease_breaks(struct cached_dirs *cdirs, bool teardown) -{ - struct cached_lease_break *lb, *q; - - write_seqlock(&cdirs->seq); - list_for_each_entry_safe(lb, q, &cdirs->lease_breaks, head) - drop_lease_break(lb, teardown); - write_sequnlock(&cdirs->seq); -} - static bool cdir_match_key(struct cached_dir *cdir, const void *key, int mode); -static void __dump_cdir(struct cached_dir *cdir, const char *caller) -{ -//#if 0 -// pr_err("%s: cdir=%p, path='%s'\n", caller, cdir, cdir->path); -//#else -// pr_err("%s: cdir=%p:\n", caller, cdir); -// pr_err("\t\tpath=%s\n", cdir->path); -// pr_err("\t\tdentry=%pd\n", cdir->dentry); -// if (cdir->lease_key) -// pr_err("\t\tleasekey=%*ph\n", 16, cdir->lease_key); -// else -// pr_err("\t\tinvalid!\n"); -// pr_err("\t\texpired=%s\n", str_yes_no(cdir_is_expired(cdir))); -// pr_err("\t\tuses=%u\n", kref_read(&cdir->ref)); -// pr_err("\t\tpfid=0x%llx\n", cdir->pfid); -// pr_err("\t\tvfid=0x%llx\n", cdir->vfid); -//#endif -// pr_err("\t\t--------\n"); -} - -static void dump_cdir(struct cached_dir *cdir, const char *caller) -{ - struct cached_dir copy = {}; - int seq = 0; - - if (!cdir) - return; - - do { - read_seqbegin_or_lock(&cdir->seq, &seq); - - copy = *cdir; - __dump_cdir(cdir, caller); - } while (need_seqretry(&cdir->seq, seq)); - - done_seqretry(&cdir->seq, seq); -} - /* * Utils */ @@ -281,25 +155,6 @@ static inline bool cdir_in_use(struct cached_dir *cdir, void __maybe_unused *arg return (kref_read(&cdir->ref) > 1); } -static bool cdir_got_lease_break(struct cached_dir *cdir, void *arg) -{ - struct cached_lease_break *lb; - struct cached_dirs *cdirs = (struct cached_dirs *)arg; - - if (!cdir || !cdirs) - return true; - - if (!cdir_has_lease(cdir, NULL)) - return true; - - lb = check_lease_break(cdirs, cdir->lease_key); - if (!lb) - return false; - - ack_lease_break(lb); - return true; -} - static inline bool cdir_check(struct cached_dir *cdir, void *arg, bool (*fn)(struct cached_dir *cdir, void *arg)) { @@ -323,7 +178,6 @@ static inline bool cdir_check(struct cached_dir *cdir, void *arg, #define cdir_check_lease(cdir) cdir_check(cdir, NULL, cdir_has_lease) #define cdir_check_valid(cdir) cdir_check(cdir, NULL, cdir_is_valid) #define cdir_check_use(cdir) cdir_check(cdir, NULL, cdir_in_use) -#define cdir_check_lease_break(cdir, arg) cdir_check(cdir, arg, cdir_got_lease_break) /* * Mark a cdir as invalid (i.e. no lease anymore). @@ -435,11 +289,20 @@ static int cdir_match_check(struct cached_dir *cdir, const void *key, int mode) read_seqbegin_or_lock(&cdir->seq, &seq); - if (cdir_match_key(cdir, key, mode)) - ret = 0; + if (cdir_match_key(cdir, key, mode)) { + if (kref_get_unless_zero(&cdir->ref)) { + if (cdir_is_valid(cdir, NULL)) + ret = 0; + else + ret = -EINVAL; + } + } - if (need_seqretry(&cdir->seq, seq)) + if (need_seqretry(&cdir->seq, seq)) { + if (!ret) + kref_put(&cdir->ref, cdir_invalidate_put); ret = -ECHILD; + } done_seqretry(&cdir->seq, seq); @@ -476,20 +339,10 @@ static struct cached_dir *cdir_find_entry(struct cached_dirs *cdirs, const void } } while (need_seqretry(&cdirs->seq, seq) && ret != -ECHILD); -#if 0 - if (ret == -ECHILD) - goto out; - - ret = 0; -#endif done_seqretry(&cdirs->seq, seq); - if (!ret) { - if (cdir_check_valid(cdir)) - return cdir; - - ret = -EINVAL; - } + if (!ret) + return cdir; return ERR_PTR(ret); } @@ -569,8 +422,13 @@ static void cdir_find_drop(struct cached_dirs *cdirs, const void *key, int mode) if (IS_ERR(cdir)) return; - if (cdir_check_lease_break(cdir, cdirs)) - return; +#if 0 + if (cdir_is_open(cdir, NULL)) { + (void) SMB2_close(get_xid(), cdirs->tcon, cdir->pfid, cdir->vfid); + cdir->pfid = COMPOUND_FID; + cdir->vfid = COMPOUND_FID; + } +#endif /* * Put our ref. @@ -593,22 +451,6 @@ static void cdir_find_drop(struct cached_dirs *cdirs, const void *key, int mode) } } -#if 0 - //pr_err("%s: dropping now cdir=%p\n", __func__, cdir); - write_seqlock(&cdir->seq); - // leave it on the list?? - //cdir_del_entry(cdir, ©); - copy.dentry = READ_ONCE(cdir->dentry); - cdir_free(cdir); - write_sequnlock(&cdir->seq); - - // leave it on the list? - //kfree(cdir); - - if (copy.dentry) - dput(copy.dentry); -#endif - mod_delayed_work(cfid_put_wq, &cdirs->cleanup_work, 0); } @@ -621,23 +463,13 @@ static bool cdir_cleanup_sync(struct cached_dirs *cdirs, bool teardown) if (WARN_ON(!copy)) return list_empty(&cdirs->list); - cleanup_lease_breaks(cdirs, teardown); - write_seqlock(&cdirs->seq); list_for_each_entry_safe(cdir, q, &cdirs->list, head) { - if (!teardown && cdir_check_valid(cdir)) { - dump_cdir(cdir, "skipping valid cleanup"); + if (!teardown && cdir_check_valid(cdir)) continue; - } write_seqlock(&cdir->seq); if (cdir_invalidate_unlocked(cdir) || teardown) { - if (teardown) - __dump_cdir(cdir, "tearing down"); - else - __dump_cdir(cdir, "cleaning up"); - - //cdir_del_entry(cdir, ©[i++]); list_del(&cdir->head); copy[i].dentry = READ_ONCE(cdir->dentry); copy[i].pfid = READ_ONCE(cdir->pfid); @@ -655,19 +487,14 @@ static bool cdir_cleanup_sync(struct cached_dirs *cdirs, bool teardown) continue; } write_sequnlock(&cdir->seq); - - dump_cdir(cdir, "skipping invalid cleanup"); } write_sequnlock(&cdirs->seq); while (--i >= 0) { - if (copy[i].dentry) { - //pr_err("%s: cleaning up[%d]: put dentry='%pd'\n", __func__, i, copy[i].dentry); + if (copy[i].dentry) dput(copy[i].dentry); - } if (cdir_is_open(©[i], NULL)) { - //pr_err("%s: cleaning up[%d]: remote close=0x%llx\n", __func__, i, copy[i].pfid); SMB2_close(get_xid(), cdirs->tcon, copy[i].pfid, copy[i].vfid); atomic_dec(&cdirs->tcon->num_remote_opens); } @@ -870,20 +697,8 @@ struct cached_dir *cached_dir_get_path(struct cached_dirs *cdirs, const char *pa { struct cached_dir *cdir = cdir_find_entry(cdirs, cdir_root_path(path), CDIR_FIND_PATH); - if (IS_ERR(cdir)) - return NULL; - - if (cdir_check_lease_break(cdir, cdirs)) { - cdir_invalidate(cdir); - return NULL; - } - - if (kref_get_unless_zero(&cdir->ref)) { - if (cdir_check_valid(cdir)) - return cdir; - - cdir_put(cdir); - } + if (!IS_ERR(cdir)) + return cdir; return NULL; } @@ -901,8 +716,11 @@ struct cached_dir *cached_dir_open(struct cached_dirs *cdirs, const char *path, /* Try to find a cached entry by @path. */ cdir = cached_dir_get_path(cdirs, cdir_root_path(path)); - if (cdir) - return cdir; + if (cdir) { + if (!IS_ERR(cdir)) + return cdir; + cdir = NULL; + } /* Check if we can actually cache a new entry. */ if (atomic_read(&cdirs->count) >= 16) { @@ -922,28 +740,11 @@ struct cached_dir *cached_dir_open(struct cached_dirs *cdirs, const char *path, */ if (IS_ERR(cdir)) { cifs_dbg(VFS, "%s: remote open failed, err=%ld\n", __func__, PTR_ERR(cdir)); - return NULL; - } - - /* - * Cache a new entry only if we're sure there are no duplicates. - * - * We want to know if a concurrent thread successfully cached the same path as us, so - * check if there was a lease break for us. - */ - if (cdir_check_lease_break(cdir, cdirs)) { - dput(cdir->dentry); - cdir_free(cdir); - kfree(cdir); - cdir = NULL; + return ERR_CAST(cdir); } - /* - * Regardless if there was a leasebreak for our @path, we still check (preventively) if - * that same path wasn't added before us. - */ check = cached_dir_get_path(cdirs, cdir_root_path(path)); - if (check && !IS_ERR(check)) { + if (check) { pr_err("%s: race in open path=%s\n", __func__, cdir_root_path(path)); // cdir != NULL means no leasebreak for this path, but also means some other bug? @@ -953,7 +754,9 @@ struct cached_dir *cached_dir_open(struct cached_dirs *cdirs, const char *path, kfree(cdir); } - return check; + if (!IS_ERR(check)) + return check; + check = NULL; } // got a leasebreak but nobody cached it yet? should retry... @@ -976,13 +779,6 @@ void cached_dir_close(struct cached_dirs *cdirs, struct cached_dir *cdir) if (!cdir) return; - //dump_cdir(cdir, "closing"); - - if (!cdir_check_valid(cdir)) { - pr_err("%s: closing cdir no longer valid, should reopen? path='%s', dentry='%pd', leasekey=%*ph\n", - __func__, cdir->path, cdir->dentry, 16, cdir->lease_key); - } - if (cdir_put(cdir)) { mod_delayed_work(cfid_put_wq, &cdirs->cleanup_work, 0); return; @@ -994,6 +790,7 @@ void cached_dir_close(struct cached_dirs *cdirs, struct cached_dir *cdir) void cached_dir_drop(struct cached_dirs *cdirs, const char *path) { pr_err("%s: cached dir drop (path=%s)\n", __func__, cdir_root_path(path)); + cdir_find_drop(cdirs, cdir_root_path(path), CDIR_FIND_PATH); } @@ -1002,7 +799,6 @@ void cached_dir_lease_break(struct cached_dirs *cdirs, u8 *lease_key) { pr_err("%s: cached dir lease break (key=%*ph)\n", __func__, 16, lease_key); - recv_lease_break(cdirs, lease_key); cdir_find_drop(cdirs, lease_key, CDIR_FIND_LEASEKEY); } @@ -1019,6 +815,9 @@ bool cached_dir_check_expired_path(struct cached_dirs *cdirs, const char *path) /* cached_dir_get_path() already checks for valid + non-expired, nothing else to do here */ cdir = cached_dir_get_path(cdirs, path); if (cdir) { + if (IS_ERR(cdir)) + return true; + if (cdir_put(cdir)) return true; return false; @@ -1040,26 +839,25 @@ bool cached_dir_check_expired_dentry(struct cached_dirs *cdirs, struct dentry *d cdir = cdir_find_entry(cdirs, dentry, CDIR_FIND_DENTRY); pr_err("%s: cdir=%p (err=%ld) for dentry='%pd', expired=%s\n", cdirs->tcon->tree_name, cdir, !cdir ? 0 : (IS_ERR(cdir) ? PTR_ERR(cdir) : 0), dentry, (cdir && !IS_ERR(cdir)) ? str_yes_no(cdir_check_expired(cdir, (void *)time)) : "yes"); - if (!cdir || IS_ERR(cdir)) + + if (IS_ERR(cdir)) return true; - if (cdir) { - if (kref_get_unless_zero(&cdir->ref)) { - bool older = cdir_check_expired(cdir, (void *)time); - - //pr_err("%s: cdir=%p, dentry=%pd, older=%s (cdir=%u, time=%u)\n", __func__, - // cdir, dentry, str_yes_no(older), jiffies_to_msecs(jiffies - cdir->time), jiffies_to_msecs(jiffies - time)); - if (cdir_put(cdir)) { - pr_err("%s: (last ref) cdir=%p, dentry=%pd, older=%s (cdir=%u, time=%u)\n", __func__, - cdir, dentry, str_yes_no(older), jiffies_to_msecs(jiffies - cdir->time), jiffies_to_msecs(jiffies - time)); - return true; - } - - if (older) - pr_err("%s: (ref) cdir=%p, dentry=%pd, older=true (cdir=%u, time=%u)\n", __func__, - cdir, dentry, jiffies_to_msecs(jiffies - cdir->time), jiffies_to_msecs(jiffies - time)); - return older; + if (kref_get_unless_zero(&cdir->ref)) { + bool older = cdir_check_expired(cdir, (void *)time); + + //pr_err("%s: cdir=%p, dentry=%pd, older=%s (cdir=%u, time=%u)\n", __func__, + // cdir, dentry, str_yes_no(older), jiffies_to_msecs(jiffies - cdir->time), jiffies_to_msecs(jiffies - time)); + if (cdir_put(cdir)) { + pr_err("%s: (last ref) cdir=%p, dentry=%pd, older=%s (cdir=%u, time=%u)\n", __func__, + cdir, dentry, str_yes_no(older), jiffies_to_msecs(jiffies - cdir->time), jiffies_to_msecs(jiffies - time)); + return true; } + + if (older) + pr_err("%s: (ref) cdir=%p, dentry=%pd, older=true (cdir=%u, time=%u)\n", __func__, + cdir, dentry, jiffies_to_msecs(jiffies - cdir->time), jiffies_to_msecs(jiffies - time)); + return older; } pr_err("%s: no cdir for dentry=%pd (%p, positive=%s), time=%lu\n", __func__, dentry, dentry, str_yes_no(d_really_is_positive(dentry)), time); @@ -1083,7 +881,6 @@ void cached_dir_init(struct cifs_tcon *tcon) tcon->tc_count++; INIT_LIST_HEAD(&tcon->cdirs->list); - INIT_LIST_HEAD(&tcon->cdirs->lease_breaks); INIT_DELAYED_WORK(&tcon->cdirs->cleanup_work, cdir_cleanup); seqlock_init(&tcon->cdirs->seq); atomic_set(&tcon->cdirs->count, 0); @@ -1160,7 +957,6 @@ void cached_dir_destroy(struct cifs_sb_info *cifs_sb) void cached_dir_cleanup(struct cifs_tcon *tcon, bool free) { - struct cached_lease_break *lb, *q; struct cached_dirs *cdirs; struct cached_dir *cdir; @@ -1184,12 +980,6 @@ void cached_dir_cleanup(struct cifs_tcon *tcon, bool free) WRITE_ONCE(cdir->vfid, COMPOUND_FID); write_sequnlock(&cdir->seq); } - - list_for_each_entry_safe(lb, q, &cdirs->lease_breaks, head) { - list_del(&lb->head); - kfree(lb->lease_key); - kfree(lb); - } write_sequnlock(&cdirs->seq); (void) cdir_cleanup_sync(cdirs, true); diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h index 8753df164171..4e5c9b86fc49 100644 --- a/fs/smb/client/cached_dir.h +++ b/fs/smb/client/cached_dir.h @@ -48,17 +48,8 @@ struct cached_dir { struct smb2_file_all_info *info; }; -struct cached_lease_break { - struct list_head head; - u8 *lease_key; - atomic_t recvs; - atomic_t acks; - unsigned long time; -}; - struct cached_dirs { struct list_head list; - struct list_head lease_breaks; seqlock_t seq; /* tcon backreference */ @@ -71,11 +62,6 @@ struct cached_dirs { struct cached_dir *cached_dir_get_path(struct cached_dirs *cdirs, const char *path); struct cached_dir *cached_dir_open(struct cached_dirs *cdirs, const char *path, struct cifs_sb_info *cifs_sb); -#if 0 -int cached_dir_add(struct cached_dirs *cdirs, const char *path, struct dentry *dentry, - struct cifs_sb_info *cifs_sb, struct cifs_fid *fid, - struct smb2_file_all_info *info); -#endif void cached_dir_close(struct cached_dirs *cdirs, struct cached_dir *cdir); void cached_dir_drop(struct cached_dirs *cdirs, const char *path); void cached_dir_lease_break(struct cached_dirs *cdirs, u8 *lease_key); diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c index 1311caa93fee..07aa3cb0c4a6 100644 --- a/fs/smb/client/readdir.c +++ b/fs/smb/client/readdir.c @@ -963,6 +963,10 @@ int cifs_readdir(struct file *file, struct dir_context *ctx) if (!cdir) { pr_err("%s: (1) cdir not found for path='%s' (unexpected because remote open)\n", __func__, full_path); goto cache_not_found; + } else if (IS_ERR(cdir)) { + rc = PTR_ERR(cdir); + cdir = NULL; + goto rddir2_exit; } if (!cached_dir_emit_entries(cdir, ctx, file)) @@ -1017,6 +1021,10 @@ int cifs_readdir(struct file *file, struct dir_context *ctx) if (!cdir) { pr_err("%s: (2) cdir not found for path='%s' (unexpected because remote open)\n", __func__, full_path); goto rddir2_exit; + } else if (IS_ERR(cdir)) { + rc = PTR_ERR(cdir); + cdir = NULL; + goto rddir2_exit; } if (!current_entry) { diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 8ab20e56c97e..1b5602cadfcf 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -938,6 +938,8 @@ int smb2_query_path_info(const unsigned int xid, if (!cdir) { pr_err("%s: cache dir not found for path='\'\n", __func__); goto no_cache; + } else if (IS_ERR(cdir)) { + return PTR_ERR(cdir); } /* If it is a root and its handle is cached then use it */ diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 5a336ebfa533..094beadb89e3 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -852,6 +852,9 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, cdir = cached_dir_open(tcon->cdirs, "", cifs_sb); if (cdir) { + if (IS_ERR(cdir)) + return; + fid.persistent_fid = cdir->pfid; fid.volatile_fid = cdir->vfid; memcpy(fid.lease_key, cdir->lease_key, SMB2_LEASE_KEY_SIZE); @@ -930,22 +933,28 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_open_parms oparms; struct kvec err_iov = {}; struct cifs_fid fid; + struct cached_dir *cdir; bool islink; int rc, rc2; +#if 0 if (!cached_dir_check_expired_path(tcon->cdirs, full_path)) return 0; +#endif -#if 0 // lookup + open (leasebreak?) cdir = cached_dir_open(tcon->cdirs, full_path, cifs_sb); if (cdir) { - bool expired = cached_dir_is_expired(cdir); + bool expired; + + if (IS_ERR(cdir)) + return PTR_ERR(cdir); + + expired = cached_dir_is_expired(cdir); cached_dir_close(tcon->cdirs, cdir); if (!expired) return 0; } -#endif pr_err("%s: cached dir for path='%s' not found or expired, remote opening...\n", __func__, *full_path ? full_path : "\\"); @@ -2716,10 +2725,15 @@ replay_again: */ if (!strcmp(path, "")) { cdir = cached_dir_open(tcon->cdirs, path, cifs_sb); - if (!cdir) + if (!cdir) { pr_err("%s: cache dir not found for path='\\' (unexpected because of remote open)\n", __func__); - else + } else if (IS_ERR(cdir)) { + rc = PTR_ERR(cdir); + kfree(vars); + goto out_free_path; + } else { using_cdir = true; + } } rqst[0].rq_iov = vars->open_iov; @@ -2812,7 +2826,7 @@ replay_again: SMB2_close_free(&rqst[2]); free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - cached_dir_close(tcon->cdirs, cdir); + cached_dir_close(tcon->cdirs, cdir); if (!cdir && using_cdir) { pr_err("%s: was using cdir, it's gone now, retrying...\n", __func__); rc = -EAGAIN; |
