From patchwork Mon Jun 1 10:22:25 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: juncheng bai X-Patchwork-Id: 6520471 Return-Path: X-Original-To: patchwork-linux-fsdevel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id B9EAFC0020 for ; Mon, 1 Jun 2015 10:22:45 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C24F920570 for ; Mon, 1 Jun 2015 10:22:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 5105920591 for ; Mon, 1 Jun 2015 10:22:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752175AbbFAKWj (ORCPT ); Mon, 1 Jun 2015 06:22:39 -0400 Received: from smtpbg298.qq.com ([184.105.67.102]:34626 "EHLO smtpbg298.qq.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751901AbbFAKWh (ORCPT ); Mon, 1 Jun 2015 06:22:37 -0400 X-QQ-mid: bizesmtp8t1433154147t961t169 Received: from [172.16.0.123] (unknown [182.48.117.114]) by esmtp4.qq.com (ESMTP) with id ; Mon, 01 Jun 2015 18:22:25 +0800 (CST) X-QQ-SSF: 0140000000200010F322B00A0000000 X-QQ-FEAT: SRgUpOeSSxJ0RpVmGDNBE4TxdREjgsbb2Rt9vESktx+DuLvpJjAV+AppadJog cEpkfv9nncjHiBUdqab5gKNhK47LsaivJVFSQ6QnH/pBdX1hkErmUfCxsHkyFwgqD1e9XMl 2/E7byIsRNFVYhPNTuj35DNZX8MU2CgzX8lhhJVMQWKbjL/xtTmZQHMldkI0V8VDuByahi8 0ZaY19yyxBGE1wWIpNVazmvg7E4iB03M= X-QQ-GoodBg: 2 Message-ID: <556C3261.2030308@unitedstack.com> Date: Mon, 01 Jun 2015 18:22:25 +0800 From: juncheng bai User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Thunderbird/31.7.0 MIME-Version: 1.0 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 X-QQ-SENDSIZE: 520 X-QQ-FName: 140DF6F7A2B3403B9736F86C7911976C X-QQ-LocalIP: 58.250.134.100 To: unlisted-recipients:; (no To-header on input) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 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 --- 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 #include +#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 #include +#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 +#include +#include +#include +#include +#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