[PATCH] fscache: Make fscache_acquire_volume() return errors

David Howells dhowells at redhat.com
Mon Dec 6 04:13:35 PST 2021


Make fscache_acquire_volume() return -EBUSY if there's already an acquired
volume cookie with the requested volume_key and make it return -ENOMEM on
allocation failure.  It can still return a NULL cookie if caching is
disabled.
---

 Documentation/filesystems/caching/netfs-api.rst |    9 ++++---
 fs/9p/cache.c                                   |   21 +++++++++++++----
 fs/9p/cache.h                                   |    2 +-
 fs/9p/v9fs.c                                    |    7 ++++--
 fs/afs/internal.h                               |    2 +-
 fs/afs/volume.c                                 |   20 +++++++++++++---
 fs/cifs/connect.c                               |    4 ++-
 fs/cifs/fscache.c                               |   28 +++++++++++++++++------
 fs/cifs/fscache.h                               |    4 ++-
 fs/fscache/volume.c                             |   11 ++++-----
 fs/nfs/fscache.c                                |   25 +++++++++++++++------
 fs/nfs/fscache.h                                |    2 +-
 fs/nfs/super.c                                  |   21 ++++++++++-------
 include/linux/fscache.h                         |    4 +++
 14 files changed, 110 insertions(+), 50 deletions(-)

diff --git a/Documentation/filesystems/caching/netfs-api.rst b/Documentation/filesystems/caching/netfs-api.rst
index 860e5f6d43dc..42bc3b537d68 100644
--- a/Documentation/filesystems/caching/netfs-api.rst
+++ b/Documentation/filesystems/caching/netfs-api.rst
@@ -115,10 +115,11 @@ it will use the first cache that comes to hand and set the name to that.
 The specified coherency data is stored in the cookie and will be matched
 against coherency data stored on disk.
 
-This function never returns an error, though it may return a NULL volume cookie
-on allocation failure or if fscache is not enabled.  It is safe to pass such a
-NULL value to any function that takes a volume cookie.  This will cause the
-function to do nothing.
+This function can return errors such as EBUSY if the volume key is already in
+use by an acquired volume or ENOMEM if an allocation failure occured.  It may
+also return a NULL volume cookie if fscache is not enabled.  It is safe to
+pass a NULL cookie to any function that takes a volume cookie.  This will
+cause that function to do nothing.
 
 
 When the network filesystem has finished with a volume, it should relinquish it
diff --git a/fs/9p/cache.c b/fs/9p/cache.c
index 99662920699d..dc51779fd9eb 100644
--- a/fs/9p/cache.c
+++ b/fs/9p/cache.c
@@ -16,24 +16,35 @@
 #include "v9fs.h"
 #include "cache.h"
 
-void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses,
-				   const char *dev_name)
+int v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses,
+				  const char *dev_name)
 {
+	struct fscache_volume *vcookie;
 	char *name, *p;
 
 	name = kasprintf(GFP_KERNEL, "9p,%s,%s",
 			 dev_name, v9ses->cachetag ?: v9ses->aname);
 	if (!name)
-		return;
+		return -ENOMEM;
 
 	for (p = name; *p; p++)
 		if (*p == '/')
 			*p = ';';
 
-	v9ses->fscache = fscache_acquire_volume(name, NULL, 0);
+	vcookie = fscache_acquire_volume(name, NULL, 0);
 	p9_debug(P9_DEBUG_FSC, "session %p get volume %p (%s)\n",
-		 v9ses, v9ses->fscache, name);
+		 v9ses, vcookie, name);
+	if (IS_ERR(vcookie)) {
+		if (vcookie != ERR_PTR(-EBUSY)) {
+			kfree(name);
+			return PTR_ERR(vcookie);
+		}
+		pr_err("Cache volume key already in use (%s)\n", name);
+		vcookie = NULL;
+	}
+	v9ses->fscache = vcookie;
 	kfree(name);
+	return 0;
 }
 
 void v9fs_cache_inode_get_cookie(struct inode *inode)
diff --git a/fs/9p/cache.h b/fs/9p/cache.h
index dd791194bb6f..1923affcdc62 100644
--- a/fs/9p/cache.h
+++ b/fs/9p/cache.h
@@ -12,7 +12,7 @@
 
 #ifdef CONFIG_9P_FSCACHE
 
-extern void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses,
+extern int v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses,
 					  const char *dev_name);
 
 extern void v9fs_cache_inode_get_cookie(struct inode *inode);
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c
index b2d196f29f42..5ea91ab3cfe2 100644
--- a/fs/9p/v9fs.c
+++ b/fs/9p/v9fs.c
@@ -469,8 +469,11 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
 
 #ifdef CONFIG_9P_FSCACHE
 	/* register the session for caching */
