All of lore.kernel.org
 help / color / mirror / Atom feed
From: juncheng bai <baijuncheng@unitedstack.com>
To: unlisted-recipients:; (no To-header on input)
Cc: ericvh@gmail.com, viro@zeniv.linux.org.uk,
	dominique.martinet@cea.fr, kirill.shutemov@linux.intel.com,
	dhowells@redhat.com, linux-fsdevel@vger.kernel.org,
	v9fs-developer@lists.sourceforge.net
Subject: [PATCH RFC v2] fs/9p: add new feature of xattr cache
Date: Mon, 01 Jun 2015 18:22:25 +0800	[thread overview]
Message-ID: <556C3261.2030308@unitedstack.com> (raw)

Hi, All.

Recently, I check the code many times and test it. I find two bug, the
first one: In function __v9fs_xattr_get_cached, can't clear flag of the
xattr_item if need to query again; the second one, in the same function,
if buffer_size is 0, miss handle if the flag of xattr_item is enotsupp.

And I divide the flag of xattr_item into two classes: the xattr item
data flags and xattr item flags. The flag of xattr item data is 
exclusion, the flag of xattr item is shared.

changes since v1:
- fix bug: in the function __v9fs_xattr_get_cached,if buffer_size is 0,
miss handling if the flag of xattr_item is enotsupp

changes since RFC:
- fix bug: in the function __v9fs_xattr_get_cached, can't clear flag of
the xattr_item if need to query again
- redesign the flag of xattr_item, divide the flag of xattr_item into
two classes

Please review, any input welcome.

----------------------------------------------------------------------

 From e3a0c4749f0f59c5c04e412d52946072db5830f2 Mon Sep 17 00:00:00 2001
From: juncheng bai <baijuncheng@unitedstack.com>
Date: Mon, 1 Jun 2015 17:28:35 +0800
Subject: [PATCH RFC v2] fs/9p: add feature of xattr cache

first: this can be optimized with the application of extended attributes
especially, read more than writing.
second: if specify mount option:fscache or loose, the vfs layer will
search the extended attribute:security.capacility at every time to write

through the mount option:xattrcache, we can be flexible to enable this
feature
In this implementation, multipath applications can read and write multiple
extended attributes at the same time; More than one applications can read
the same extended attribute.

Signed-off-by: juncheng bai <baijuncheng@unitedstack.com>
---
  Documentation/filesystems/9p.txt |   3 +
  fs/9p/Makefile                   |   1 +
  fs/9p/v9fs.c                     |  38 ++++++-
  fs/9p/v9fs.h                     |  15 +++
  fs/9p/v9fs_vfs.h                 |   1 +
  fs/9p/vfs_inode.c                |   8 ++
  fs/9p/xattr.c                    | 214 
++++++++++++++++++++++++++++++++++--
  fs/9p/xattr.h                    |   2 +
  fs/9p/xattr_cache.c              | 228 
+++++++++++++++++++++++++++++++++++++++
  fs/9p/xattr_cache.h              | 100 +++++++++++++++++
  include/net/9p/9p.h              |   1 +
  11 files changed, 602 insertions(+), 9 deletions(-)
  create mode 100644 fs/9p/xattr_cache.c
  create mode 100644 fs/9p/xattr_cache.h

diff --git a/Documentation/filesystems/9p.txt 
b/Documentation/filesystems/9p.txt
index fec7144..0d6173b 100644
--- a/Documentation/filesystems/9p.txt
+++ b/Documentation/filesystems/9p.txt
@@ -91,6 +91,7 @@ OPTIONS
  			0x200 = display Fid debug
  			0x400 = display packet debug
  			0x800 = display fscache tracing debug
+			0x1000 = display xattrcache tracing debug

    rfdno=n	the file descriptor for reading with trans=fd

@@ -133,6 +134,8 @@ OPTIONS
  		cache tags for existing cache sessions can be listed at
  		/sys/fs/9p/caches. (applies only to cache=fscache)

+  xattrcache	enable extended attributes cache
+
  RESOURCES
  =========

diff --git a/fs/9p/Makefile b/fs/9p/Makefile
index ff7be98..68ee80c 100644
--- a/fs/9p/Makefile
+++ b/fs/9p/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_9P_FS) := 9p.o
  	v9fs.o \
  	fid.o  \
  	xattr.o \
+	xattr_cache.o \
  	xattr_user.o \
  	xattr_trusted.o

diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c
index 620d934..94d2fce 100644
--- a/fs/9p/v9fs.c
+++ b/fs/9p/v9fs.c
@@ -38,10 +38,12 @@
  #include "v9fs.h"
  #include "v9fs_vfs.h"
  #include "cache.h"
+#include "xattr_cache.h"

  static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
  static LIST_HEAD(v9fs_sessionlist);
  struct kmem_cache *v9fs_inode_cache;
+struct kmem_cache *v9fs_xattr_cache;

  /*
   * Option Parsing (code inspired by NFS code)
@@ -59,6 +61,8 @@ enum {
  	Opt_cache_loose, Opt_fscache, Opt_mmap,
  	/* Access options */
  	Opt_access, Opt_posixacl,
+	/* xattr cache */
+	Opt_xcache,
  	/* Error token */
  	Opt_err
  };
@@ -78,6 +82,7 @@ static const match_table_t tokens = {
  	{Opt_cachetag, "cachetag=%s"},
  	{Opt_access, "access=%s"},
  	{Opt_posixacl, "posixacl"},
+	{Opt_xcache, "xattrcache"},
  	{Opt_err, NULL}
  };

@@ -126,6 +131,7 @@ static int v9fs_parse_options(struct 
v9fs_session_info *v9ses, char *opts)
  #ifdef CONFIG_9P_FSCACHE
  	v9ses->cachetag = NULL;
  #endif
+	v9ses->xcache = 0;

  	if (!opts)
  		return 0;
@@ -297,7 +303,9 @@ static int v9fs_parse_options(struct 
v9fs_session_info *v9ses, char *opts)
  				 "Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n");
  #endif
  			break;
-
+		case Opt_xcache:
+			v9ses->xcache = 1;
+			break;
  		default:
  			continue;
  		}
@@ -589,6 +597,17 @@ static int v9fs_init_inode_cache(void)
  	return 0;
  }

+static int v9fs_init_xattr_cache(void)
+{
+	v9fs_xattr_cache = kmem_cache_create("v9fs_xattr_cache",
+					sizeof(struct xattr_item),
+					0, 0, NULL);
+	if (!v9fs_xattr_cache)
+		return -ENOMEM;
+
+	return 0;
+}
+
  /**
   * v9fs_destroy_inode_cache - destroy the cache of 9P inode
   *
@@ -603,16 +622,30 @@ static void v9fs_destroy_inode_cache(void)
  	kmem_cache_destroy(v9fs_inode_cache);
  }

+static void v9fs_destroy_xattr_cache(void)
+{
+	kmem_cache_destroy(v9fs_xattr_cache);
+}
+
  static int v9fs_cache_register(void)
  {
  	int ret;
  	ret = v9fs_init_inode_cache();
  	if (ret < 0)
  		return ret;
+
+	ret = v9fs_init_xattr_cache();
+	if (ret < 0) {
+		v9fs_destroy_inode_cache();
+		return ret;
+	}
+
  #ifdef CONFIG_9P_FSCACHE
  	ret = fscache_register_netfs(&v9fs_cache_netfs);
-	if (ret < 0)
+	if (ret < 0) {
  		v9fs_destroy_inode_cache();
+		v9fs_destroy_xattr_cache();
+	}
  #endif
  	return ret;
  }
@@ -620,6 +653,7 @@ static int v9fs_cache_register(void)
  static void v9fs_cache_unregister(void)
  {
  	v9fs_destroy_inode_cache();
+	v9fs_destroy_xattr_cache();
  #ifdef CONFIG_9P_FSCACHE
  	fscache_unregister_netfs(&v9fs_cache_netfs);
  #endif
diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h
index fb9ffcb..58ac5d8 100644
--- a/fs/9p/v9fs.h
+++ b/fs/9p/v9fs.h
@@ -101,6 +101,7 @@ struct v9fs_session_info {
  	unsigned short debug;
  	unsigned int afid;
  	unsigned int cache;
+	unsigned short xcache;
  #ifdef CONFIG_9P_FSCACHE
  	char *cachetag;
  	struct fscache_cookie *fscache;
@@ -121,6 +122,15 @@ struct v9fs_session_info {
  /* cache_validity flags */
  #define V9FS_INO_INVALID_ATTR 0x01

