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

fuse: Use hash table to link processing request

We noticed the performance bottleneck in FUSE running our Virtuozzo storage
over rdma. On some types of workload we observe 20% of times spent in
request_find() in profiler. This function is iterating over long requests
list, and it scales bad.

The patch introduces hash table to reduce the number of iterations, we do
in this function. Hash generating algorithm is taken from hash_add()
function, while 256 lines table is used to store pending requests. This
fixes problem and improves the performance.

Reported-by: Alexey Kuznetsov <kuznet@virtuozzo.com>
Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>

authored by

Kirill Tkhai and committed by
Miklos Szeredi
be2ff42c 3a5358d1

+41 -14
+17 -4
fs/fuse/dev.c
··· 327 327 return fiq->reqctr; 328 328 } 329 329 330 + static unsigned int fuse_req_hash(u64 unique) 331 + { 332 + return hash_long(unique & ~FUSE_INT_REQ_BIT, FUSE_PQ_HASH_BITS); 333 + } 334 + 330 335 static void queue_request(struct fuse_iqueue *fiq, struct fuse_req *req) 331 336 { 332 337 req->in.h.len = sizeof(struct fuse_in_header) + ··· 1253 1248 struct fuse_req *req; 1254 1249 struct fuse_in *in; 1255 1250 unsigned reqsize; 1251 + unsigned int hash; 1256 1252 1257 1253 restart: 1258 1254 spin_lock(&fiq->waitq.lock); ··· 1326 1320 err = reqsize; 1327 1321 goto out_end; 1328 1322 } 1329 - list_move_tail(&req->list, &fpq->processing); 1323 + hash = fuse_req_hash(req->in.h.unique); 1324 + list_move_tail(&req->list, &fpq->processing[hash]); 1330 1325 __fuse_get_request(req); 1331 1326 set_bit(FR_SENT, &req->flags); 1332 1327 spin_unlock(&fpq->lock); ··· 1811 1804 /* Look up request on processing list by unique ID */ 1812 1805 static struct fuse_req *request_find(struct fuse_pqueue *fpq, u64 unique) 1813 1806 { 1807 + unsigned int hash = fuse_req_hash(unique); 1814 1808 struct fuse_req *req; 1815 1809 1816 - list_for_each_entry(req, &fpq->processing, list) { 1810 + list_for_each_entry(req, &fpq->processing[hash], list) { 1817 1811 if (req->in.h.unique == unique) 1818 1812 return req; 1819 1813 } ··· 2126 2118 struct fuse_dev *fud; 2127 2119 struct fuse_req *req, *next; 2128 2120 LIST_HEAD(to_end); 2121 + unsigned int i; 2129 2122 2130 2123 /* Background queuing checks fc->connected under bg_lock */ 2131 2124 spin_lock(&fc->bg_lock); ··· 2151 2142 } 2152 2143 spin_unlock(&req->waitq.lock); 2153 2144 } 2154 - list_splice_tail_init(&fpq->processing, &to_end); 2145 + for (i = 0; i < FUSE_PQ_HASH_SIZE; i++) 2146 + list_splice_tail_init(&fpq->processing[i], 2147 + &to_end); 2155 2148 spin_unlock(&fpq->lock); 2156 2149 } 2157 2150 spin_lock(&fc->bg_lock); ··· 2196 2185 struct fuse_conn *fc = fud->fc; 2197 2186 struct fuse_pqueue *fpq = &fud->pq; 2198 2187 LIST_HEAD(to_end); 2188 + unsigned int i; 2199 2189 2200 2190 spin_lock(&fpq->lock); 2201 2191 WARN_ON(!list_empty(&fpq->io)); 2202 - list_splice_init(&fpq->processing, &to_end); 2192 + for (i = 0; i < FUSE_PQ_HASH_SIZE; i++) 2193 + list_splice_init(&fpq->processing[i], &to_end); 2203 2194 spin_unlock(&fpq->lock); 2204 2195 2205 2196 end_requests(fc, &to_end);
+5 -2
fs/fuse/fuse_i.h
··· 408 408 struct fasync_struct *fasync; 409 409 }; 410 410 411 + #define FUSE_PQ_HASH_BITS 8 412 + #define FUSE_PQ_HASH_SIZE (1 << FUSE_PQ_HASH_BITS) 413 + 411 414 struct fuse_pqueue { 412 415 /** Connection established */ 413 416 unsigned connected; ··· 418 415 /** Lock protecting accessess to members of this structure */ 419 416 spinlock_t lock; 420 417 421 - /** The list of requests being processed */ 422 - struct list_head processing; 418 + /** Hash table of requests being processed */ 419 + struct list_head *processing; 423 420 424 421 /** The list of requests under I/O */ 425 422 struct list_head io;
+19 -8
fs/fuse/inode.c
··· 594 594 595 595 static void fuse_pqueue_init(struct fuse_pqueue *fpq) 596 596 { 597 - memset(fpq, 0, sizeof(struct fuse_pqueue)); 597 + unsigned int i; 598 + 598 599 spin_lock_init(&fpq->lock); 599 - INIT_LIST_HEAD(&fpq->processing); 600 + for (i = 0; i < FUSE_PQ_HASH_SIZE; i++) 601 + INIT_LIST_HEAD(&fpq->processing[i]); 600 602 INIT_LIST_HEAD(&fpq->io); 601 603 fpq->connected = 1; 602 604 } ··· 1027 1025 struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc) 1028 1026 { 1029 1027 struct fuse_dev *fud; 1028 + struct list_head *pq; 1030 1029 1031 1030 fud = kzalloc(sizeof(struct fuse_dev), GFP_KERNEL); 1032 - if (fud) { 1033 - fud->fc = fuse_conn_get(fc); 1034 - fuse_pqueue_init(&fud->pq); 1031 + if (!fud) 1032 + return NULL; 1035 1033 1036 - spin_lock(&fc->lock); 1037 - list_add_tail(&fud->entry, &fc->devices); 1038 - spin_unlock(&fc->lock); 1034 + pq = kcalloc(FUSE_PQ_HASH_SIZE, sizeof(struct list_head), GFP_KERNEL); 1035 + if (!pq) { 1036 + kfree(fud); 1037 + return NULL; 1039 1038 } 1039 + 1040 + fud->pq.processing = pq; 1041 + fud->fc = fuse_conn_get(fc); 1042 + fuse_pqueue_init(&fud->pq); 1043 + 1044 + spin_lock(&fc->lock); 1045 + list_add_tail(&fud->entry, &fc->devices); 1046 + spin_unlock(&fc->lock); 1040 1047 1041 1048 return fud; 1042 1049 }