aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse
diff options
context:
space:
mode:
authorMaxim Patlasov <mpatlasov@parallels.com>2012-10-26 11:50:29 -0400
committerMiklos Szeredi <mszeredi@suse.cz>2013-01-24 10:21:27 -0500
commit7c190c8b9c0dd373cdd4d96e63306ec6e1a7115d (patch)
tree6814852e3df702e680781928ed5a0a8fae6bb9c9 /fs/fuse
parentb98d023a24496bf7d538c549e5426b1173c6f55d (diff)
fuse: optimize fuse_get_user_pages()
Let fuse_get_user_pages() pack as many iov-s to a single fuse_req as possible. This is very beneficial in case of iov[] consisting of many iov-s of relatively small sizes (e.g. PAGE_SIZE). Signed-off-by: Maxim Patlasov <mpatlasov@parallels.com> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/file.c79
1 files changed, 51 insertions, 28 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 542ad97b103a..b2aa6c21e209 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1047,29 +1047,37 @@ static void fuse_release_user_pages(struct fuse_req *req, int write)
1047 } 1047 }
1048} 1048}
1049 1049
1050static inline void fuse_page_descs_length_init(struct fuse_req *req) 1050static inline void fuse_page_descs_length_init(struct fuse_req *req,
1051 unsigned index, unsigned nr_pages)
1051{ 1052{
1052 int i; 1053 int i;
1053 1054
1054 for (i = 0; i < req->num_pages; i++) 1055 for (i = index; i < index + nr_pages; i++)
1055 req->page_descs[i].length = PAGE_SIZE - 1056 req->page_descs[i].length = PAGE_SIZE -
1056 req->page_descs[i].offset; 1057 req->page_descs[i].offset;
1057} 1058}
1058 1059
1060static inline unsigned long fuse_get_user_addr(const struct iov_iter *ii)
1061{
1062 return (unsigned long)ii->iov->iov_base + ii->iov_offset;
1063}
1064
1065static inline size_t fuse_get_frag_size(const struct iov_iter *ii,
1066 size_t max_size)
1067{
1068 return min(iov_iter_single_seg_count(ii), max_size);
1069}
1070
1059static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii, 1071static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii,
1060 size_t *nbytesp, int write) 1072 size_t *nbytesp, int write)
1061{ 1073{
1062 size_t nbytes = *nbytesp; 1074 size_t nbytes = 0; /* # bytes already packed in req */
1063 size_t frag_size = min(iov_iter_single_seg_count(ii), nbytes);
1064 unsigned long user_addr;
1065 unsigned offset;
1066 int npages;
1067
1068 user_addr = (unsigned long)ii->iov->iov_base + ii->iov_offset;
1069 offset = user_addr & ~PAGE_MASK;
1070 1075
1071 /* Special case for kernel I/O: can copy directly into the buffer */ 1076 /* Special case for kernel I/O: can copy directly into the buffer */
1072 if (segment_eq(get_fs(), KERNEL_DS)) { 1077 if (segment_eq(get_fs(), KERNEL_DS)) {
1078 unsigned long user_addr = fuse_get_user_addr(ii);
1079 size_t frag_size = fuse_get_frag_size(ii, *nbytesp);
1080
1073 if (write) 1081 if (write)
1074 req->in.args[1].value = (void *) user_addr; 1082 req->in.args[1].value = (void *) user_addr;
1075 else 1083 else
@@ -1080,30 +1088,45 @@ static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii,
1080 return 0; 1088 return 0;
1081 } 1089 }
1082 1090
1083 nbytes = min_t(size_t, frag_size, FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT); 1091 while (nbytes < *nbytesp && req->num_pages < FUSE_MAX_PAGES_PER_REQ) {
1084 npages = (nbytes + offset + PAGE_SIZE - 1) >> PAGE_SHIFT; 1092 unsigned npages;
1085 npages = clamp(npages, 1, FUSE_MAX_PAGES_PER_REQ); 1093 unsigned long user_addr = fuse_get_user_addr(ii);
1086 npages = get_user_pages_fast(user_addr, npages, !write, req->pages); 1094 unsigned offset = user_addr & ~PAGE_MASK;
1087 if (npages < 0) 1095 size_t frag_size = fuse_get_frag_size(ii, *nbytesp - nbytes);
1088 return npages; 1096 int ret;
1089 1097
1090 req->num_pages = npages; 1098 unsigned n = FUSE_MAX_PAGES_PER_REQ - req->num_pages;
1091 req->page_descs[0].offset = offset; 1099 frag_size = min_t(size_t, frag_size, n << PAGE_SHIFT);
1092 fuse_page_descs_length_init(req); 1100
1101 npages = (frag_size + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
1102 npages = clamp(npages, 1U, n);
1103
1104 ret = get_user_pages_fast(user_addr, npages, !write,
1105 &req->pages[req->num_pages]);
1106 if (ret < 0)
1107 return ret;
1108
1109 npages = ret;
1110 frag_size = min_t(size_t, frag_size,
1111 (npages << PAGE_SHIFT) - offset);
1112 iov_iter_advance(ii, frag_size);
1113
1114 req->page_descs[req->num_pages].offset = offset;
1115 fuse_page_descs_length_init(req, req->num_pages, npages);
1116
1117 req->num_pages += npages;
1118 req->page_descs[req->num_pages - 1].length -=
1119 (npages << PAGE_SHIFT) - offset - frag_size;
1120
1121 nbytes += frag_size;
1122 }
1093 1123
1094 if (write) 1124 if (write)
1095 req->in.argpages = 1; 1125 req->in.argpages = 1;
1096 else 1126 else
1097 req->out.argpages = 1; 1127 req->out.argpages = 1;
1098 1128
1099 nbytes = (req->num_pages << PAGE_SHIFT) - req->page_descs[0].offset; 1129 *nbytesp = nbytes;
1100
1101 if (frag_size < nbytes)
1102 req->page_descs[req->num_pages - 1].length -=
1103 nbytes - frag_size;
1104
1105 *nbytesp = min(frag_size, nbytes);
1106 iov_iter_advance(ii, *nbytesp);
1107 1130
1108 return 0; 1131 return 0;
1109} 1132}
@@ -1948,7 +1971,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
1948 } 1971 }
1949 memcpy(req->pages, pages, sizeof(req->pages[0]) * num_pages); 1972 memcpy(req->pages, pages, sizeof(req->pages[0]) * num_pages);
1950 req->num_pages = num_pages; 1973 req->num_pages = num_pages;
1951 fuse_page_descs_length_init(req); 1974 fuse_page_descs_length_init(req, 0, req->num_pages);
1952 1975
1953 /* okay, let's send it to the client */ 1976 /* okay, let's send it to the client */
1954 req->in.h.opcode = FUSE_IOCTL; 1977 req->in.h.opcode = FUSE_IOCTL;