-	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
-		v9fs_cache_session_get_cookie(v9ses, dev_name);
+	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
+		rc = v9fs_cache_session_get_cookie(v9ses, dev_name);
+		if (rc < 0)
+			goto err_clnt;
+	}
 #endif
 	spin_lock(&v9fs_sessionlist_lock);
 	list_add(&v9ses->slist, &v9fs_sessionlist);
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 0dd15de57be9..b6f02321fc09 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -1511,7 +1511,7 @@ extern struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *,
  * volume.c
  */
 extern struct afs_volume *afs_create_volume(struct afs_fs_context *);
-extern void afs_activate_volume(struct afs_volume *);
+extern int afs_activate_volume(struct afs_volume *);
 extern void afs_deactivate_volume(struct afs_volume *);
 extern struct afs_volume *afs_get_volume(struct afs_volume *, enum afs_volume_trace);
 extern void afs_put_volume(struct afs_net *, struct afs_volume *, enum afs_volume_trace);
diff --git a/fs/afs/volume.c b/fs/afs/volume.c
index 1269ec08170e..f906d38c7f17 100644
--- a/fs/afs/volume.c
+++ b/fs/afs/volume.c
@@ -268,18 +268,30 @@ void afs_put_volume(struct afs_net *net, struct afs_volume *volume,
 /*
  * Activate a volume.
  */
-void afs_activate_volume(struct afs_volume *volume)
+int afs_activate_volume(struct afs_volume *volume)
 {
 #ifdef CONFIG_AFS_FSCACHE
+	struct fscache_volume *vcookie;
 	char *name;
 
 	name = kasprintf(GFP_KERNEL, "afs,%s,%llx",
 			 volume->cell->name, volume->vid);
-	if (name) {
-		volume->cache = fscache_acquire_volume(name, NULL, 0);
-		kfree(name);
+	if (!name)
+		return -ENOMEM;
+
+	vcookie = fscache_acquire_volume(name, NULL, 0);
+	if (IS_ERR(vcookie)) {
+		if (vcookie != ERR_PTR(-EBUSY)) {
+			kfree(name);
+			return PTR_ERR(vcookie);
+		}
+		pr_err("AFS: Cache volume key already in use (%s)\n", name);
+		vcookie = NULL;
 	}
+	volume->cache = vcookie;
+	kfree(name);
 #endif
+	return 0;
 }
 
 /*
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index b84ce4d7e84e..39c413edb712 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -3043,7 +3043,9 @@ static int mount_get_conns(struct mount_ctx *mnt_ctx)
 		 * Inside cifs_fscache_get_super_cookie it checks
 		 * that we do not get super cookie twice.
 		 */
-		cifs_fscache_get_super_cookie(tcon);
+		rc = cifs_fscache_get_super_cookie(tcon);
+		if (rc < 0)
+			goto out;
 	}
 
 	/*
diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c
index f030ad2abd52..bf137443a082 100644
--- a/fs/cifs/fscache.c
+++ b/fs/cifs/fscache.c
@@ -12,14 +12,16 @@
 #include "cifs_fs_sb.h"
 #include "cifsproto.h"
 
-void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
+int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
 {
 	struct cifs_fscache_super_auxdata auxdata;
 	struct TCP_Server_Info *server = tcon->ses->server;
+	struct fscache_volume *vcookie;
 	const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr;
 	size_t slen, i;
 	char *sharename;
 	char *key;
+	int ret = -ENOMEM;
 
 	tcon->fscache = NULL;
 	switch (sa->sa_family) {
@@ -28,13 +30,13 @@ void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
 		break;
 	default:
 		cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family);
-		return;
+		return -EINVAL;
 	}
 
 	sharename = extract_sharename(tcon->treeName);
 	if (IS_ERR(sharename)) {
 		cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__);
-		return;
+		return -EINVAL;
 	}
 
 	slen = strlen(sharename);
@@ -52,14 +54,26 @@ void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
 	auxdata.vol_serial_number = tcon->vol_serial_number;
 	// TODO: Do something with the volume coherency data
 
-	tcon->fscache = fscache_acquire_volume(key,
-					       NULL, /* preferred_cache */
-					       0 /* coherency_data */);
-	cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, tcon->fscache);
+	vcookie = fscache_acquire_volume(key,
+					 NULL, /* preferred_cache */
+					 0 /* coherency_data */);
+	cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie);
+	if (IS_ERR(vcookie)) {
+		if (vcookie != ERR_PTR(-EBUSY)) {
+			ret = PTR_ERR(vcookie);
+			goto out_2;
+		}
+		pr_err("Cache volume key already in use (%s)\n", key);
+		vcookie = NULL;
+	}
 
