From: Rishabh Bhatnagar <risbhat@amazon.com>
To: <gregkh@linuxfoundation.org>, <pc@cjr.nz>
Cc: <stable@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<linux-cifs@vger.kernel.org>,
Steve French <stfrench@microsoft.com>,
"Rishabh Bhatnagar" <risbhat@amazon.com>
Subject: [PATCH 5.4 5/5] cifs: Fix potential deadlock when updating vol in cifs_reconnect()
Date: Fri, 23 Jun 2023 21:34:06 +0000 [thread overview]
Message-ID: <20230623213406.5596-6-risbhat@amazon.com> (raw)
In-Reply-To: <20230623213406.5596-1-risbhat@amazon.com>
From: "Paulo Alcantara (SUSE)" <pc@cjr.nz>
commit 06d57378bcc9b2c33640945174842115593795d1 upstream.
We can't acquire volume lock while refreshing the DFS cache because
cifs_reconnect() may call dfs_cache_update_vol() while we are walking
through the volume list.
To prevent that, make vol_info refcounted, create a temp list with all
volumes eligible for refreshing, and then use it without any locks
held.
Besides, replace vol_lock with a spinlock and protect cache_ttl from
concurrent accesses or changes.
Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Rishabh Bhatnagar <risbhat@amazon.com>
---
fs/cifs/dfs_cache.c | 109 +++++++++++++++++++++++++++++++-------------
1 file changed, 77 insertions(+), 32 deletions(-)
diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c
index 1efba8ee86bf..cf9267cf42e7 100644
--- a/fs/cifs/dfs_cache.c
+++ b/fs/cifs/dfs_cache.c
@@ -49,15 +49,20 @@ struct cache_entry {
struct vol_info {
char *fullpath;
+ spinlock_t smb_vol_lock;
struct smb_vol smb_vol;
char *mntdata;
struct list_head list;
+ struct list_head rlist;
+ struct kref refcnt;
};
static struct kmem_cache *cache_slab __read_mostly;
static struct workqueue_struct *dfscache_wq __read_mostly;
static int cache_ttl;
+static DEFINE_SPINLOCK(cache_ttl_lock);
+
static struct nls_table *cache_nlsc;
/*
@@ -69,7 +74,7 @@ static struct hlist_head cache_htable[CACHE_HTABLE_SIZE];
static DEFINE_MUTEX(list_lock);
static LIST_HEAD(vol_list);
-static DEFINE_MUTEX(vol_lock);
+static DEFINE_SPINLOCK(vol_list_lock);
static void refresh_cache_worker(struct work_struct *work);
@@ -300,7 +305,6 @@ int dfs_cache_init(void)
for (i = 0; i < CACHE_HTABLE_SIZE; i++)
INIT_HLIST_HEAD(&cache_htable[i]);
- cache_ttl = -1;
cache_nlsc = load_nls_default();
cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__);
@@ -471,15 +475,15 @@ add_cache_entry(unsigned int hash, const char *path,
hlist_add_head_rcu(&ce->hlist, &cache_htable[hash]);
- mutex_lock(&vol_lock);
- if (cache_ttl < 0) {
+ spin_lock(&cache_ttl_lock);
+ if (!cache_ttl) {
cache_ttl = ce->ttl;
queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
} else {
cache_ttl = min_t(int, cache_ttl, ce->ttl);
mod_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
}
- mutex_unlock(&vol_lock);
+ spin_unlock(&cache_ttl_lock);
return ce;
}
@@ -523,21 +527,32 @@ static inline void destroy_slab_cache(void)
kmem_cache_destroy(cache_slab);
}
-static inline void free_vol(struct vol_info *vi)
+static void __vol_release(struct vol_info *vi)
{
- list_del(&vi->list);
kfree(vi->fullpath);
kfree(vi->mntdata);
cifs_cleanup_volume_info_contents(&vi->smb_vol);
kfree(vi);
}
+static void vol_release(struct kref *kref)
+{
+ struct vol_info *vi = container_of(kref, struct vol_info, refcnt);
+
+ spin_lock(&vol_list_lock);
+ list_del(&vi->list);
+ spin_unlock(&vol_list_lock);
+ __vol_release(vi);
+}
+
static inline void free_vol_list(void)
{
struct vol_info *vi, *nvi;
- list_for_each_entry_safe(vi, nvi, &vol_list, list)
- free_vol(vi);
+ list_for_each_entry_safe(vi, nvi, &vol_list, list) {
+ list_del_init(&vi->list);
+ __vol_release(vi);
+ }
}
/**
@@ -1156,10 +1171,13 @@ int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, const char *fullpath)
goto err_free_fullpath;
vi->mntdata = mntdata;
+ spin_lock_init(&vi->smb_vol_lock);
+ kref_init(&vi->refcnt);
- mutex_lock(&vol_lock);
+ spin_lock(&vol_list_lock);
list_add_tail(&vi->list, &vol_list);
- mutex_unlock(&vol_lock);
+ spin_unlock(&vol_list_lock);
+
return 0;
err_free_fullpath:
@@ -1169,7 +1187,8 @@ int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, const char *fullpath)
return rc;
}
-static inline struct vol_info *find_vol(const char *fullpath)
+/* Must be called with vol_list_lock held */
+static struct vol_info *find_vol(const char *fullpath)
{
struct vol_info *vi;
@@ -1191,7 +1210,6 @@ static inline struct vol_info *find_vol(const char *fullpath)
*/
int dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server)
{
- int rc;
struct vol_info *vi;
if (!fullpath || !server)
@@ -1199,22 +1217,24 @@ int dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server)
cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
- mutex_lock(&vol_lock);
-
+ spin_lock(&vol_list_lock);
vi = find_vol(fullpath);
if (IS_ERR(vi)) {
- rc = PTR_ERR(vi);
- goto out;
+ spin_unlock(&vol_list_lock);
+ return PTR_ERR(vi);
}
+ kref_get(&vi->refcnt);
+ spin_unlock(&vol_list_lock);
cifs_dbg(FYI, "%s: updating volume info\n", __func__);
+ spin_lock(&vi->smb_vol_lock);
memcpy(&vi->smb_vol.dstaddr, &server->dstaddr,
sizeof(vi->smb_vol.dstaddr));
- rc = 0;
+ spin_unlock(&vi->smb_vol_lock);
-out:
- mutex_unlock(&vol_lock);
- return rc;
+ kref_put(&vi->refcnt, vol_release);
+
+ return 0;
}
/**
@@ -1231,11 +1251,11 @@ void dfs_cache_del_vol(const char *fullpath)
cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
- mutex_lock(&vol_lock);
+ spin_lock(&vol_list_lock);
vi = find_vol(fullpath);
- if (!IS_ERR(vi))
- free_vol(vi);
- mutex_unlock(&vol_lock);
+ spin_unlock(&vol_list_lock);
+
+ kref_put(&vi->refcnt, vol_release);
}
/* Get all tcons that are within a DFS namespace and can be refreshed */
@@ -1449,27 +1469,52 @@ static void refresh_tcon(struct vol_info *vi, struct cifs_tcon *tcon)
*/
static void refresh_cache_worker(struct work_struct *work)
{
- struct vol_info *vi;
+ struct vol_info *vi, *nvi;
struct TCP_Server_Info *server;
- LIST_HEAD(list);
+ LIST_HEAD(vols);
+ LIST_HEAD(tcons);
struct cifs_tcon *tcon, *ntcon;
- mutex_lock(&vol_lock);
-
+ /*
+ * Find SMB volumes that are eligible (server->tcpStatus == CifsGood)
+ * for refreshing.
+ */
+ spin_lock(&vol_list_lock);
list_for_each_entry(vi, &vol_list, list) {
server = get_tcp_server(&vi->smb_vol);
if (!server)
continue;
- get_tcons(server, &list);
- list_for_each_entry_safe(tcon, ntcon, &list, ulist) {
+ kref_get(&vi->refcnt);
+ list_add_tail(&vi->rlist, &vols);
+ put_tcp_server(server);
+ }
+ spin_unlock(&vol_list_lock);
+
+ /* Walk through all TCONs and refresh any expired cache entry */
+ list_for_each_entry_safe(vi, nvi, &vols, rlist) {
+ spin_lock(&vi->smb_vol_lock);
+ server = get_tcp_server(&vi->smb_vol);
+ spin_unlock(&vi->smb_vol_lock);
+
+ if (!server)
+ goto next_vol;
+
+ get_tcons(server, &tcons);
+ list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) {
refresh_tcon(vi, tcon);
list_del_init(&tcon->ulist);
cifs_put_tcon(tcon);
}
put_tcp_server(server);
+
+next_vol:
+ list_del_init(&vi->rlist);
+ kref_put(&vi->refcnt, vol_release);
}
+
+ spin_lock(&cache_ttl_lock);
queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
- mutex_unlock(&vol_lock);
+ spin_unlock(&cache_ttl_lock);
}
--
2.40.1
next prev parent reply other threads:[~2023-06-23 21:35 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-06-23 21:34 [PATCH 5.4 0/5] CIFS DFS fixes for 5.4 Rishabh Bhatnagar
2023-06-23 21:34 ` [PATCH 5.4 1/5] cifs: Clean up DFS referral cache Rishabh Bhatnagar
2023-06-23 21:34 ` [PATCH 5.4 2/5] cifs: Get rid of kstrdup_const()'d paths Rishabh Bhatnagar
2023-06-25 15:38 ` David Laight
2023-06-25 16:58 ` Paulo Alcantara
2023-06-23 21:34 ` [PATCH 5.4 3/5] cifs: Introduce helpers for finding TCP connection Rishabh Bhatnagar
2023-06-26 6:04 ` Shyam Prasad N
2023-06-26 6:13 ` Greg KH
2023-06-26 6:30 ` Shyam Prasad N
2023-06-23 21:34 ` [PATCH 5.4 4/5] cifs: Merge is_path_valid() into get_normalized_path() Rishabh Bhatnagar
2023-06-23 21:34 ` Rishabh Bhatnagar [this message]
2023-06-23 22:08 ` [PATCH 5.4 0/5] CIFS DFS fixes for 5.4 Paulo Alcantara
2023-06-24 14:10 ` Greg KH
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230623213406.5596-6-risbhat@amazon.com \
--to=risbhat@amazon.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-cifs@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=pc@cjr.nz \
--cc=stable@vger.kernel.org \
--cc=stfrench@microsoft.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.