+/**
+ * @xal_head: fast path, the xattr has beed queried from backend
+ * @xaf_head: slow path, the xattr is looking up from backend
+ *
+ * Ensure implementation query request as soon as possible from xal_head
+ *
+ * Return from query, if there is not error, except -ENOTSUPP and -ERANGE,
+ * move xattr_item from xaf_head to xal_head, or set  V9FS_XI_Removing
+ */
  struct v9fs_inode {
  #ifdef CONFIG_9P_FSCACHE
  	spinlock_t fscache_lock;
@@ -131,6 +141,11 @@ struct v9fs_inode {
  	struct p9_fid *writeback_fid;
  	struct mutex v_mutex;
  	struct inode vfs_inode;
+
+	rwlock_t xal_lock;
+	struct list_head xal_head;
+	spinlock_t xaf_lock;
+	struct list_head xaf_head;
  };

  static inline struct v9fs_inode *V9FS_I(const struct inode *inode)
diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h
index 5a0db6d..41bcd2d 100644
--- a/fs/9p/v9fs_vfs.h
+++ b/fs/9p/v9fs_vfs.h
@@ -53,6 +53,7 @@ extern const struct file_operations 
v9fs_cached_file_operations_dotl;
  extern const struct file_operations v9fs_mmap_file_operations;
  extern const struct file_operations v9fs_mmap_file_operations_dotl;
  extern struct kmem_cache *v9fs_inode_cache;
+extern struct kmem_cache *v9fs_xattr_cache;

  struct inode *v9fs_alloc_inode(struct super_block *sb);
  void v9fs_destroy_inode(struct inode *inode);
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 39eb2bc..718dd45 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -249,6 +249,12 @@ struct inode *v9fs_alloc_inode(struct super_block *sb)
  	v9inode->writeback_fid = NULL;
  	v9inode->cache_validity = 0;
  	mutex_init(&v9inode->v_mutex);
+
+	rwlock_init(&v9inode->xal_lock);
+	INIT_LIST_HEAD(&v9inode->xal_head);
+	spin_lock_init(&v9inode->xaf_lock);
+	INIT_LIST_HEAD(&v9inode->xaf_head);
+
  	return &v9inode->vfs_inode;
  }

@@ -260,6 +266,8 @@ struct inode *v9fs_alloc_inode(struct super_block *sb)
  static void v9fs_i_callback(struct rcu_head *head)
  {
  	struct inode *inode = container_of(head, struct inode, i_rcu);
+
+	v9fs_free_inode_xattr_items(inode);
  	kmem_cache_free(v9fs_inode_cache, V9FS_I(inode));
  }

diff --git a/fs/9p/xattr.c b/fs/9p/xattr.c
index 0cf44b6..a11d935 100644
--- a/fs/9p/xattr.c
+++ b/fs/9p/xattr.c
@@ -19,8 +19,10 @@
  #include <net/9p/9p.h>
  #include <net/9p/client.h>

+#include "v9fs.h"
  #include "fid.h"
  #include "xattr.h"
+#include "xattr_cache.h"

  ssize_t v9fs_fid_xattr_get(struct p9_fid *fid, const char *name,
  			   void *buffer, size_t buffer_size)
@@ -56,6 +58,142 @@ ssize_t v9fs_fid_xattr_get(struct p9_fid *fid, const 
char *name,
  	return retval;
  }