+	tcon->fscache = vcookie;
+	ret = 0;
+out_2:
 	kfree(key);
 out:
 	kfree(sharename);
+	return ret;
 }
 
 void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon)
diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h
index a3e300cdd133..cf4b04317149 100644
--- a/fs/cifs/fscache.h
+++ b/fs/cifs/fscache.h
@@ -37,7 +37,7 @@ struct cifs_fscache_inode_auxdata {
 /*
  * fscache.c
  */
-extern void cifs_fscache_get_super_cookie(struct cifs_tcon *);
+extern int cifs_fscache_get_super_cookie(struct cifs_tcon *);
 extern void cifs_fscache_release_super_cookie(struct cifs_tcon *);
 
 extern void cifs_fscache_get_inode_cookie(struct inode *);
@@ -105,7 +105,7 @@ void cifs_fscache_fill_auxdata(struct inode *inode,
 {
 }
 
-static inline void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) {}
+static inline int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) { return 0; }
 static inline void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) {}
 
 static inline void cifs_fscache_get_inode_cookie(struct inode *inode) {}
diff --git a/fs/fscache/volume.c b/fs/fscache/volume.c
index 10f741c2072f..edd3c245010e 100644
--- a/fs/fscache/volume.c
+++ b/fs/fscache/volume.c
@@ -158,7 +158,7 @@ static void fscache_wait_on_volume_collision(struct fscache_volume *candidate,
  * wait for the old volume to complete if it's being relinquished and an error
  * otherwise.
  */
-static struct fscache_volume *fscache_hash_volume(struct fscache_volume *candidate)
+static bool fscache_hash_volume(struct fscache_volume *candidate)
 {
 	struct fscache_volume *cursor;
 	struct hlist_bl_head *h;
@@ -186,13 +186,12 @@ static struct fscache_volume *fscache_hash_volume(struct fscache_volume *candida
 
 	if (test_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &candidate->flags))
 		fscache_wait_on_volume_collision(candidate, collidee_debug_id);
-	return candidate;
+	return true;
 
 collision:
 	fscache_see_volume(cursor, fscache_volume_collision);
-	pr_err("Cache volume already in use\n");
 	hlist_bl_unlock(h);
-	return NULL;
+	return false;
 }
 
 /*
@@ -318,11 +317,11 @@ struct fscache_volume *__fscache_acquire_volume(const char *volume_key,
 
 	volume = fscache_alloc_volume(volume_key, cache_name, coherency_data);
 	if (!volume)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 	if (!fscache_hash_volume(volume)) {
 		fscache_put_volume(volume, fscache_volume_put_hash_collision);
-		return NULL;
+		return ERR_PTR(-EBUSY);
 	}
 
 	fscache_create_volume(volume, false);
diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c
index b72b925beac2..87ba3f939630 100644
--- a/fs/nfs/fscache.c
+++ b/fs/nfs/fscache.c
@@ -83,8 +83,9 @@ static bool nfs_fscache_get_client_key(struct nfs_client *clp,
  * either by the 'fsc=xxx' option to mount, or by inheriting it from the parent
  * superblock across an automount point of some nature.
  */
-void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, int ulen)
+int nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, int ulen)
 {
+	struct fscache_volume *vcookie;
 	struct nfs_server *nfss = NFS_SB(sb);
 	unsigned int len = 3;
 	char *key;
@@ -92,12 +93,12 @@ void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, int
 	if (uniq) {
 		nfss->fscache_uniq = kmemdup_nul(uniq, ulen, GFP_KERNEL);
 		if (!nfss->fscache_uniq)
-			return;
+			return -ENOMEM;
 	}
 
 	key = kmalloc(NFS_MAX_KEY_LEN + 24, GFP_KERNEL);
 	if (!key)
-		return;
+		return -ENOMEM;
 
 	memcpy(key, "nfs", 3);
 	if (!nfs_fscache_get_client_key(nfss->nfs_client, key, &len) ||
@@ -124,14 +125,24 @@ void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, int
 	key[len] = 0;
 
 	/* create a cache index for looking up filehandles */
-	nfss->fscache = fscache_acquire_volume(key,
-					       NULL, /* preferred_cache */
-					       0 /* coherency_data */);
+	vcookie = fscache_acquire_volume(key,
+					 NULL, /* preferred_cache */
+					 0 /* coherency_data */);
 	dfprintk(FSCACHE, "NFS: get superblock cookie (0x%p/0x%p)\n",
-		 nfss, nfss->fscache);
+		 nfss, vcookie);
+	if (IS_ERR(vcookie)) {
+		if (vcookie != ERR_PTR(-EBUSY)) {
+			kfree(key);
+			return PTR_ERR(vcookie);
+		}
+		pr_err("NFS: Cache volume key already in use (%s)\n", key);
+		vcookie = NULL;
+	}
+	nfss->fscache = vcookie;
 
 out:
 	kfree(key);
+	return 0;
 }
 
 /*
diff --git a/fs/nfs/fscache.h b/fs/nfs/fscache.h
index 240ea87788de..0067227de1fa 100644
--- a/fs/nfs/fscache.h
+++ b/fs/nfs/fscache.h
@@ -36,7 +36,7 @@ struct nfs_fscache_inode_auxdata {
 /*
  * fscache.c
  */
