diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2015-04-01 19:57:53 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2015-04-11 22:28:25 -0400 |
commit | 4f3b35c157e43107cc7e1f1aa06694e8b22e10bb (patch) | |
tree | 2e871c8698529878f93c225b818f4aa4b871f3c6 /net/9p | |
parent | 6e242a1ceeb1bcf55ffefa84d3079f711fe8a667 (diff) |
net/9p: switch the guts of p9_client_{read,write}() to iov_iter
... and have get_user_pages_fast() mapping fewer pages than requested
to generate a short read/write.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'net/9p')
-rw-r--r-- | net/9p/client.c | 117 | ||||
-rw-r--r-- | net/9p/protocol.c | 24 | ||||
-rw-r--r-- | net/9p/trans_virtio.c | 137 |
3 files changed, 146 insertions, 132 deletions
diff --git a/net/9p/client.c b/net/9p/client.c index e86a9bea1d16..9ef5d85f082f 100644 --- a/net/9p/client.c +++ b/net/9p/client.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/slab.h> | 34 | #include <linux/slab.h> |
35 | #include <linux/sched.h> | 35 | #include <linux/sched.h> |
36 | #include <linux/uaccess.h> | 36 | #include <linux/uaccess.h> |
37 | #include <linux/uio.h> | ||
37 | #include <net/9p/9p.h> | 38 | #include <net/9p/9p.h> |
38 | #include <linux/parser.h> | 39 | #include <linux/parser.h> |
39 | #include <net/9p/client.h> | 40 | #include <net/9p/client.h> |
@@ -555,7 +556,7 @@ out_err: | |||
555 | */ | 556 | */ |
556 | 557 | ||
557 | static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req, | 558 | static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req, |
558 | char *uidata, int in_hdrlen, int kern_buf) | 559 | struct iov_iter *uidata, int in_hdrlen) |
559 | { | 560 | { |
560 | int err; | 561 | int err; |
561 | int ecode; | 562 | int ecode; |
@@ -591,16 +592,11 @@ static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req, | |||
591 | ename = &req->rc->sdata[req->rc->offset]; | 592 | ename = &req->rc->sdata[req->rc->offset]; |
592 | if (len > inline_len) { | 593 | if (len > inline_len) { |
593 | /* We have error in external buffer */ | 594 | /* We have error in external buffer */ |
594 | if (kern_buf) { | 595 | err = copy_from_iter(ename + inline_len, |
595 | memcpy(ename + inline_len, uidata, | 596 | len - inline_len, uidata); |
596 | len - inline_len); | 597 | if (err != len - inline_len) { |
597 | } else { | 598 | err = -EFAULT; |
598 | err = copy_from_user(ename + inline_len, | 599 | goto out_err; |
599 | uidata, len - inline_len); | ||
600 | if (err) { | ||
601 | err = -EFAULT; | ||
602 | goto out_err; | ||
603 | } | ||
604 | } | 600 | } |
605 | } | 601 | } |
606 | ename = NULL; | 602 | ename = NULL; |
@@ -806,8 +802,8 @@ reterr: | |||
806 | * p9_client_zc_rpc - issue a request and wait for a response | 802 | * p9_client_zc_rpc - issue a request and wait for a response |
807 | * @c: client session | 803 | * @c: client session |
808 | * @type: type of request | 804 | * @type: type of request |
809 | * @uidata: user bffer that should be ued for zero copy read | 805 | * @uidata: destination for zero copy read |
810 | * @uodata: user buffer that shoud be user for zero copy write | 806 | * @uodata: source for zero copy write |
811 | * @inlen: read buffer size | 807 | * @inlen: read buffer size |
812 | * @olen: write buffer size | 808 | * @olen: write buffer size |
813 | * @hdrlen: reader header size, This is the size of response protocol data | 809 | * @hdrlen: reader header size, This is the size of response protocol data |
@@ -816,9 +812,10 @@ reterr: | |||
816 | * Returns request structure (which client must free using p9_free_req) | 812 | * Returns request structure (which client must free using p9_free_req) |
817 | */ | 813 | */ |
818 | static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, | 814 | static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, |
819 | char *uidata, char *uodata, | 815 | struct iov_iter *uidata, |
816 | struct iov_iter *uodata, | ||
820 | int inlen, int olen, int in_hdrlen, | 817 | int inlen, int olen, int in_hdrlen, |
821 | int kern_buf, const char *fmt, ...) | 818 | const char *fmt, ...) |
822 | { | 819 | { |
823 | va_list ap; | 820 | va_list ap; |
824 | int sigpending, err; | 821 | int sigpending, err; |
@@ -841,12 +838,8 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, | |||
841 | } else | 838 | } else |
842 | sigpending = 0; | 839 | sigpending = 0; |
843 | 840 | ||
844 | /* If we are called with KERNEL_DS force kern_buf */ | ||
845 | if (segment_eq(get_fs(), KERNEL_DS)) | ||
846 | kern_buf = 1; | ||
847 | |||
848 | err = c->trans_mod->zc_request(c, req, uidata, uodata, | 841 | err = c->trans_mod->zc_request(c, req, uidata, uodata, |
849 | inlen, olen, in_hdrlen, kern_buf); | 842 | inlen, olen, in_hdrlen); |
850 | if (err < 0) { | 843 | if (err < 0) { |
851 | if (err == -EIO) | 844 | if (err == -EIO) |
852 | c->status = Disconnected; | 845 | c->status = Disconnected; |
@@ -876,7 +869,7 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, | |||
876 | if (err < 0) | 869 | if (err < 0) |
877 | goto reterr; | 870 | goto reterr; |
878 | 871 | ||
879 | err = p9_check_zc_errors(c, req, uidata, in_hdrlen, kern_buf); | 872 | err = p9_check_zc_errors(c, req, uidata, in_hdrlen); |
880 | trace_9p_client_res(c, type, req->rc->tag, err); | 873 | trace_9p_client_res(c, type, req->rc->tag, err); |
881 | if (!err) | 874 | if (!err) |
882 | return req; | 875 | return req; |
@@ -1545,11 +1538,24 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, | |||
1545 | u32 count) | 1538 | u32 count) |
1546 | { | 1539 | { |
1547 | char *dataptr; | 1540 | char *dataptr; |
1548 | int kernel_buf = 0; | ||
1549 | struct p9_req_t *req; | 1541 | struct p9_req_t *req; |
1550 | struct p9_client *clnt; | 1542 | struct p9_client *clnt; |
1551 | int err, rsize, non_zc = 0; | 1543 | int err, rsize, non_zc = 0; |
1552 | 1544 | struct iov_iter to; | |
1545 | union { | ||
1546 | struct kvec kv; | ||
1547 | struct iovec iov; | ||
1548 | } v; | ||
1549 | |||
1550 | if (data) { | ||
1551 | v.kv.iov_base = data; | ||
1552 | v.kv.iov_len = count; | ||
1553 | iov_iter_kvec(&to, ITER_KVEC | READ, &v.kv, 1, count); | ||
1554 | } else { | ||
1555 | v.iov.iov_base = udata; | ||
1556 | v.iov.iov_len = count; | ||
1557 | iov_iter_init(&to, READ, &v.iov, 1, count); | ||
1558 | } | ||
1553 | 1559 | ||
1554 | p9_debug(P9_DEBUG_9P, ">>> TREAD fid %d offset %llu %d\n", | 1560 | p9_debug(P9_DEBUG_9P, ">>> TREAD fid %d offset %llu %d\n", |
1555 | fid->fid, (unsigned long long) offset, count); | 1561 | fid->fid, (unsigned long long) offset, count); |
@@ -1565,18 +1571,12 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, | |||
1565 | 1571 | ||
1566 | /* Don't bother zerocopy for small IO (< 1024) */ | 1572 | /* Don't bother zerocopy for small IO (< 1024) */ |
1567 | if (clnt->trans_mod->zc_request && rsize > 1024) { | 1573 | if (clnt->trans_mod->zc_request && rsize > 1024) { |
1568 | char *indata; | ||
1569 | if (data) { | ||
1570 | kernel_buf = 1; | ||
1571 | indata = data; | ||
1572 | } else | ||
1573 | indata = (__force char *)udata; | ||
1574 | /* | 1574 | /* |
1575 | * response header len is 11 | 1575 | * response header len is 11 |
1576 | * PDU Header(7) + IO Size (4) | 1576 | * PDU Header(7) + IO Size (4) |
1577 | */ | 1577 | */ |
1578 | req = p9_client_zc_rpc(clnt, P9_TREAD, indata, NULL, rsize, 0, | 1578 | req = p9_client_zc_rpc(clnt, P9_TREAD, &to, NULL, rsize, 0, |
1579 | 11, kernel_buf, "dqd", fid->fid, | 1579 | 11, "dqd", fid->fid, |
1580 | offset, rsize); | 1580 | offset, rsize); |
1581 | } else { | 1581 | } else { |
1582 | non_zc = 1; | 1582 | non_zc = 1; |
@@ -1596,16 +1596,9 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, | |||
1596 | 1596 | ||
1597 | p9_debug(P9_DEBUG_9P, "<<< RREAD count %d\n", count); | 1597 | p9_debug(P9_DEBUG_9P, "<<< RREAD count %d\n", count); |
1598 | 1598 | ||
1599 | if (non_zc) { | 1599 | if (non_zc && copy_to_iter(dataptr, count, &to) != count) { |
1600 | if (data) { | 1600 | err = -EFAULT; |
1601 | memmove(data, dataptr, count); | 1601 | goto free_and_error; |
1602 | } else { | ||
1603 | err = copy_to_user(udata, dataptr, count); | ||
1604 | if (err) { | ||
1605 | err = -EFAULT; | ||
1606 | goto free_and_error; | ||
1607 | } | ||
1608 | } | ||
1609 | } | 1602 | } |
1610 | p9_free_req(clnt, req); | 1603 | p9_free_req(clnt, req); |
1611 | return count; | 1604 | return count; |
@@ -1622,9 +1615,23 @@ p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, | |||
1622 | u64 offset, u32 count) | 1615 | u64 offset, u32 count) |
1623 | { | 1616 | { |
1624 | int err, rsize; | 1617 | int err, rsize; |
1625 | int kernel_buf = 0; | ||
1626 | struct p9_client *clnt; | 1618 | struct p9_client *clnt; |
1627 | struct p9_req_t *req; | 1619 | struct p9_req_t *req; |
1620 | struct iov_iter from; | ||
1621 | union { | ||
1622 | struct kvec kv; | ||
1623 | struct iovec iov; | ||
1624 | } v; | ||
1625 | |||
1626 | if (data) { | ||
1627 | v.kv.iov_base = data; | ||
1628 | v.kv.iov_len = count; | ||
1629 | iov_iter_kvec(&from, ITER_KVEC | WRITE, &v.kv, 1, count); | ||
1630 | } else { | ||
1631 | v.iov.iov_base = udata; | ||
1632 | v.iov.iov_len = count; | ||
1633 | iov_iter_init(&from, WRITE, &v.iov, 1, count); | ||
1634 | } | ||
1628 | 1635 | ||
1629 | p9_debug(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %d\n", | 1636 | p9_debug(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %d\n", |
1630 | fid->fid, (unsigned long long) offset, count); | 1637 | fid->fid, (unsigned long long) offset, count); |
@@ -1640,22 +1647,12 @@ p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, | |||
1640 | 1647 | ||
1641 | /* Don't bother zerocopy for small IO (< 1024) */ | 1648 | /* Don't bother zerocopy for small IO (< 1024) */ |
1642 | if (clnt->trans_mod->zc_request && rsize > 1024) { | 1649 | if (clnt->trans_mod->zc_request && rsize > 1024) { |
1643 | char *odata; | 1650 | req = p9_client_zc_rpc(clnt, P9_TWRITE, NULL, &from, 0, rsize, |
1644 | if (data) { | 1651 | P9_ZC_HDR_SZ, "dqd", |
1645 | kernel_buf = 1; | ||
1646 | odata = data; | ||
1647 | } else | ||
1648 | odata = (char *)udata; | ||
1649 | req = p9_client_zc_rpc(clnt, P9_TWRITE, NULL, odata, 0, rsize, | ||
1650 | P9_ZC_HDR_SZ, kernel_buf, "dqd", | ||
1651 | fid->fid, offset, rsize); | 1652 | fid->fid, offset, rsize); |
1652 | } else { | 1653 | } else { |
1653 | if (data) | 1654 | req = p9_client_rpc(clnt, P9_TWRITE, "dqV", fid->fid, |
1654 | req = p9_client_rpc(clnt, P9_TWRITE, "dqD", fid->fid, | 1655 | offset, rsize, &from); |
1655 | offset, rsize, data); | ||
1656 | else | ||
1657 | req = p9_client_rpc(clnt, P9_TWRITE, "dqU", fid->fid, | ||
1658 | offset, rsize, udata); | ||
1659 | } | 1656 | } |
1660 | if (IS_ERR(req)) { | 1657 | if (IS_ERR(req)) { |
1661 | err = PTR_ERR(req); | 1658 | err = PTR_ERR(req); |
@@ -2068,6 +2065,10 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) | |||
2068 | struct p9_client *clnt; | 2065 | struct p9_client *clnt; |
2069 | struct p9_req_t *req; | 2066 | struct p9_req_t *req; |
2070 | char *dataptr; | 2067 | char *dataptr; |
2068 | struct kvec kv = {.iov_base = data, .iov_len = count}; | ||
2069 | struct iov_iter to; | ||
2070 | |||
2071 | iov_iter_kvec(&to, READ | ITER_KVEC, &kv, 1, count); | ||
2071 | 2072 | ||
2072 | p9_debug(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %d\n", | 2073 | p9_debug(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %d\n", |
2073 | fid->fid, (unsigned long long) offset, count); | 2074 | fid->fid, (unsigned long long) offset, count); |
@@ -2088,8 +2089,8 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) | |||
2088 | * response header len is 11 | 2089 | * response header len is 11 |
2089 | * PDU Header(7) + IO Size (4) | 2090 | * PDU Header(7) + IO Size (4) |
2090 | */ | 2091 | */ |
2091 | req = p9_client_zc_rpc(clnt, P9_TREADDIR, data, NULL, rsize, 0, | 2092 | req = p9_client_zc_rpc(clnt, P9_TREADDIR, &to, NULL, rsize, 0, |
2092 | 11, 1, "dqd", fid->fid, offset, rsize); | 2093 | 11, "dqd", fid->fid, offset, rsize); |
2093 | } else { | 2094 | } else { |
2094 | non_zc = 1; | 2095 | non_zc = 1; |
2095 | req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, | 2096 | req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, |
diff --git a/net/9p/protocol.c b/net/9p/protocol.c index ab9127ec5b7a..e9d0f0c1a048 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/sched.h> | 33 | #include <linux/sched.h> |
34 | #include <linux/stddef.h> | 34 | #include <linux/stddef.h> |
35 | #include <linux/types.h> | 35 | #include <linux/types.h> |
36 | #include <linux/uio.h> | ||
36 | #include <net/9p/9p.h> | 37 | #include <net/9p/9p.h> |
37 | #include <net/9p/client.h> | 38 | #include <net/9p/client.h> |
38 | #include "protocol.h" | 39 | #include "protocol.h" |
@@ -69,10 +70,11 @@ static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size) | |||
69 | } | 70 | } |
70 | 71 | ||
71 | static size_t | 72 | static size_t |
72 | pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size) | 73 | pdu_write_u(struct p9_fcall *pdu, struct iov_iter *from, size_t size) |
73 | { | 74 | { |
74 | size_t len = min(pdu->capacity - pdu->size, size); | 75 | size_t len = min(pdu->capacity - pdu->size, size); |
75 | if (copy_from_user(&pdu->sdata[pdu->size], udata, len)) | 76 | struct iov_iter i = *from; |
77 | if (copy_from_iter(&pdu->sdata[pdu->size], len, &i) != len) | ||
76 | len = 0; | 78 | len = 0; |
77 | 79 | ||
78 | pdu->size += len; | 80 | pdu->size += len; |
@@ -437,23 +439,13 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, | |||
437 | stbuf->extension, stbuf->n_uid, | 439 | stbuf->extension, stbuf->n_uid, |
438 | stbuf->n_gid, stbuf->n_muid); | 440 | stbuf->n_gid, stbuf->n_muid); |
439 | } break; | 441 | } break; |
440 | case 'D':{ | 442 | case 'V':{ |
441 | uint32_t count = va_arg(ap, uint32_t); | ||
442 | const void *data = va_arg(ap, const void *); | ||
443 | |||
444 | errcode = p9pdu_writef(pdu, proto_version, "d", | ||
445 | count); | ||
446 | if (!errcode && pdu_write(pdu, data, count)) | ||
447 | errcode = -EFAULT; | ||
448 | } | ||
449 | break; | ||
450 | case 'U':{ | ||
451 | int32_t count = va_arg(ap, int32_t); | 443 | int32_t count = va_arg(ap, int32_t); |
452 | const char __user *udata = | 444 | struct iov_iter *from = |
453 | va_arg(ap, const void __user *); | 445 | va_arg(ap, struct iov_iter *); |
454 | errcode = p9pdu_writef(pdu, proto_version, "d", | 446 | errcode = p9pdu_writef(pdu, proto_version, "d", |
455 | count); | 447 | count); |
456 | if (!errcode && pdu_write_u(pdu, udata, count)) | 448 | if (!errcode && pdu_write_u(pdu, from, count)) |
457 | errcode = -EFAULT; | 449 | errcode = -EFAULT; |
458 | } | 450 | } |
459 | break; | 451 | break; |
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 36a1a739ad68..e62bcbbabb5e 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c | |||
@@ -217,15 +217,15 @@ static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req) | |||
217 | * @start: which segment of the sg_list to start at | 217 | * @start: which segment of the sg_list to start at |
218 | * @pdata: a list of pages to add into sg. | 218 | * @pdata: a list of pages to add into sg. |
219 | * @nr_pages: number of pages to pack into the scatter/gather list | 219 | * @nr_pages: number of pages to pack into the scatter/gather list |
220 | * @data: data to pack into scatter/gather list | 220 | * @offs: amount of data in the beginning of first page _not_ to pack |
221 | * @count: amount of data to pack into the scatter/gather list | 221 | * @count: amount of data to pack into the scatter/gather list |
222 | */ | 222 | */ |
223 | static int | 223 | static int |
224 | pack_sg_list_p(struct scatterlist *sg, int start, int limit, | 224 | pack_sg_list_p(struct scatterlist *sg, int start, int limit, |
225 | struct page **pdata, int nr_pages, char *data, int count) | 225 | struct page **pdata, int nr_pages, size_t offs, int count) |
226 | { | 226 | { |
227 | int i = 0, s; | 227 | int i = 0, s; |
228 | int data_off; | 228 | int data_off = offs; |
229 | int index = start; | 229 | int index = start; |
230 | 230 | ||
231 | BUG_ON(nr_pages > (limit - start)); | 231 | BUG_ON(nr_pages > (limit - start)); |
@@ -233,16 +233,14 @@ pack_sg_list_p(struct scatterlist *sg, int start, int limit, | |||
233 | * if the first page doesn't start at | 233 | * if the first page doesn't start at |
234 | * page boundary find the offset | 234 | * page boundary find the offset |
235 | */ | 235 | */ |
236 | data_off = offset_in_page(data); | ||
237 | while (nr_pages) { | 236 | while (nr_pages) { |
238 | s = rest_of_page(data); | 237 | s = PAGE_SIZE - data_off; |
239 | if (s > count) | 238 | if (s > count) |
240 | s = count; | 239 | s = count; |
241 | /* Make sure we don't terminate early. */ | 240 | /* Make sure we don't terminate early. */ |
242 | sg_unmark_end(&sg[index]); | 241 | sg_unmark_end(&sg[index]); |
243 | sg_set_page(&sg[index++], pdata[i++], s, data_off); | 242 | sg_set_page(&sg[index++], pdata[i++], s, data_off); |
244 | data_off = 0; | 243 | data_off = 0; |
245 | data += s; | ||
246 | count -= s; | 244 | count -= s; |
247 | nr_pages--; | 245 | nr_pages--; |
248 | } | 246 | } |
@@ -314,11 +312,20 @@ req_retry: | |||
314 | } | 312 | } |
315 | 313 | ||
316 | static int p9_get_mapped_pages(struct virtio_chan *chan, | 314 | static int p9_get_mapped_pages(struct virtio_chan *chan, |
317 | struct page **pages, char *data, | 315 | struct page ***pages, |
318 | int nr_pages, int write, int kern_buf) | 316 | struct iov_iter *data, |
317 | int count, | ||
318 | size_t *offs, | ||
319 | int *need_drop) | ||
319 | { | 320 | { |
321 | int nr_pages; | ||
320 | int err; | 322 | int err; |
321 | if (!kern_buf) { | 323 | |
324 | if (!iov_iter_count(data)) | ||
325 | return 0; | ||
326 | |||
327 | if (!(data->type & ITER_KVEC)) { | ||
328 | int n; | ||
322 | /* | 329 | /* |
323 | * We allow only p9_max_pages pinned. We wait for the | 330 | * We allow only p9_max_pages pinned. We wait for the |
324 | * Other zc request to finish here | 331 | * Other zc request to finish here |
@@ -329,26 +336,49 @@ static int p9_get_mapped_pages(struct virtio_chan *chan, | |||
329 | if (err == -ERESTARTSYS) | 336 | if (err == -ERESTARTSYS) |
330 | return err; | 337 | return err; |
331 | } | 338 | } |
332 | err = p9_payload_gup(data, &nr_pages, pages, write); | 339 | n = iov_iter_get_pages_alloc(data, pages, count, offs); |
333 | if (err < 0) | 340 | if (n < 0) |
334 | return err; | 341 | return n; |
342 | *need_drop = 1; | ||
343 | nr_pages = DIV_ROUND_UP(n + *offs, PAGE_SIZE); | ||
335 | atomic_add(nr_pages, &vp_pinned); | 344 | atomic_add(nr_pages, &vp_pinned); |
345 | return n; | ||
336 | } else { | 346 | } else { |
337 | /* kernel buffer, no need to pin pages */ | 347 | /* kernel buffer, no need to pin pages */ |
338 | int s, index = 0; | 348 | int index; |
339 | int count = nr_pages; | 349 | size_t len; |
340 | while (nr_pages) { | 350 | void *p; |
341 | s = rest_of_page(data); | 351 | |
342 | if (is_vmalloc_addr(data)) | 352 | /* we'd already checked that it's non-empty */ |
343 | pages[index++] = vmalloc_to_page(data); | 353 | while (1) { |
354 | len = iov_iter_single_seg_count(data); | ||
355 | if (likely(len)) { | ||
356 | p = data->kvec->iov_base + data->iov_offset; | ||
357 | break; | ||
358 | } | ||
359 | iov_iter_advance(data, 0); | ||
360 | } | ||
361 | if (len > count) | ||
362 | len = count; | ||
363 | |||
364 | nr_pages = DIV_ROUND_UP((unsigned long)p + len, PAGE_SIZE) - | ||
365 | (unsigned long)p / PAGE_SIZE; | ||
366 | |||
367 | *pages = kmalloc(sizeof(struct page *) * nr_pages, GFP_NOFS); | ||
368 | if (!*pages) | ||
369 | return -ENOMEM; | ||
370 | |||
371 | *need_drop = 0; | ||
372 | p -= (*offs = (unsigned long)p % PAGE_SIZE); | ||
373 | for (index = 0; index < nr_pages; index++) { | ||
374 | if (is_vmalloc_addr(p)) | ||
375 | (*pages)[index] = vmalloc_to_page(p); | ||
344 | else | 376 | else |
345 | pages[index++] = kmap_to_page(data); | 377 | (*pages)[index] = kmap_to_page(p); |
346 | data += s; | 378 | p += PAGE_SIZE; |
347 | nr_pages--; | ||
348 | } | 379 | } |
349 | nr_pages = count; | 380 | return len; |
350 | } | 381 | } |
351 | return nr_pages; | ||
352 | } | 382 | } |
353 | 383 | ||
354 | /** | 384 | /** |
@@ -364,8 +394,8 @@ static int p9_get_mapped_pages(struct virtio_chan *chan, | |||
364 | */ | 394 | */ |
365 | static int | 395 | static int |
366 | p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req, | 396 | p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req, |
367 | char *uidata, char *uodata, int inlen, | 397 | struct iov_iter *uidata, struct iov_iter *uodata, |
368 | int outlen, int in_hdr_len, int kern_buf) | 398 | int inlen, int outlen, int in_hdr_len) |
369 | { | 399 | { |
370 | int in, out, err, out_sgs, in_sgs; | 400 | int in, out, err, out_sgs, in_sgs; |
371 | unsigned long flags; | 401 | unsigned long flags; |
@@ -373,41 +403,32 @@ p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req, | |||
373 | struct page **in_pages = NULL, **out_pages = NULL; | 403 | struct page **in_pages = NULL, **out_pages = NULL; |
374 | struct virtio_chan *chan = client->trans; | 404 | struct virtio_chan *chan = client->trans; |
375 | struct scatterlist *sgs[4]; | 405 | struct scatterlist *sgs[4]; |
406 | size_t offs; | ||
407 | int need_drop = 0; | ||
376 | 408 | ||
377 | p9_debug(P9_DEBUG_TRANS, "virtio request\n"); | 409 | p9_debug(P9_DEBUG_TRANS, "virtio request\n"); |
378 | 410 | ||
379 | if (uodata) { | 411 | if (uodata) { |
380 | out_nr_pages = p9_nr_pages(uodata, outlen); | 412 | int n = p9_get_mapped_pages(chan, &out_pages, uodata, |
381 | out_pages = kmalloc(sizeof(struct page *) * out_nr_pages, | 413 | outlen, &offs, &need_drop); |
382 | GFP_NOFS); | 414 | if (n < 0) |
383 | if (!out_pages) { | 415 | return n; |
384 | err = -ENOMEM; | 416 | out_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE); |
385 | goto err_out; | 417 | if (n != outlen) { |
386 | } | 418 | __le32 v = cpu_to_le32(n); |
387 | out_nr_pages = p9_get_mapped_pages(chan, out_pages, uodata, | 419 | memcpy(&req->tc->sdata[req->tc->size - 4], &v, 4); |
388 | out_nr_pages, 0, kern_buf); | 420 | outlen = n; |
389 | if (out_nr_pages < 0) { | ||
390 | err = out_nr_pages; | ||
391 | kfree(out_pages); | ||
392 | out_pages = NULL; | ||
393 | goto err_out; | ||
394 | } | 421 | } |
395 | } | 422 | } else if (uidata) { |
396 | if (uidata) { | 423 | int n = p9_get_mapped_pages(chan, &in_pages, uidata, |
397 | in_nr_pages = p9_nr_pages(uidata, inlen); | 424 | inlen, &offs, &need_drop); |
398 | in_pages = kmalloc(sizeof(struct page *) * in_nr_pages, | 425 | if (n < 0) |
399 | GFP_NOFS); | 426 | return n; |
400 | if (!in_pages) { | 427 | in_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE); |
401 | err = -ENOMEM; | 428 | if (n != inlen) { |
402 | goto err_out; | 429 | __le32 v = cpu_to_le32(n); |
403 | } | 430 | memcpy(&req->tc->sdata[req->tc->size - 4], &v, 4); |
404 | in_nr_pages = p9_get_mapped_pages(chan, in_pages, uidata, | 431 | inlen = n; |
405 | in_nr_pages, 1, kern_buf); | ||
406 | if (in_nr_pages < 0) { | ||
407 | err = in_nr_pages; | ||
408 | kfree(in_pages); | ||
409 | in_pages = NULL; | ||
410 | goto err_out; | ||
411 | } | 432 | } |
412 | } | 433 | } |
413 | req->status = REQ_STATUS_SENT; | 434 | req->status = REQ_STATUS_SENT; |
@@ -426,7 +447,7 @@ req_retry_pinned: | |||
426 | if (out_pages) { | 447 | if (out_pages) { |
427 | sgs[out_sgs++] = chan->sg + out; | 448 | sgs[out_sgs++] = chan->sg + out; |
428 | out += pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM, | 449 | out += pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM, |
429 | out_pages, out_nr_pages, uodata, outlen); | 450 | out_pages, out_nr_pages, offs, outlen); |
430 | } | 451 | } |
431 | 452 | ||
432 | /* | 453 | /* |
@@ -444,7 +465,7 @@ req_retry_pinned: | |||
444 | if (in_pages) { | 465 | if (in_pages) { |
445 | sgs[out_sgs + in_sgs++] = chan->sg + out + in; | 466 | sgs[out_sgs + in_sgs++] = chan->sg + out + in; |
446 | in += pack_sg_list_p(chan->sg, out + in, VIRTQUEUE_NUM, | 467 | in += pack_sg_list_p(chan->sg, out + in, VIRTQUEUE_NUM, |
447 | in_pages, in_nr_pages, uidata, inlen); | 468 | in_pages, in_nr_pages, offs, inlen); |
448 | } | 469 | } |
449 | 470 | ||
450 | BUG_ON(out_sgs + in_sgs > ARRAY_SIZE(sgs)); | 471 | BUG_ON(out_sgs + in_sgs > ARRAY_SIZE(sgs)); |
@@ -478,7 +499,7 @@ req_retry_pinned: | |||
478 | * Non kernel buffers are pinned, unpin them | 499 | * Non kernel buffers are pinned, unpin them |
479 | */ | 500 | */ |
480 | err_out: | 501 | err_out: |
481 | if (!kern_buf) { | 502 | if (need_drop) { |
482 | if (in_pages) { | 503 | if (in_pages) { |
483 | p9_release_pages(in_pages, in_nr_pages); | 504 | p9_release_pages(in_pages, in_nr_pages); |
484 | atomic_sub(in_nr_pages, &vp_pinned); | 505 | atomic_sub(in_nr_pages, &vp_pinned); |