+static ssize_t __v9fs_xattr_get(struct dentry *dentry, const char *name,
+				void *buffer, size_t buffer_size)
+{
+	struct p9_fid *fid;
+
+	fid = v9fs_fid_lookup(dentry);
+	if (IS_ERR(fid))
+		return PTR_ERR(fid);
+
+	return v9fs_fid_xattr_get(fid, name, buffer, buffer_size);
+}
+
+static ssize_t __v9fs_xattr_get_cached(struct dentry *dentry, const 
char *name,
+				void *buffer, size_t buffer_size)
+{
+	ssize_t ret = 0;
+	unsigned long old_flags = V9FS_XI_FLAGS_INIT;
+	struct xattr_item *xi;
+
+	xi = v9fs_find_cached_xattr_item(dentry, name);
+	if (!xi) {
+		xi = v9fs_find_or_create_cached_xattr_item_slow(dentry, name);
+		if (IS_ERR(xi)) {
+			p9_debug(P9_DEBUG_ERROR,
+				"create fail, dentry:%s xname:%s errno:%ld\n",
+				dentry->d_name.name, name, PTR_ERR(xi));
+
+			return __v9fs_xattr_get(dentry, name,
+							buffer, buffer_size);
+		}
+	}
+
+	/*
+	 * We don't know whether the xattr is removed succes
+	 * don't wait, handled by backend
+	 */
+	if (v9fs_xif_test_removing(xi)) {
+		v9fs_xattr_item_dec_ref(dentry, xi);
+		return __v9fs_xattr_get(dentry, name, buffer, buffer_size);
+	}
+
+	if (v9fs_xif_test_clear_new(xi)) {
+again:
+		down_write(&xi->rw_sem);
+
+		if (old_flags == v9fs_xif_current_dflag(xi)) {
+			ret = __v9fs_xattr_get(dentry, name, buffer, buffer_size);
+			v9fs_xattr_item_setval(xi, buffer, buffer_size, ret);
+			if (likely(!v9fs_xif_test_removing(xi) &&
+						v9fs_xif_test_slowpath(xi)))
+				v9fs_move_xattr_item_to_fast(dentry, xi);
+
+			up_write(&xi->rw_sem);
+			v9fs_xattr_item_dec_ref(dentry, xi);
+
+			return ret;
+		} else {
+			/*
+			 * the other process has re-fill in the data
+			 * do nothing, read from cache again
+			 */
+			up_write(&xi->rw_sem);
+		}
+	}
+
+	down_read(&xi->rw_sem);
+	/* check whether the xattr name is exist */
+	if (buffer_size == 0) {
+		/*
+		 * Returns the actual length of the attribute value
+		 */
+		if (v9fs_xif_test_erange(xi)) {
+			old_flags = v9fs_xif_test_nodata(xi);
+			up_read(&xi->rw_sem);
+			goto again;
+		} else if (v9fs_xif_test_normal(xi) ||
+				v9fs_xif_test_nodata(xi))
+			ret = xi->xi_vlen;
+		else if (v9fs_xif_test_enotsupp(xi))
+			ret = -EOPNOTSUPP;
+
+		goto exit;
+	}
+
+	if (v9fs_xif_test_normal(xi)) {
+		if (xi->xi_vlen > buffer_size) {
+			ret = -ERANGE;
+			goto exit;
+		} else {
+			strcpy(buffer, xi->xi_value);
+			ret = xi->xi_vlen;
+			goto exit;
+		}
+	}
+
+	if (v9fs_xif_test_enotsupp(xi)) {
+		ret = -EOPNOTSUPP;
+		goto exit;
+	}
+
+	if (v9fs_xif_test_nodata(xi)) {
+		if (xi->xi_vlen > buffer_size) {
+			ret = -ERANGE;
+			goto exit;
+		} else {
+			old_flags = v9fs_xif_test_nodata(xi);
+			up_read(&xi->rw_sem);
+			goto again;
+		}
+	}
+
+	if (v9fs_xif_test_erange(xi)) {
+		if (xi->xi_vlen >= buffer_size) {
+			ret = -ERANGE;
+			goto exit;
+		} else {
+			old_flags = v9fs_xif_test_erange(xi);
+			up_read(&xi->rw_sem);
+			goto again;
+		}
+	}
+
+	if (v9fs_xif_test_removing(xi)) {
+		ret = -ENODATA;
+		goto exit;
+	}
+
+	ret = -EINVAL;
+	BUG_ON(1);
+
+exit:
+	up_read(&xi->rw_sem);
+	v9fs_xattr_item_dec_ref(dentry, xi);
+
+	return ret;
+}

  /*
   * v9fs_xattr_get()
@@ -70,15 +208,74 @@ ssize_t v9fs_fid_xattr_get(struct p9_fid *fid, 
const char *name,
  ssize_t v9fs_xattr_get(struct dentry *dentry, const char *name,
  		       void *buffer, size_t buffer_size)
  {
-	struct p9_fid *fid;
+	struct v9fs_session_info *v9ses;

  	p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu\n",
  		 name, buffer_size);
-	fid = v9fs_fid_lookup(dentry);
+
+	v9ses = v9fs_dentry2v9ses(dentry);
+	/* when listxattr, the @name is NULL*/
+	if (v9ses->xcache && name)
+		return __v9fs_xattr_get_cached(dentry, name,
+								buffer, buffer_size);
+	else
+		return __v9fs_xattr_get(dentry, name, buffer, buffer_size);
+}
+
+static int __v9fs_xattr_set(struct dentry *dentry, const char *name,
+			const void *value, size_t value_len, int flags)
+{
+	struct p9_fid *fid = v9fs_fid_lookup(dentry);
  	if (IS_ERR(fid))
  		return PTR_ERR(fid);
+	return v9fs_fid_xattr_set(fid, name, value, value_len, flags);
+}

