diff options
author | Maxim Patlasov <mpatlasov@parallels.com> | 2012-10-26 11:50:29 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2013-01-24 10:21:27 -0500 |
commit | 7c190c8b9c0dd373cdd4d96e63306ec6e1a7115d (patch) | |
tree | 6814852e3df702e680781928ed5a0a8fae6bb9c9 /fs/fuse | |
parent | b98d023a24496bf7d538c549e5426b1173c6f55d (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.c | 79 |
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 | ||
1050 | static inline void fuse_page_descs_length_init(struct fuse_req *req) | 1050 | static 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 | ||
1060 | static 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 | |||
1065 | static 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 | |||
1059 | static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii, | 1071 | static 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; |