Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

orangefs: implement xattr cache

This uses the same timeout as the getattr cache. This substantially
increases performance when writing files with smaller buffer sizes.

When writing, the size is (often) changed, which causes a call to
notify_change which calls security_inode_need_killpriv which needs a
getxattr. Caching it reduces traffic to the server.

Signed-off-by: Martin Brandenburg <martin@omnibond.com>
Signed-off-by: Mike Marshall <hubcap@omnibond.com>

authored by

Martin Brandenburg and committed by
Mike Marshall
fc2e2e9c 37624b58

+127 -1
+1
fs/orangefs/inode.c
··· 364 364 struct orangefs_object_kref *ref = (struct orangefs_object_kref *) data; 365 365 ORANGEFS_I(inode)->refn.fs_id = ref->fs_id; 366 366 ORANGEFS_I(inode)->refn.khandle = ref->khandle; 367 + hash_init(ORANGEFS_I(inode)->xattr_cache); 367 368 return 0; 368 369 } 369 370
+11
fs/orangefs/orangefs-kernel.h
··· 51 51 #include <linux/rwsem.h> 52 52 #include <linux/xattr.h> 53 53 #include <linux/exportfs.h> 54 + #include <linux/hashtable.h> 54 55 55 56 #include <asm/unaligned.h> 56 57 ··· 194 193 195 194 unsigned long getattr_time; 196 195 u32 getattr_mask; 196 + 197 + DECLARE_HASHTABLE(xattr_cache, 4); 197 198 }; 198 199 199 200 /* per superblock private orangefs info */ ··· 218 215 unsigned long cache_misses; 219 216 unsigned long reads; 220 217 unsigned long writes; 218 + }; 219 + 220 + struct orangefs_cached_xattr { 221 + struct hlist_node node; 222 + char key[ORANGEFS_MAX_XATTR_NAMELEN]; 223 + char val[ORANGEFS_MAX_XATTR_VALUELEN]; 224 + ssize_t length; 225 + unsigned long timeout; 221 226 }; 222 227 223 228 extern struct orangefs_stats orangefs_stats;
+10
fs/orangefs/super.c
··· 10 10 #include "orangefs-bufmap.h" 11 11 12 12 #include <linux/parser.h> 13 + #include <linux/hashtable.h> 13 14 14 15 /* a cache for orangefs-inode objects (i.e. orangefs inode private data) */ 15 16 static struct kmem_cache *orangefs_inode_cache; ··· 129 128 { 130 129 struct inode *inode = container_of(head, struct inode, i_rcu); 131 130 struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); 131 + struct orangefs_cached_xattr *cx; 132 + struct hlist_node *tmp; 133 + int i; 134 + 135 + hash_for_each_safe(orangefs_inode->xattr_cache, i, tmp, cx, node) { 136 + hlist_del(&cx->node); 137 + kfree(cx); 138 + } 139 + 132 140 kmem_cache_free(orangefs_inode_cache, orangefs_inode); 133 141 } 134 142
+105 -1
fs/orangefs/xattr.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 /* 3 3 * (C) 2001 Clemson University and The University of Chicago 4 + * Copyright 2018 Omnibond Systems, L.L.C. 4 5 * 5 6 * See COPYING in top-level directory. 6 7 */ ··· 15 14 #include "orangefs-bufmap.h" 16 15 #include <linux/posix_acl_xattr.h> 17 16 #include <linux/xattr.h> 18 - 17 + #include <linux/hashtable.h> 19 18 20 19 #define SYSTEM_ORANGEFS_KEY "system.pvfs2." 21 20 #define SYSTEM_ORANGEFS_KEY_LEN 13 ··· 51 50 return internal_flag; 52 51 } 53 52 53 + static unsigned int xattr_key(const char *key) 54 + { 55 + unsigned int i = 0; 56 + while (key) 57 + i += *key++; 58 + return i % 16; 59 + } 60 + 61 + static struct orangefs_cached_xattr *find_cached_xattr(struct inode *inode, 62 + const char *key) 63 + { 64 + struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); 65 + struct orangefs_cached_xattr *cx; 66 + struct hlist_head *h; 67 + struct hlist_node *tmp; 68 + h = &orangefs_inode->xattr_cache[xattr_key(key)]; 69 + if (hlist_empty(h)) 70 + return NULL; 71 + hlist_for_each_entry_safe(cx, tmp, h, node) { 72 + /* if (!time_before(jiffies, cx->timeout)) { 73 + hlist_del(&cx->node); 74 + kfree(cx); 75 + continue; 76 + }*/ 77 + if (!strcmp(cx->key, key)) 78 + return cx; 79 + } 80 + return NULL; 81 + } 54 82 55 83 /* 56 84 * Tries to get a specified key's attributes of a given ··· 95 65 { 96 66 struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); 97 67 struct orangefs_kernel_op_s *new_op = NULL; 68 + struct orangefs_cached_xattr *cx; 98 69 ssize_t ret = -ENOMEM; 99 70 ssize_t length = 0; 100 71 int fsuid; ··· 124 93 125 94 down_read(&orangefs_inode->xattr_sem); 126 95 96 + cx = find_cached_xattr(inode, name); 97 + if (cx && time_before(jiffies, cx->timeout)) { 98 + if (cx->length == -1) { 99 + ret = -ENODATA; 100 + goto out_unlock; 101 + } else { 102 + if (size == 0) { 103 + ret = cx->length; 104 + goto out_unlock; 105 + } 106 + if (cx->length > size) { 107 + ret = -ERANGE; 108 + goto out_unlock; 109 + } 110 + memcpy(buffer, cx->val, cx->length); 111 + memset(buffer + cx->length, 0, size - cx->length); 112 + ret = cx->length; 113 + goto out_unlock; 114 + } 115 + } 116 + 127 117 new_op = op_alloc(ORANGEFS_VFS_OP_GETXATTR); 128 118 if (!new_op) 129 119 goto out_unlock; ··· 169 117 " does not exist!\n", 170 118 get_khandle_from_ino(inode), 171 119 (char *)new_op->upcall.req.getxattr.key); 120 + cx = kmalloc(sizeof *cx, GFP_KERNEL); 121 + if (cx) { 122 + strcpy(cx->key, name); 123 + cx->length = -1; 124 + cx->timeout = jiffies + 125 + orangefs_getattr_timeout_msecs*HZ/1000; 126 + hash_add(orangefs_inode->xattr_cache, &cx->node, 127 + xattr_key(cx->key)); 128 + } 172 129 } 173 130 goto out_release_op; 174 131 } ··· 217 156 218 157 ret = length; 219 158 159 + if (cx) { 160 + strcpy(cx->key, name); 161 + memcpy(cx->val, buffer, length); 162 + cx->length = length; 163 + cx->timeout = jiffies + HZ; 164 + } else { 165 + cx = kmalloc(sizeof *cx, GFP_KERNEL); 166 + if (cx) { 167 + strcpy(cx->key, name); 168 + memcpy(cx->val, buffer, length); 169 + cx->length = length; 170 + cx->timeout = jiffies + HZ; 171 + hash_add(orangefs_inode->xattr_cache, &cx->node, 172 + xattr_key(cx->key)); 173 + } 174 + } 175 + 220 176 out_release_op: 221 177 op_release(new_op); 222 178 out_unlock: ··· 246 168 { 247 169 struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); 248 170 struct orangefs_kernel_op_s *new_op = NULL; 171 + struct orangefs_cached_xattr *cx; 172 + struct hlist_head *h; 173 + struct hlist_node *tmp; 249 174 int ret = -ENOMEM; 250 175 251 176 if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN) ··· 290 209 "orangefs_inode_removexattr: returning %d\n", ret); 291 210 292 211 op_release(new_op); 212 + 213 + h = &orangefs_inode->xattr_cache[xattr_key(name)]; 214 + hlist_for_each_entry_safe(cx, tmp, h, node) { 215 + if (!strcmp(cx->key, name)) { 216 + hlist_del(&cx->node); 217 + kfree(cx); 218 + break; 219 + } 220 + } 221 + 293 222 out_unlock: 294 223 up_write(&orangefs_inode->xattr_sem); 295 224 return ret; ··· 317 226 struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); 318 227 struct orangefs_kernel_op_s *new_op; 319 228 int internal_flag = 0; 229 + struct orangefs_cached_xattr *cx; 230 + struct hlist_head *h; 231 + struct hlist_node *tmp; 320 232 int ret = -ENOMEM; 321 233 322 234 gossip_debug(GOSSIP_XATTR_DEBUG, ··· 381 287 382 288 /* when request is serviced properly, free req op struct */ 383 289 op_release(new_op); 290 + 291 + h = &orangefs_inode->xattr_cache[xattr_key(name)]; 292 + hlist_for_each_entry_safe(cx, tmp, h, node) { 293 + if (!strcmp(cx->key, name)) { 294 + hlist_del(&cx->node); 295 + kfree(cx); 296 + break; 297 + } 298 + } 299 + 384 300 out_unlock: 385 301 up_write(&orangefs_inode->xattr_sem); 386 302 return ret;