-extern void nfs_fscache_get_super_cookie(struct super_block *, const char *, int);
+extern int nfs_fscache_get_super_cookie(struct super_block *, const char *, int);
 extern void nfs_fscache_release_super_cookie(struct super_block *);
 
 extern void nfs_fscache_init_inode(struct inode *);
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index c96700f4b8a6..6ab5eeb000dc 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1204,8 +1204,8 @@ static int nfs_compare_super(struct super_block *sb, struct fs_context *fc)
 }
 
 #ifdef CONFIG_NFS_FSCACHE
-static void nfs_get_cache_cookie(struct super_block *sb,
-				 struct nfs_fs_context *ctx)
+static int nfs_get_cache_cookie(struct super_block *sb,
+				struct nfs_fs_context *ctx)
 {
 	struct nfs_server *nfss = NFS_SB(sb);
 	char *uniq = NULL;
@@ -1214,31 +1214,32 @@ static void nfs_get_cache_cookie(struct super_block *sb,
 	nfss->fscache = NULL;
 
 	if (!ctx)
-		return;
+		return 0;
 
 	if (ctx->clone_data.sb) {
 		struct nfs_server *mnt_s = NFS_SB(ctx->clone_data.sb);
 		if (!(mnt_s->options & NFS_OPTION_FSCACHE))
-			return;
+			return 0;
 		if (mnt_s->fscache_uniq) {
 			uniq = mnt_s->fscache_uniq;
 			ulen = strlen(uniq);
 		}
 	} else {
 		if (!(ctx->options & NFS_OPTION_FSCACHE))
-			return;
+			return 0;
 		if (ctx->fscache_uniq) {
 			uniq = ctx->fscache_uniq;
 			ulen = strlen(ctx->fscache_uniq);
 		}
 	}
 
-	nfs_fscache_get_super_cookie(sb, uniq, ulen);
+	return nfs_fscache_get_super_cookie(sb, uniq, ulen);
 }
 #else
-static void nfs_get_cache_cookie(struct super_block *sb,
-				 struct nfs_fs_context *ctx)
+static int nfs_get_cache_cookie(struct super_block *sb,
+				struct nfs_fs_context *ctx)
 {
+	return 0;
 }
 #endif
 
@@ -1298,7 +1299,9 @@ int nfs_get_tree_common(struct fs_context *fc)
 			s->s_blocksize_bits = bsize;
 			s->s_blocksize = 1U << bsize;
 		}
-		nfs_get_cache_cookie(s, ctx);
+		error = nfs_get_cache_cookie(s, ctx);
+		if (error < 0)
+			goto error_splat_super;
 	}
 
 	error = nfs_get_root(s, fc);
diff --git a/include/linux/fscache.h b/include/linux/fscache.h
index 364be32f7217..4dd13c3b87df 100644
--- a/include/linux/fscache.h
+++ b/include/linux/fscache.h
@@ -181,6 +181,10 @@ extern void __fscache_clear_page_bits(struct address_space *, loff_t, size_t);
  * caller must provide an identifier for the volume and may also indicate which
  * cache it should be in.  If a preexisting volume entry is found in the cache,
  * the coherency data must match otherwise the entry will be invalidated.
+ *
+ * Returns a cookie pointer on success, -ENOMEM if out of memory or -EBUSY if a
+ * cache volume of that name is already acquired.  Note that "NULL" is a valid
+ * cookie pointer and can be returned if caching is refused.
  */
 static inline
 struct fscache_volume *fscache_acquire_volume(const char *volume_key,





More information about the linux-afs mailing list