aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse
diff options
context:
space:
mode:
authorKirill Tkhai <ktkhai@virtuozzo.com>2018-09-11 06:12:14 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2018-09-28 10:43:23 -0400
commitbe2ff42c5d6ebc8552c82a7d1697afae30510ed9 (patch)
tree34d256dcf2c28957a3a1deefe42cd6658f0ff4a3 /fs/fuse
parent3a5358d1a1b70bb3360578f09894d6856629ecdf (diff)
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>
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/dev.c21
-rw-r--r--fs/fuse/fuse_i.h7
-rw-r--r--fs/fuse/inode.c27
3 files changed, 41 insertions, 14 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index eee43057b99b..91b4ecf85dc7 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -327,6 +327,11 @@ static u64 fuse_get_unique(struct fuse_iqueue *fiq)
327 return fiq->reqctr; 327 return fiq->reqctr;
328} 328}
329 329
330static unsigned int fuse_req_hash(u64 unique)
331{
332 return hash_long(unique & ~FUSE_INT_REQ_BIT, FUSE_PQ_HASH_BITS);
333}
334
330static void queue_request(struct fuse_iqueue *fiq, struct fuse_req *req) 335static void queue_request(struct fuse_iqueue *fiq, struct fuse_req *req)
331{ 336{
332 req->in.h.len = sizeof(struct fuse_in_header) + 337 req->in.h.len = sizeof(struct fuse_in_header) +
@@ -1248,6 +1253,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
1248 struct fuse_req *req; 1253 struct fuse_req *req;
1249 struct fuse_in *in; 1254 struct fuse_in *in;
1250 unsigned reqsize; 1255 unsigned reqsize;
1256 unsigned int hash;
1251 1257
1252 restart: 1258 restart:
1253 spin_lock(&fiq->waitq.lock); 1259 spin_lock(&fiq->waitq.lock);
@@ -1320,7 +1326,8 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
1320 err = reqsize; 1326 err = reqsize;
1321 goto out_end; 1327 goto out_end;
1322 } 1328 }
1323 list_move_tail(&req->list, &fpq->processing); 1329 hash = fuse_req_hash(req->in.h.unique);
1330 list_move_tail(&req->list, &fpq->processing[hash]);
1324 __fuse_get_request(req); 1331 __fuse_get_request(req);
1325 set_bit(FR_SENT, &req->flags); 1332 set_bit(FR_SENT, &req->flags);
1326 spin_unlock(&fpq->lock); 1333 spin_unlock(&fpq->lock);
@@ -1804,9 +1811,10 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
1804/* Look up request on processing list by unique ID */ 1811/* Look up request on processing list by unique ID */
1805static struct fuse_req *request_find(struct fuse_pqueue *fpq, u64 unique) 1812static struct fuse_req *request_find(struct fuse_pqueue *fpq, u64 unique)
1806{ 1813{
1814 unsigned int hash = fuse_req_hash(unique);
1807 struct fuse_req *req; 1815 struct fuse_req *req;
1808 1816
1809 list_for_each_entry(req, &fpq->processing, list) { 1817 list_for_each_entry(req, &fpq->processing[hash], list) {
1810 if (req->in.h.unique == unique) 1818 if (req->in.h.unique == unique)
1811 return req; 1819 return req;
1812 } 1820 }
@@ -2118,6 +2126,7 @@ void fuse_abort_conn(struct fuse_conn *fc, bool is_abort)
2118 struct fuse_dev *fud; 2126 struct fuse_dev *fud;
2119 struct fuse_req *req, *next; 2127 struct fuse_req *req, *next;
2120 LIST_HEAD(to_end); 2128 LIST_HEAD(to_end);
2129 unsigned int i;
2121 2130
2122 /* Background queuing checks fc->connected under bg_lock */ 2131 /* Background queuing checks fc->connected under bg_lock */
2123 spin_lock(&fc->bg_lock); 2132 spin_lock(&fc->bg_lock);
@@ -2142,7 +2151,9 @@ void fuse_abort_conn(struct fuse_conn *fc, bool is_abort)
2142 } 2151 }
2143 spin_unlock(&req->waitq.lock); 2152 spin_unlock(&req->waitq.lock);
2144 } 2153 }
2145 list_splice_tail_init(&fpq->processing, &to_end); 2154 for (i = 0; i < FUSE_PQ_HASH_SIZE; i++)
2155 list_splice_tail_init(&fpq->processing[i],
2156 &to_end);
2146 spin_unlock(&fpq->lock); 2157 spin_unlock(&fpq->lock);
2147 } 2158 }
2148 spin_lock(&fc->bg_lock); 2159 spin_lock(&fc->bg_lock);
@@ -2185,10 +2196,12 @@ int fuse_dev_release(struct inode *inode, struct file *file)
2185 struct fuse_conn *fc = fud->fc; 2196 struct fuse_conn *fc = fud->fc;
2186 struct fuse_pqueue *fpq = &fud->pq; 2197 struct fuse_pqueue *fpq = &fud->pq;
2187 LIST_HEAD(to_end); 2198 LIST_HEAD(to_end);
2199 unsigned int i;
2188 2200
2189 spin_lock(&fpq->lock); 2201 spin_lock(&fpq->lock);
2190 WARN_ON(!list_empty(&fpq->io)); 2202 WARN_ON(!list_empty(&fpq->io));
2191 list_splice_init(&fpq->processing, &to_end); 2203 for (i = 0; i < FUSE_PQ_HASH_SIZE; i++)
2204 list_splice_init(&fpq->processing[i], &to_end);
2192 spin_unlock(&fpq->lock); 2205 spin_unlock(&fpq->lock);
2193 2206
2194 end_requests(fc, &to_end); 2207 end_requests(fc, &to_end);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 1d7b5b7a051d..2c4272076f62 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -408,6 +408,9 @@ struct fuse_iqueue {
408 struct fasync_struct *fasync; 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
411struct fuse_pqueue { 414struct fuse_pqueue {
412 /** Connection established */ 415 /** Connection established */
413 unsigned connected; 416 unsigned connected;
@@ -415,8 +418,8 @@ struct fuse_pqueue {
415 /** Lock protecting accessess to members of this structure */ 418 /** Lock protecting accessess to members of this structure */
416 spinlock_t lock; 419 spinlock_t lock;
417 420
418 /** The list of requests being processed */ 421 /** Hash table of requests being processed */
419 struct list_head processing; 422 struct list_head *processing;
420 423
421 /** The list of requests under I/O */ 424 /** The list of requests under I/O */
422 struct list_head io; 425 struct list_head io;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index ed3f49628ce2..9383b47b3d9b 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -594,9 +594,11 @@ static void fuse_iqueue_init(struct fuse_iqueue *fiq)
594 594
595static void fuse_pqueue_init(struct fuse_pqueue *fpq) 595static void fuse_pqueue_init(struct fuse_pqueue *fpq)
596{ 596{
597 memset(fpq, 0, sizeof(struct fuse_pqueue)); 597 unsigned int i;
598
598 spin_lock_init(&fpq->lock); 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 INIT_LIST_HEAD(&fpq->io); 602 INIT_LIST_HEAD(&fpq->io);
601 fpq->connected = 1; 603 fpq->connected = 1;
602} 604}
@@ -1025,17 +1027,26 @@ static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb)
1025struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc) 1027struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc)
1026{ 1028{
1027 struct fuse_dev *fud; 1029 struct fuse_dev *fud;
1030 struct list_head *pq;
1028 1031
1029 fud = kzalloc(sizeof(struct fuse_dev), GFP_KERNEL); 1032 fud = kzalloc(sizeof(struct fuse_dev), GFP_KERNEL);
1030 if (fud) { 1033 if (!fud)
1031 fud->fc = fuse_conn_get(fc); 1034 return NULL;
1032 fuse_pqueue_init(&fud->pq);
1033 1035
1034 spin_lock(&fc->lock); 1036 pq = kcalloc(FUSE_PQ_HASH_SIZE, sizeof(struct list_head), GFP_KERNEL);
1035 list_add_tail(&fud->entry, &fc->devices); 1037 if (!pq) {
1036 spin_unlock(&fc->lock); 1038 kfree(fud);
1039 return NULL;
1037 } 1040 }
1038 1041
1042 fud->pq.processing = pq;
1043 fud->fc = fuse_conn_get(fc);
1044 fuse_pqueue_init(&fud->pq);
1045
1046 spin_lock(&fc->lock);
1047 list_add_tail(&fud->entry, &fc->devices);
1048 spin_unlock(&fc->lock);
1049
1039 return fud; 1050 return fud;
1040} 1051}
1041EXPORT_SYMBOL_GPL(fuse_dev_alloc); 1052EXPORT_SYMBOL_GPL(fuse_dev_alloc);