-	return v9fs_fid_xattr_get(fid, name, buffer, buffer_size);
+static int __v9fs_xattr_set_cached(struct dentry *dentry, const char *name,
+			const void *value, size_t value_len, int flags)
+{
+	int ret;
+	struct xattr_item *xi;
+
+	/* remove xattr item */
+	if (flags == XATTR_REPLACE && value == NULL) {
+		xi = v9fs_find_cached_xattr_item_full(dentry, name);
+		if (xi && !v9fs_xif_test_removing(xi)) {
+			down_write(&xi->rw_sem);
+			v9fs_xif_set_removing(xi);
+
+			ret = __v9fs_xattr_set(dentry, name, value,
+						value_len, flags);
+			if (ret != 0)
+				/* remove fail */
+				v9fs_xif_clear_removing(xi);
+
+			goto exit;
+		} else
+			return __v9fs_xattr_set(dentry, name,
+								value, value_len, flags);
+	} else {
+		/* create or replace */
+		xi = v9fs_find_cached_xattr_item(dentry, name);
+		if (!xi)
+			xi = v9fs_find_or_create_cached_xattr_item_slow(dentry, name);
+
+		if (!IS_ERR(xi) && !v9fs_xif_test_removing(xi)) {
+			down_write(&xi->rw_sem);
+
+			ret = __v9fs_xattr_set(dentry, name, value, value_len, flags);
+			if (ret == 0)
+				v9fs_xattr_item_setval(xi, value, value_len, value_len);
+
+			goto exit;
+		} else
+			return __v9fs_xattr_set(dentry, name, value, value_len, flags);
+	}
+
+exit:
+	up_write(&xi->rw_sem);
+	v9fs_xattr_item_dec_ref(dentry, xi);
+	return ret;
  }

  /*
@@ -96,10 +293,13 @@ ssize_t v9fs_xattr_get(struct dentry *dentry, const 
char *name,
  int v9fs_xattr_set(struct dentry *dentry, const char *name,
  		   const void *value, size_t value_len, int flags)
  {
-	struct p9_fid *fid = v9fs_fid_lookup(dentry);
-	if (IS_ERR(fid))
-		return PTR_ERR(fid);
-	return v9fs_fid_xattr_set(fid, name, value, value_len, flags);
+	struct v9fs_session_info *v9ses;
+
+	v9ses = v9fs_dentry2v9ses(dentry);
+	if (v9ses->xcache)
+		return __v9fs_xattr_set_cached(dentry, name, value, value_len, flags);
+	else
+		return __v9fs_xattr_set(dentry, name, value, value_len, flags);
  }

  int v9fs_fid_xattr_set(struct p9_fid *fid, const char *name,
diff --git a/fs/9p/xattr.h b/fs/9p/xattr.h
index d3e2ea3..d687382 100644
--- a/fs/9p/xattr.h
+++ b/fs/9p/xattr.h
@@ -18,6 +18,8 @@
  #include <net/9p/9p.h>
  #include <net/9p/client.h>

+#include "xattr_cache.h"
+
  extern const struct xattr_handler *v9fs_xattr_handlers[];
  extern struct xattr_handler v9fs_xattr_user_handler;
  extern struct xattr_handler v9fs_xattr_trusted_handler;
diff --git a/fs/9p/xattr_cache.c b/fs/9p/xattr_cache.c
new file mode 100644
index 0000000..e6dc209
--- /dev/null
+++ b/fs/9p/xattr_cache.c
@@ -0,0 +1,228 @@
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include "xattr.h"
+#include "v9fs.h"
+#include "v9fs_vfs.h"
+#include "xattr_cache.h"
+
+static void v9fs_init_xattr_item(struct xattr_item *xi, const char *xname)
+{
+	xi->xi_name = kstrdup(xname, GFP_NOFS);
+	xi->xi_nlen = strlen(xname);
+
+	xi->xi_value = NULL;
+	xi->xi_vlen = 0;
+	xi->flags = V9FS_XI_FLAGS_INIT;
+
+	v9fs_xif_set_slowpath(xi);
+	v9fs_xif_set_new(xi);
+	atomic_set(&xi->ref, 1);
+	init_rwsem(&xi->rw_sem);
+}
+
+static struct xattr_item *v9fs_find_cached_xattr_slow_lock(
+			struct list_head *head,
+			const char *xname)
+{
+	struct xattr_item *xi = NULL;
+
+	list_for_each_entry(xi, head, xi_list) {
+		if (0 == strcmp(xname, xi->xi_name)) {
+			p9_debug(P9_DEBUG_XCACHE,
+				"found xattr from slow path, name:%s nlen:%u flags:%lu\n",
+				xname, xi->xi_nlen, xi->flags);
+
+			atomic_inc(&xi->ref);
+
+			return xi;
+		}
+	}
+
+	return NULL;
+}
+
+static void v9fs_free_cached_xattr_item(struct dentry *dentry,
+			struct xattr_item *xi)
+{
+	struct v9fs_inode *v9inode = V9FS_I(dentry->d_inode);
+	unsigned long flag = v9fs_xif_test_slowpath(xi);
+
+	p9_debug(P9_DEBUG_XCACHE, "free xattr item, name:%s flags:%lu\n",
+				xi->xi_name, xi->flags);
+	if (flag)
+		spin_lock(&v9inode->xaf_lock);
+	else
+		write_lock(&v9inode->xal_lock);
+
+	list_del(&xi->xi_list);
+	kfree(xi->xi_value);
+	kfree(xi->xi_name);
+	kmem_cache_free(v9fs_xattr_cache, xi);
+
+	if (flag)
+		spin_unlock(&v9inode->xaf_lock);
+	else
+		write_unlock(&v9inode->xal_lock);
+}
+
+struct xattr_item *v9fs_find_cached_xattr_item(struct dentry *dentry,
+			const char *xname)
+{
+	struct v9fs_inode *v9inode;
+	struct xattr_item *xi;
+
+	v9inode = V9FS_I(dentry->d_inode);
+
+	read_lock(&v9inode->xal_lock);
+
+	list_for_each_entry(xi, &v9inode->xal_head, xi_list) {
+		if (0 == strcmp(xname, xi->xi_name)) {
+			p9_debug(P9_DEBUG_XCACHE,
+				"found xattr from fast path name:%s nlen:%u flags:%lu\n",
+				xname, xi->xi_nlen, xi->flags);
+
+			atomic_inc(&xi->ref);
+			read_unlock(&v9inode->xal_lock);
+
+			return xi;
+		}
+	}
+
+	read_unlock(&v9inode->xal_lock);
+
+	return NULL;
+}
+
+struct xattr_item *v9fs_find_cached_xattr_item_full(
+			struct dentry *dentry,
+			const char *xname)
+{
+	struct v9fs_inode *v9inode = V9FS_I(dentry->d_inode);
+	struct xattr_item *xi;
+
+	xi = v9fs_find_cached_xattr_item(dentry, xname);
+	if (!xi) {
+		spin_lock(&v9inode->xaf_lock);
+		xi = v9fs_find_cached_xattr_slow_lock(&v9inode->xaf_head, xname);
+		spin_unlock(&v9inode->xaf_lock);
+	}
+
+	return xi;
+}
+
+struct xattr_item *v9fs_find_or_create_cached_xattr_item_slow(
+			struct dentry *dentry,
+			const char *xname)
+{
+	struct v9fs_inode *v9inode = V9FS_I(dentry->d_inode);
+	struct xattr_item *xi = NULL;
+
+	spin_lock(&v9inode->xaf_lock);
+	xi = v9fs_find_cached_xattr_slow_lock(&v9inode->xaf_head, xname);
+	if (!xi) {
+		xi = kmem_cache_alloc(v9fs_xattr_cache, GFP_NOFS);
+		if (!xi) {
+			spin_unlock(&v9inode->xaf_lock);
+			p9_debug(P9_DEBUG_XCACHE,
+					"create cached xattr item fail, name:%s\n",
+					xname);
+			return ERR_PTR(-ENOMEM);
+		}
+
+		p9_debug(P9_DEBUG_XCACHE,
+				"create xattr item success, name:%s\n",
+				xname);
+
+		v9fs_init_xattr_item(xi, xname);
+		list_add(&xi->xi_list, &v9inode->xaf_head);
+	}
+	spin_unlock(&v9inode->xaf_lock);
+
+	return xi;
+}
+
+void v9fs_move_xattr_item_to_fast(struct dentry *dentry, struct 
xattr_item *xi)
+{
+	struct v9fs_inode *v9inode = V9FS_I(dentry->d_inode);
+
+	p9_debug(P9_DEBUG_XCACHE,
+			"move xattr item to fast path, name:%s flags:%lu\n",
+			xi->xi_name, xi->flags);
+
+	write_lock(&v9inode->xal_lock);
+	spin_lock(&v9inode->xaf_lock);
+
+	list_move(&xi->xi_list, &v9inode->xal_head);
+	v9fs_xif_clear_slowpath(xi);
+
+	spin_unlock(&v9inode->xaf_lock);
+	write_unlock(&v9inode->xal_lock);
+}
+
+int v9fs_xattr_item_setval(struct xattr_item *xi, const char *buffer,
+				size_t buffer_size, ssize_t ret)
+{
+	p9_debug(P9_DEBUG_XCACHE,
+			"setvalue, name:%s flags:%lu buffer:%s buffer_size:%zu ret:%zu\n",
+			xi->xi_name, xi->flags,
+			buffer ? buffer : "null", buffer_size, ret);
+
+	if (likely(ret >= 0)) {
+		if (buffer_size) {
+			kfree(xi->xi_value);
+
+			xi->xi_vlen = ret;
+			xi->xi_value = kstrdup(buffer, GFP_NOFS);
+			if (!xi->xi_value) {
+				v9fs_xif_set_nodata(xi);
+				p9_debug(P9_DEBUG_ERROR, "setval fail, no memory.\n");
+				return -ENOMEM;
+			} else
+				v9fs_xif_set_normal(xi);
+		} else {
+			v9fs_xif_set_nodata(xi);
+			xi->xi_vlen = ret;
+		}
+	} else if (ret == -EOPNOTSUPP) {
+		v9fs_xif_set_enotsupp(xi);
+	} else if (ret == -ERANGE) {
+		xi->xi_vlen = buffer_size;
+		v9fs_xif_set_erange(xi);
+	} else
+		v9fs_xif_set_removing(xi);
+
+	return ret;
+}
+
+void v9fs_xattr_item_dec_ref(struct dentry *dentry, struct xattr_item *xi)
+{
+	if (v9fs_xif_test_removing(xi) && atomic_dec_and_test(&xi->ref))
+		v9fs_free_cached_xattr_item(dentry, xi);
+	else
+		atomic_dec(&xi->ref);
+}
+
+void v9fs_free_inode_xattr_items(struct inode *inode)
+{
+	struct v9fs_inode *v9inode = V9FS_I(inode);
+	struct xattr_item *xi, *tmp;
+
+	p9_debug(P9_DEBUG_XCACHE,
+			"free all xattr items for inode, inode:%p\n", inode);
+
+	list_for_each_entry_safe(xi, tmp, &v9inode->xal_head, xi_list) {
+		if (xi->xi_vlen)
+			kfree(xi->xi_value);
+		kfree(xi->xi_name);
+		kmem_cache_free(v9fs_xattr_cache, xi);
+	}
+
+	list_for_each_entry_safe(xi, tmp, &v9inode->xaf_head, xi_list) {
+		kfree(xi->xi_value);
+		kfree(xi->xi_name);
+		kmem_cache_free(v9fs_xattr_cache, xi);
+	}
+}
diff --git a/fs/9p/xattr_cache.h b/fs/9p/xattr_cache.h
new file mode 100644
index 0000000..17786af
--- /dev/null
+++ b/fs/9p/xattr_cache.h
@@ -0,0 +1,100 @@
+#ifndef FS_9P_XCACHE_H
+#define FS_9P_XCACHE_H
+
+struct xattr_item {
+	struct list_head xi_list;
+
+	char *xi_name;
+	u16 xi_nlen;
+	char *xi_value;
+	u16 xi_vlen;
+
+	unsigned long flags;
+	atomic_t ref;
+	struct rw_semaphore rw_sem;
+};
+
+enum v9fs_xi_flags {
+	/* xattr item value flags */
+	V9FS_XI_Normal,
+	V9FS_XI_Nodata,
+	V9FS_XI_Erange,
+	V9FS_XI_Enotsupp,
+	V9FS_XI_Bound,
+	/* xattr item flags */
+	V9FS_XI_New,
+	V9FS_XI_Removing,
+	V9FS_XI_Slowpath,
+};
+
+#define V9FS_XI_FLAGS_INIT	0UL
+
+#define V9FS_XI_FLAGS_BOUND ((1 << V9FS_XI_Bound) - 1)
+
+#define V9FS_XIF_FNS_EXCL(bit, name)				\
+static inline void v9fs_xif_set_##name(struct xattr_item *xi)		\
+{									\
+	xi->flags = (xi->flags & ~V9FS_XI_FLAGS_BOUND) | (1 << V9FS_XI_##bit);	\
+}									\
+static inline int v9fs_xif_test_##name(struct xattr_item *xi)		\
+{									\
+	return (xi->flags & V9FS_XI_FLAGS_BOUND) & (1 << V9FS_XI_##bit);	\
+}
+
+V9FS_XIF_FNS_EXCL(Normal, normal);
+V9FS_XIF_FNS_EXCL(Nodata, nodata);
+V9FS_XIF_FNS_EXCL(Erange, erange);
+V9FS_XIF_FNS_EXCL(Enotsupp, enotsupp);
+
+static inline unsigned long v9fs_xif_current_dflag(struct xattr_item *xi)
+{
+	return xi->flags & V9FS_XI_FLAGS_BOUND;
+}
+
+#define V9FS_XIF_FNS(bit, name)						\
+static inline void v9fs_xif_set_##name(struct xattr_item *xi)		\
+{									\
+	set_bit(V9FS_XI_##bit, &(xi)->flags);				\
+}									\
+static inline void v9fs_xif_clear_##name(struct xattr_item *xi)		\
+{									\
+	clear_bit(V9FS_XI_##bit, &(xi)->flags);				\
+}									\
+static inline int v9fs_xif_test_##name(const struct xattr_item *xi)		\
+{									\
+	return test_bit(V9FS_XI_##bit, &(xi)->flags);			\
+}
+
+V9FS_XIF_FNS(New, new);
+V9FS_XIF_FNS(Removing, removing);
+V9FS_XIF_FNS(Slowpath, slowpath);
+
+#define V9FS_XIF_TAS(bit, name)						\
+static inline int v9fs_xif_test_set_##name(struct xattr_item *xi)	\
+{									\
+	return test_and_set_bit(V9FS_XI_##bit, &(xi)->flags);		\
+}									\
+static inline int v9fs_xif_test_clear_##name(struct xattr_item *xi)	\
+{									\
+	return test_and_clear_bit(V9FS_XI_##bit, &(xi)->flags);		\
+}
+
+V9FS_XIF_TAS(New, new);
+
+extern struct xattr_item *v9fs_find_cached_xattr_item(struct dentry 
*dentry,
+			const char *xname);
+extern struct xattr_item *v9fs_find_cached_xattr_item_full(
+			struct dentry *dentry,
+			const char *xname);
+extern struct xattr_item *v9fs_find_or_create_cached_xattr_item_slow(
+			struct dentry *dentry,
+			const char *xname);
+extern void v9fs_move_xattr_item_to_fast(struct dentry *dentry,
+			struct xattr_item *xi);
+extern int v9fs_xattr_item_setval(struct xattr_item *xi, const char 
*buffer,
+			size_t buffer_size, ssize_t ret);
+extern void v9fs_xattr_item_dec_ref(struct dentry *dentry,
+			struct xattr_item *xi);
+extern void v9fs_free_inode_xattr_items(struct inode *inode);
+
+#endif
diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h
index 27dfe85..8346030 100644
--- a/include/net/9p/9p.h
+++ b/include/net/9p/9p.h
@@ -59,6 +59,7 @@ enum p9_debug_flags {
  	P9_DEBUG_PKT =		(1<<10),
  	P9_DEBUG_FSC =		(1<<11),
  	P9_DEBUG_VPKT =		(1<<12),
+	P9_DEBUG_XCACHE =	(1<<13),
  };

  #ifdef CONFIG_NET_9P_DEBUG
-- 
1.9.5.msysgit.1

                 reply	other threads:[~2015-06-01 10:22 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=556C3261.2030308@unitedstack.com \
    --to=baijuncheng@unitedstack.com \
    --cc=dhowells@redhat.com \
    --cc=dominique.martinet@cea.fr \
    --cc=ericvh@gmail.com \
    --cc=kirill.shutemov@linux.intel.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=v9fs-developer@lists.sourceforge.net \
    --cc=viro@zeniv.linux.org.uk \
    /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.