2022-08-10 22:00:08 -05:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0 */
|
|
|
|
/*
|
|
|
|
* Functions to handle the cached directory entries
|
|
|
|
*
|
|
|
|
* Copyright (c) 2022, Ronnie Sahlberg <lsahlber@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _CACHED_DIR_H
|
|
|
|
#define _CACHED_DIR_H
|
|
|
|
|
|
|
|
|
2022-08-11 19:04:29 -05:00
|
|
|
struct cached_dirent {
|
|
|
|
struct list_head entry;
|
|
|
|
char *name;
|
|
|
|
int namelen;
|
|
|
|
loff_t pos;
|
|
|
|
struct cifs_fattr fattr;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct cached_dirents {
|
|
|
|
bool is_valid:1;
|
|
|
|
bool is_failed:1;
|
2025-06-11 16:59:02 +05:30
|
|
|
struct file *file; /*
|
|
|
|
* Used to associate the cache with a single
|
|
|
|
* open file instance.
|
|
|
|
*/
|
2022-08-11 19:04:29 -05:00
|
|
|
struct mutex de_mutex;
|
2025-06-19 21:05:32 +05:30
|
|
|
loff_t pos; /* Expected ctx->pos */
|
2022-08-11 19:04:29 -05:00
|
|
|
struct list_head entries;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct cached_fid {
|
2022-10-06 00:14:31 -05:00
|
|
|
struct list_head entry;
|
|
|
|
struct cached_fids *cfids;
|
2022-08-31 12:49:44 +10:00
|
|
|
const char *path;
|
2022-08-11 19:04:29 -05:00
|
|
|
bool has_lease:1;
|
2022-10-06 00:14:31 -05:00
|
|
|
bool is_open:1;
|
|
|
|
bool on_list:1;
|
|
|
|
bool file_all_info_is_valid:1;
|
2022-08-11 19:04:29 -05:00
|
|
|
unsigned long time; /* jiffies of when lease was taken */
|
2025-07-24 22:23:53 -05:00
|
|
|
unsigned long last_access_time; /* jiffies of when last accessed */
|
2022-08-11 19:04:29 -05:00
|
|
|
struct kref refcount;
|
|
|
|
struct cifs_fid fid;
|
2022-10-06 00:14:31 -05:00
|
|
|
spinlock_t fid_lock;
|
2022-08-11 19:04:29 -05:00
|
|
|
struct cifs_tcon *tcon;
|
|
|
|
struct dentry *dentry;
|
smb: During unmount, ensure all cached dir instances drop their dentry
The unmount process (cifs_kill_sb() calling close_all_cached_dirs()) can
race with various cached directory operations, which ultimately results
in dentries not being dropped and these kernel BUGs:
BUG: Dentry ffff88814f37e358{i=1000000000080,n=/} still in use (2) [unmount of cifs cifs]
VFS: Busy inodes after unmount of cifs (cifs)
------------[ cut here ]------------
kernel BUG at fs/super.c:661!
This happens when a cfid is in the process of being cleaned up when, and
has been removed from the cfids->entries list, including:
- Receiving a lease break from the server
- Server reconnection triggers invalidate_all_cached_dirs(), which
removes all the cfids from the list
- The laundromat thread decides to expire an old cfid.
To solve these problems, dropping the dentry is done in queued work done
in a newly-added cfid_put_wq workqueue, and close_all_cached_dirs()
flushes that workqueue after it drops all the dentries of which it's
aware. This is a global workqueue (rather than scoped to a mount), but
the queued work is minimal.
The final cleanup work for cleaning up a cfid is performed via work
queued in the serverclose_wq workqueue; this is done separate from
dropping the dentries so that close_all_cached_dirs() doesn't block on
any server operations.
Both of these queued works expect to invoked with a cfid reference and
a tcon reference to avoid those objects from being freed while the work
is ongoing.
While we're here, add proper locking to close_all_cached_dirs(), and
locking around the freeing of cfid->dentry.
Fixes: ebe98f1447bb ("cifs: enable caching of directories for which a lease is held")
Cc: stable@vger.kernel.org
Signed-off-by: Paul Aurich <paul@darkrain42.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2024-11-18 13:50:28 -08:00
|
|
|
struct work_struct put_work;
|
|
|
|
struct work_struct close_work;
|
2022-08-11 19:04:29 -05:00
|
|
|
struct smb2_file_all_info file_all_info;
|
|
|
|
struct cached_dirents dirents;
|
|
|
|
};
|
|
|
|
|
2023-09-01 02:15:14 -05:00
|
|
|
/* default MAX_CACHED_FIDS is 16 */
|
2022-08-31 12:49:42 +10:00
|
|
|
struct cached_fids {
|
2022-10-06 00:14:31 -05:00
|
|
|
/* Must be held when:
|
|
|
|
* - accessing the cfids->entries list
|
smb: During unmount, ensure all cached dir instances drop their dentry
The unmount process (cifs_kill_sb() calling close_all_cached_dirs()) can
race with various cached directory operations, which ultimately results
in dentries not being dropped and these kernel BUGs:
BUG: Dentry ffff88814f37e358{i=1000000000080,n=/} still in use (2) [unmount of cifs cifs]
VFS: Busy inodes after unmount of cifs (cifs)
------------[ cut here ]------------
kernel BUG at fs/super.c:661!
This happens when a cfid is in the process of being cleaned up when, and
has been removed from the cfids->entries list, including:
- Receiving a lease break from the server
- Server reconnection triggers invalidate_all_cached_dirs(), which
removes all the cfids from the list
- The laundromat thread decides to expire an old cfid.
To solve these problems, dropping the dentry is done in queued work done
in a newly-added cfid_put_wq workqueue, and close_all_cached_dirs()
flushes that workqueue after it drops all the dentries of which it's
aware. This is a global workqueue (rather than scoped to a mount), but
the queued work is minimal.
The final cleanup work for cleaning up a cfid is performed via work
queued in the serverclose_wq workqueue; this is done separate from
dropping the dentries so that close_all_cached_dirs() doesn't block on
any server operations.
Both of these queued works expect to invoked with a cfid reference and
a tcon reference to avoid those objects from being freed while the work
is ongoing.
While we're here, add proper locking to close_all_cached_dirs(), and
locking around the freeing of cfid->dentry.
Fixes: ebe98f1447bb ("cifs: enable caching of directories for which a lease is held")
Cc: stable@vger.kernel.org
Signed-off-by: Paul Aurich <paul@darkrain42.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2024-11-18 13:50:28 -08:00
|
|
|
* - accessing the cfids->dying list
|
2022-10-06 00:14:31 -05:00
|
|
|
*/
|
|
|
|
spinlock_t cfid_list_lock;
|
|
|
|
int num_entries;
|
|
|
|
struct list_head entries;
|
smb: During unmount, ensure all cached dir instances drop their dentry
The unmount process (cifs_kill_sb() calling close_all_cached_dirs()) can
race with various cached directory operations, which ultimately results
in dentries not being dropped and these kernel BUGs:
BUG: Dentry ffff88814f37e358{i=1000000000080,n=/} still in use (2) [unmount of cifs cifs]
VFS: Busy inodes after unmount of cifs (cifs)
------------[ cut here ]------------
kernel BUG at fs/super.c:661!
This happens when a cfid is in the process of being cleaned up when, and
has been removed from the cfids->entries list, including:
- Receiving a lease break from the server
- Server reconnection triggers invalidate_all_cached_dirs(), which
removes all the cfids from the list
- The laundromat thread decides to expire an old cfid.
To solve these problems, dropping the dentry is done in queued work done
in a newly-added cfid_put_wq workqueue, and close_all_cached_dirs()
flushes that workqueue after it drops all the dentries of which it's
aware. This is a global workqueue (rather than scoped to a mount), but
the queued work is minimal.
The final cleanup work for cleaning up a cfid is performed via work
queued in the serverclose_wq workqueue; this is done separate from
dropping the dentries so that close_all_cached_dirs() doesn't block on
any server operations.
Both of these queued works expect to invoked with a cfid reference and
a tcon reference to avoid those objects from being freed while the work
is ongoing.
While we're here, add proper locking to close_all_cached_dirs(), and
locking around the freeing of cfid->dentry.
Fixes: ebe98f1447bb ("cifs: enable caching of directories for which a lease is held")
Cc: stable@vger.kernel.org
Signed-off-by: Paul Aurich <paul@darkrain42.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2024-11-18 13:50:28 -08:00
|
|
|
struct list_head dying;
|
|
|
|
struct work_struct invalidation_work;
|
2023-10-05 16:04:25 -03:00
|
|
|
struct delayed_work laundromat_work;
|
2022-08-31 12:49:42 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
extern struct cached_fids *init_cached_dirs(void);
|
|
|
|
extern void free_cached_dirs(struct cached_fids *cfids);
|
2022-08-10 22:00:08 -05:00
|
|
|
extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
|
|
|
|
const char *path,
|
|
|
|
struct cifs_sb_info *cifs_sb,
|
2022-08-11 19:51:18 -05:00
|
|
|
bool lookup_only, struct cached_fid **cfid);
|
2022-08-10 22:00:08 -05:00
|
|
|
extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
|
|
|
|
struct dentry *dentry,
|
|
|
|
struct cached_fid **cfid);
|
|
|
|
extern void close_cached_dir(struct cached_fid *cfid);
|
2022-10-18 17:39:10 +10:00
|
|
|
extern void drop_cached_dir_by_name(const unsigned int xid,
|
|
|
|
struct cifs_tcon *tcon,
|
|
|
|
const char *name,
|
|
|
|
struct cifs_sb_info *cifs_sb);
|
2022-08-10 22:00:08 -05:00
|
|
|
extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
|
|
|
|
extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
|
2025-07-01 00:19:32 +05:30
|
|
|
extern bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
|
2022-08-10 22:00:08 -05:00
|
|
|
|
|
|
|
#endif /* _CACHED_DIR_H */
|