diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-16 11:58:09 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-16 11:58:09 -0400 |
commit | 26a992dbc24e34cbdd03621d1c97ce571ad74e65 (patch) | |
tree | cbb3171eb715b7c1ed28ed3ca29f1f03165e2faa /net/9p | |
parent | abab012a52237693ae48a655ece30cacb2ce4cf7 (diff) | |
parent | 7c9e592e1f6a994d2903c9b055e488ec90f58159 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs: (46 commits)
fs/9p: Make the writeback_fid owned by root
fs/9p: Writeback dirty data before setattr
fs/9p: call vmtruncate before setattr 9p opeation
fs/9p: Properly update inode attributes on link
fs/9p: Prevent multiple inclusion of same header
fs/9p: Workaround vfs rename rehash bug
fs/9p: Mark directory inode invalid for many directory inode operations
fs/9p: Add . and .. dentry revalidation flag
fs/9p: mark inode attribute invalid on rename, unlink and setattr
fs/9p: Add support for marking inode attribute invalid
fs/9p: Initialize root inode number for dotl
fs/9p: Update link count correctly on different file system operations
fs/9p: Add drop_inode 9p callback
fs/9p: Add direct IO support in cached mode
fs/9p: Fix inode i_size update in file_write
fs/9p: set default readahead pages in cached mode
fs/9p: Move writeback fid to v9fs_inode
fs/9p: Add v9fs_inode
fs/9p: Don't set stat.st_blocks based on nrpages
fs/9p: Add inode hashing
...
Diffstat (limited to 'net/9p')
-rw-r--r-- | net/9p/Makefile | 1 | ||||
-rw-r--r-- | net/9p/client.c | 166 | ||||
-rw-r--r-- | net/9p/protocol.c | 44 | ||||
-rw-r--r-- | net/9p/trans_common.c | 97 | ||||
-rw-r--r-- | net/9p/trans_common.h | 32 | ||||
-rw-r--r-- | net/9p/trans_virtio.c | 129 |
6 files changed, 419 insertions, 50 deletions
diff --git a/net/9p/Makefile b/net/9p/Makefile index 198a640d53a6..a0874cc1f718 100644 --- a/net/9p/Makefile +++ b/net/9p/Makefile | |||
@@ -9,6 +9,7 @@ obj-$(CONFIG_NET_9P_RDMA) += 9pnet_rdma.o | |||
9 | util.o \ | 9 | util.o \ |
10 | protocol.o \ | 10 | protocol.o \ |
11 | trans_fd.o \ | 11 | trans_fd.o \ |
12 | trans_common.o \ | ||
12 | 13 | ||
13 | 9pnet_virtio-objs := \ | 14 | 9pnet_virtio-objs := \ |
14 | trans_virtio.o \ | 15 | trans_virtio.o \ |
diff --git a/net/9p/client.c b/net/9p/client.c index a848bca9fbff..347ec0cd2718 100644 --- a/net/9p/client.c +++ b/net/9p/client.c | |||
@@ -229,10 +229,23 @@ static struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag) | |||
229 | return ERR_PTR(-ENOMEM); | 229 | return ERR_PTR(-ENOMEM); |
230 | } | 230 | } |
231 | init_waitqueue_head(req->wq); | 231 | init_waitqueue_head(req->wq); |
232 | req->tc = kmalloc(sizeof(struct p9_fcall)+c->msize, | 232 | if ((c->trans_mod->pref & P9_TRANS_PREF_PAYLOAD_MASK) == |
233 | GFP_KERNEL); | 233 | P9_TRANS_PREF_PAYLOAD_SEP) { |
234 | req->rc = kmalloc(sizeof(struct p9_fcall)+c->msize, | 234 | int alloc_msize = min(c->msize, 4096); |
235 | GFP_KERNEL); | 235 | req->tc = kmalloc(sizeof(struct p9_fcall)+alloc_msize, |
236 | GFP_KERNEL); | ||
237 | req->tc->capacity = alloc_msize; | ||
238 | req->rc = kmalloc(sizeof(struct p9_fcall)+alloc_msize, | ||
239 | GFP_KERNEL); | ||
240 | req->rc->capacity = alloc_msize; | ||
241 | } else { | ||
242 | req->tc = kmalloc(sizeof(struct p9_fcall)+c->msize, | ||
243 | GFP_KERNEL); | ||
244 | req->tc->capacity = c->msize; | ||
245 | req->rc = kmalloc(sizeof(struct p9_fcall)+c->msize, | ||
246 | GFP_KERNEL); | ||
247 | req->rc->capacity = c->msize; | ||
248 | } | ||
236 | if ((!req->tc) || (!req->rc)) { | 249 | if ((!req->tc) || (!req->rc)) { |
237 | printk(KERN_ERR "Couldn't grow tag array\n"); | 250 | printk(KERN_ERR "Couldn't grow tag array\n"); |
238 | kfree(req->tc); | 251 | kfree(req->tc); |
@@ -243,9 +256,7 @@ static struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag) | |||
243 | return ERR_PTR(-ENOMEM); | 256 | return ERR_PTR(-ENOMEM); |
244 | } | 257 | } |
245 | req->tc->sdata = (char *) req->tc + sizeof(struct p9_fcall); | 258 | req->tc->sdata = (char *) req->tc + sizeof(struct p9_fcall); |
246 | req->tc->capacity = c->msize; | ||
247 | req->rc->sdata = (char *) req->rc + sizeof(struct p9_fcall); | 259 | req->rc->sdata = (char *) req->rc + sizeof(struct p9_fcall); |
248 | req->rc->capacity = c->msize; | ||
249 | } | 260 | } |
250 | 261 | ||
251 | p9pdu_reset(req->tc); | 262 | p9pdu_reset(req->tc); |
@@ -443,6 +454,7 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req) | |||
443 | { | 454 | { |
444 | int8_t type; | 455 | int8_t type; |
445 | int err; | 456 | int err; |
457 | int ecode; | ||
446 | 458 | ||
447 | err = p9_parse_header(req->rc, NULL, &type, NULL, 0); | 459 | err = p9_parse_header(req->rc, NULL, &type, NULL, 0); |
448 | if (err) { | 460 | if (err) { |
@@ -450,36 +462,53 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req) | |||
450 | return err; | 462 | return err; |
451 | } | 463 | } |
452 | 464 | ||
453 | if (type == P9_RERROR || type == P9_RLERROR) { | 465 | if (type != P9_RERROR && type != P9_RLERROR) |
454 | int ecode; | 466 | return 0; |
455 | |||
456 | if (!p9_is_proto_dotl(c)) { | ||
457 | char *ename; | ||
458 | 467 | ||
459 | err = p9pdu_readf(req->rc, c->proto_version, "s?d", | 468 | if (!p9_is_proto_dotl(c)) { |
460 | &ename, &ecode); | 469 | char *ename; |
461 | if (err) | 470 | |
462 | goto out_err; | 471 | if (req->tc->pbuf_size) { |
472 | /* Handle user buffers */ | ||
473 | size_t len = req->rc->size - req->rc->offset; | ||
474 | if (req->tc->pubuf) { | ||
475 | /* User Buffer */ | ||
476 | err = copy_from_user( | ||
477 | &req->rc->sdata[req->rc->offset], | ||
478 | req->tc->pubuf, len); | ||
479 | if (err) { | ||
480 | err = -EFAULT; | ||
481 | goto out_err; | ||
482 | } | ||
483 | } else { | ||
484 | /* Kernel Buffer */ | ||
485 | memmove(&req->rc->sdata[req->rc->offset], | ||
486 | req->tc->pkbuf, len); | ||
487 | } | ||
488 | } | ||
489 | err = p9pdu_readf(req->rc, c->proto_version, "s?d", | ||
490 | &ename, &ecode); | ||
491 | if (err) | ||
492 | goto out_err; | ||
463 | 493 | ||
464 | if (p9_is_proto_dotu(c)) | 494 | if (p9_is_proto_dotu(c)) |
465 | err = -ecode; | 495 | err = -ecode; |
466 | 496 | ||
467 | if (!err || !IS_ERR_VALUE(err)) { | 497 | if (!err || !IS_ERR_VALUE(err)) { |
468 | err = p9_errstr2errno(ename, strlen(ename)); | 498 | err = p9_errstr2errno(ename, strlen(ename)); |
469 | 499 | ||
470 | P9_DPRINTK(P9_DEBUG_9P, "<<< RERROR (%d) %s\n", -ecode, ename); | 500 | P9_DPRINTK(P9_DEBUG_9P, "<<< RERROR (%d) %s\n", -ecode, |
501 | ename); | ||
471 | 502 | ||
472 | kfree(ename); | 503 | kfree(ename); |
473 | } | ||
474 | } else { | ||
475 | err = p9pdu_readf(req->rc, c->proto_version, "d", &ecode); | ||
476 | err = -ecode; | ||
477 | |||
478 | P9_DPRINTK(P9_DEBUG_9P, "<<< RLERROR (%d)\n", -ecode); | ||
479 | } | 504 | } |
505 | } else { | ||
506 | err = p9pdu_readf(req->rc, c->proto_version, "d", &ecode); | ||
507 | err = -ecode; | ||
508 | |||
509 | P9_DPRINTK(P9_DEBUG_9P, "<<< RLERROR (%d)\n", -ecode); | ||
510 | } | ||
480 | 511 | ||
481 | } else | ||
482 | err = 0; | ||
483 | 512 | ||
484 | return err; | 513 | return err; |
485 | 514 | ||
@@ -1191,6 +1220,27 @@ error: | |||
1191 | } | 1220 | } |
1192 | EXPORT_SYMBOL(p9_client_fsync); | 1221 | EXPORT_SYMBOL(p9_client_fsync); |
1193 | 1222 | ||
1223 | int p9_client_sync_fs(struct p9_fid *fid) | ||
1224 | { | ||
1225 | int err = 0; | ||
1226 | struct p9_req_t *req; | ||
1227 | struct p9_client *clnt; | ||
1228 | |||
1229 | P9_DPRINTK(P9_DEBUG_9P, ">>> TSYNC_FS fid %d\n", fid->fid); | ||
1230 | |||
1231 | clnt = fid->clnt; | ||
1232 | req = p9_client_rpc(clnt, P9_TSYNCFS, "d", fid->fid); | ||
1233 | if (IS_ERR(req)) { | ||
1234 | err = PTR_ERR(req); | ||
1235 | goto error; | ||
1236 | } | ||
1237 | P9_DPRINTK(P9_DEBUG_9P, "<<< RSYNCFS fid %d\n", fid->fid); | ||
1238 | p9_free_req(clnt, req); | ||
1239 | error: | ||
1240 | return err; | ||
1241 | } | ||
1242 | EXPORT_SYMBOL(p9_client_sync_fs); | ||
1243 | |||
1194 | int p9_client_clunk(struct p9_fid *fid) | 1244 | int p9_client_clunk(struct p9_fid *fid) |
1195 | { | 1245 | { |
1196 | int err; | 1246 | int err; |
@@ -1270,7 +1320,15 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, | |||
1270 | if (count < rsize) | 1320 | if (count < rsize) |
1271 | rsize = count; | 1321 | rsize = count; |
1272 | 1322 | ||
1273 | req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset, rsize); | 1323 | /* Don't bother zerocopy form small IO (< 1024) */ |
1324 | if (((clnt->trans_mod->pref & P9_TRANS_PREF_PAYLOAD_MASK) == | ||
1325 | P9_TRANS_PREF_PAYLOAD_SEP) && (rsize > 1024)) { | ||
1326 | req = p9_client_rpc(clnt, P9_TREAD, "dqE", fid->fid, offset, | ||
1327 | rsize, data, udata); | ||
1328 | } else { | ||
1329 | req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset, | ||
1330 | rsize); | ||
1331 | } | ||
1274 | if (IS_ERR(req)) { | 1332 | if (IS_ERR(req)) { |
1275 | err = PTR_ERR(req); | 1333 | err = PTR_ERR(req); |
1276 | goto error; | 1334 | goto error; |
@@ -1284,13 +1342,15 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, | |||
1284 | 1342 | ||
1285 | P9_DPRINTK(P9_DEBUG_9P, "<<< RREAD count %d\n", count); | 1343 | P9_DPRINTK(P9_DEBUG_9P, "<<< RREAD count %d\n", count); |
1286 | 1344 | ||
1287 | if (data) { | 1345 | if (!req->tc->pbuf_size) { |
1288 | memmove(data, dataptr, count); | 1346 | if (data) { |
1289 | } else { | 1347 | memmove(data, dataptr, count); |
1290 | err = copy_to_user(udata, dataptr, count); | 1348 | } else { |
1291 | if (err) { | 1349 | err = copy_to_user(udata, dataptr, count); |
1292 | err = -EFAULT; | 1350 | if (err) { |
1293 | goto free_and_error; | 1351 | err = -EFAULT; |
1352 | goto free_and_error; | ||
1353 | } | ||
1294 | } | 1354 | } |
1295 | } | 1355 | } |
1296 | p9_free_req(clnt, req); | 1356 | p9_free_req(clnt, req); |
@@ -1323,12 +1383,21 @@ p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, | |||
1323 | 1383 | ||
1324 | if (count < rsize) | 1384 | if (count < rsize) |
1325 | rsize = count; | 1385 | rsize = count; |
1326 | if (data) | 1386 | |
1327 | req = p9_client_rpc(clnt, P9_TWRITE, "dqD", fid->fid, offset, | 1387 | /* Don't bother zerocopy form small IO (< 1024) */ |
1328 | rsize, data); | 1388 | if (((clnt->trans_mod->pref & P9_TRANS_PREF_PAYLOAD_MASK) == |
1329 | else | 1389 | P9_TRANS_PREF_PAYLOAD_SEP) && (rsize > 1024)) { |
1330 | req = p9_client_rpc(clnt, P9_TWRITE, "dqU", fid->fid, offset, | 1390 | req = p9_client_rpc(clnt, P9_TWRITE, "dqE", fid->fid, offset, |
1331 | rsize, udata); | 1391 | rsize, data, udata); |
1392 | } else { | ||
1393 | |||
1394 | if (data) | ||
1395 | req = p9_client_rpc(clnt, P9_TWRITE, "dqD", fid->fid, | ||
1396 | offset, rsize, data); | ||
1397 | else | ||
1398 | req = p9_client_rpc(clnt, P9_TWRITE, "dqU", fid->fid, | ||
1399 | offset, rsize, udata); | ||
1400 | } | ||
1332 | if (IS_ERR(req)) { | 1401 | if (IS_ERR(req)) { |
1333 | err = PTR_ERR(req); | 1402 | err = PTR_ERR(req); |
1334 | goto error; | 1403 | goto error; |
@@ -1716,7 +1785,14 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) | |||
1716 | if (count < rsize) | 1785 | if (count < rsize) |
1717 | rsize = count; | 1786 | rsize = count; |
1718 | 1787 | ||
1719 | req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, offset, rsize); | 1788 | if ((clnt->trans_mod->pref & P9_TRANS_PREF_PAYLOAD_MASK) == |
1789 | P9_TRANS_PREF_PAYLOAD_SEP) { | ||
1790 | req = p9_client_rpc(clnt, P9_TREADDIR, "dqF", fid->fid, | ||
1791 | offset, rsize, data); | ||
1792 | } else { | ||
1793 | req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, | ||
1794 | offset, rsize); | ||
1795 | } | ||
1720 | if (IS_ERR(req)) { | 1796 | if (IS_ERR(req)) { |
1721 | err = PTR_ERR(req); | 1797 | err = PTR_ERR(req); |
1722 | goto error; | 1798 | goto error; |
@@ -1730,7 +1806,7 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) | |||
1730 | 1806 | ||
1731 | P9_DPRINTK(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count); | 1807 | P9_DPRINTK(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count); |
1732 | 1808 | ||
1733 | if (data) | 1809 | if (!req->tc->pbuf_size && data) |
1734 | memmove(data, dataptr, count); | 1810 | memmove(data, dataptr, count); |
1735 | 1811 | ||
1736 | p9_free_req(clnt, req); | 1812 | p9_free_req(clnt, req); |
diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 1e308f210928..2ce515b859b3 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c | |||
@@ -114,6 +114,26 @@ pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size) | |||
114 | return size - len; | 114 | return size - len; |
115 | } | 115 | } |
116 | 116 | ||
117 | static size_t | ||
118 | pdu_write_urw(struct p9_fcall *pdu, const char *kdata, const char __user *udata, | ||
119 | size_t size) | ||
120 | { | ||
121 | BUG_ON(pdu->size > P9_IOHDRSZ); | ||
122 | pdu->pubuf = (char __user *)udata; | ||
123 | pdu->pkbuf = (char *)kdata; | ||
124 | pdu->pbuf_size = size; | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static size_t | ||
129 | pdu_write_readdir(struct p9_fcall *pdu, const char *kdata, size_t size) | ||
130 | { | ||
131 | BUG_ON(pdu->size > P9_READDIRHDRSZ); | ||
132 | pdu->pkbuf = (char *)kdata; | ||
133 | pdu->pbuf_size = size; | ||
134 | return 0; | ||
135 | } | ||
136 | |||
117 | /* | 137 | /* |
118 | b - int8_t | 138 | b - int8_t |
119 | w - int16_t | 139 | w - int16_t |
@@ -445,6 +465,25 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, | |||
445 | errcode = -EFAULT; | 465 | errcode = -EFAULT; |
446 | } | 466 | } |
447 | break; | 467 | break; |
468 | case 'E':{ | ||
469 | int32_t cnt = va_arg(ap, int32_t); | ||
470 | const char *k = va_arg(ap, const void *); | ||
471 | const char *u = va_arg(ap, const void *); | ||
472 | errcode = p9pdu_writef(pdu, proto_version, "d", | ||
473 | cnt); | ||
474 | if (!errcode && pdu_write_urw(pdu, k, u, cnt)) | ||
475 | errcode = -EFAULT; | ||
476 | } | ||
477 | break; | ||
478 | case 'F':{ | ||
479 | int32_t cnt = va_arg(ap, int32_t); | ||
480 | const char *k = va_arg(ap, const void *); | ||
481 | errcode = p9pdu_writef(pdu, proto_version, "d", | ||
482 | cnt); | ||
483 | if (!errcode && pdu_write_readdir(pdu, k, cnt)) | ||
484 | errcode = -EFAULT; | ||
485 | } | ||
486 | break; | ||
448 | case 'U':{ | 487 | case 'U':{ |
449 | int32_t count = va_arg(ap, int32_t); | 488 | int32_t count = va_arg(ap, int32_t); |
450 | const char __user *udata = | 489 | const char __user *udata = |
@@ -579,6 +618,7 @@ EXPORT_SYMBOL(p9stat_read); | |||
579 | 618 | ||
580 | int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type) | 619 | int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type) |
581 | { | 620 | { |
621 | pdu->id = type; | ||
582 | return p9pdu_writef(pdu, 0, "dbw", 0, type, tag); | 622 | return p9pdu_writef(pdu, 0, "dbw", 0, type, tag); |
583 | } | 623 | } |
584 | 624 | ||
@@ -606,6 +646,10 @@ void p9pdu_reset(struct p9_fcall *pdu) | |||
606 | { | 646 | { |
607 | pdu->offset = 0; | 647 | pdu->offset = 0; |
608 | pdu->size = 0; | 648 | pdu->size = 0; |
649 | pdu->private = NULL; | ||
650 | pdu->pubuf = NULL; | ||
651 | pdu->pkbuf = NULL; | ||
652 | pdu->pbuf_size = 0; | ||
609 | } | 653 | } |
610 | 654 | ||
611 | int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, | 655 | int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, |
diff --git a/net/9p/trans_common.c b/net/9p/trans_common.c new file mode 100644 index 000000000000..d62b9aa58df8 --- /dev/null +++ b/net/9p/trans_common.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * Copyright IBM Corporation, 2010 | ||
3 | * Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of version 2.1 of the GNU Lesser General Public License | ||
7 | * as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it would be useful, but | ||
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/slab.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <net/9p/9p.h> | ||
18 | #include <net/9p/client.h> | ||
19 | #include <linux/scatterlist.h> | ||
20 | #include "trans_common.h" | ||
21 | |||
22 | /** | ||
23 | * p9_release_req_pages - Release pages after the transaction. | ||
24 | * @*private: PDU's private page of struct trans_rpage_info | ||
25 | */ | ||
26 | void | ||
27 | p9_release_req_pages(struct trans_rpage_info *rpinfo) | ||
28 | { | ||
29 | int i = 0; | ||
30 | |||
31 | while (rpinfo->rp_data[i] && rpinfo->rp_nr_pages--) { | ||
32 | put_page(rpinfo->rp_data[i]); | ||
33 | i++; | ||
34 | } | ||
35 | } | ||
36 | EXPORT_SYMBOL(p9_release_req_pages); | ||
37 | |||
38 | /** | ||
39 | * p9_nr_pages - Return number of pages needed to accomodate the payload. | ||
40 | */ | ||
41 | int | ||
42 | p9_nr_pages(struct p9_req_t *req) | ||
43 | { | ||
44 | int start_page, end_page; | ||
45 | start_page = (unsigned long long)req->tc->pubuf >> PAGE_SHIFT; | ||
46 | end_page = ((unsigned long long)req->tc->pubuf + req->tc->pbuf_size + | ||
47 | PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
48 | return end_page - start_page; | ||
49 | } | ||
50 | EXPORT_SYMBOL(p9_nr_pages); | ||
51 | |||
52 | /** | ||
53 | * payload_gup - Translates user buffer into kernel pages and | ||
54 | * pins them either for read/write through get_user_pages_fast(). | ||
55 | * @req: Request to be sent to server. | ||
56 | * @pdata_off: data offset into the first page after translation (gup). | ||
57 | * @pdata_len: Total length of the IO. gup may not return requested # of pages. | ||
58 | * @nr_pages: number of pages to accomodate the payload | ||
59 | * @rw: Indicates if the pages are for read or write. | ||
60 | */ | ||
61 | int | ||
62 | p9_payload_gup(struct p9_req_t *req, size_t *pdata_off, int *pdata_len, | ||
63 | int nr_pages, u8 rw) | ||
64 | { | ||
65 | uint32_t first_page_bytes = 0; | ||
66 | uint32_t pdata_mapped_pages; | ||
67 | struct trans_rpage_info *rpinfo; | ||
68 | |||
69 | *pdata_off = (size_t)req->tc->pubuf & (PAGE_SIZE-1); | ||
70 | |||
71 | if (*pdata_off) | ||
72 | first_page_bytes = min((PAGE_SIZE - *pdata_off), | ||
73 | req->tc->pbuf_size); | ||
74 | |||
75 | rpinfo = req->tc->private; | ||
76 | pdata_mapped_pages = get_user_pages_fast((unsigned long)req->tc->pubuf, | ||
77 | nr_pages, rw, &rpinfo->rp_data[0]); | ||
78 | |||
79 | if (pdata_mapped_pages < 0) { | ||
80 | printk(KERN_ERR "get_user_pages_fast failed:%d udata:%p" | ||
81 | "nr_pages:%d\n", pdata_mapped_pages, | ||
82 | req->tc->pubuf, nr_pages); | ||
83 | pdata_mapped_pages = 0; | ||
84 | return -EIO; | ||
85 | } | ||
86 | rpinfo->rp_nr_pages = pdata_mapped_pages; | ||
87 | if (*pdata_off) { | ||
88 | *pdata_len = first_page_bytes; | ||
89 | *pdata_len += min((req->tc->pbuf_size - *pdata_len), | ||
90 | ((size_t)pdata_mapped_pages - 1) << PAGE_SHIFT); | ||
91 | } else { | ||
92 | *pdata_len = min(req->tc->pbuf_size, | ||
93 | (size_t)pdata_mapped_pages << PAGE_SHIFT); | ||
94 | } | ||
95 | return 0; | ||
96 | } | ||
97 | EXPORT_SYMBOL(p9_payload_gup); | ||
diff --git a/net/9p/trans_common.h b/net/9p/trans_common.h new file mode 100644 index 000000000000..76309223bb02 --- /dev/null +++ b/net/9p/trans_common.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Copyright IBM Corporation, 2010 | ||
3 | * Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of version 2.1 of the GNU Lesser General Public License | ||
7 | * as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it would be useful, but | ||
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | /* TRUE if it is user context */ | ||
16 | #define P9_IS_USER_CONTEXT (!segment_eq(get_fs(), KERNEL_DS)) | ||
17 | |||
18 | /** | ||
19 | * struct trans_rpage_info - To store mapped page information in PDU. | ||
20 | * @rp_alloc:Set if this structure is allocd, not a reuse unused space in pdu. | ||
21 | * @rp_nr_pages: Number of mapped pages | ||
22 | * @rp_data: Array of page pointers | ||
23 | */ | ||
24 | struct trans_rpage_info { | ||
25 | u8 rp_alloc; | ||
26 | int rp_nr_pages; | ||
27 | struct page *rp_data[0]; | ||
28 | }; | ||
29 | |||
30 | void p9_release_req_pages(struct trans_rpage_info *); | ||
31 | int p9_payload_gup(struct p9_req_t *, size_t *, int *, int, u8); | ||
32 | int p9_nr_pages(struct p9_req_t *); | ||
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index c8f3f72ab20e..9b550ed9c711 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #include <linux/scatterlist.h> | 45 | #include <linux/scatterlist.h> |
46 | #include <linux/virtio.h> | 46 | #include <linux/virtio.h> |
47 | #include <linux/virtio_9p.h> | 47 | #include <linux/virtio_9p.h> |
48 | #include "trans_common.h" | ||
48 | 49 | ||
49 | #define VIRTQUEUE_NUM 128 | 50 | #define VIRTQUEUE_NUM 128 |
50 | 51 | ||
@@ -155,6 +156,14 @@ static void req_done(struct virtqueue *vq) | |||
155 | rc->tag); | 156 | rc->tag); |
156 | req = p9_tag_lookup(chan->client, rc->tag); | 157 | req = p9_tag_lookup(chan->client, rc->tag); |
157 | req->status = REQ_STATUS_RCVD; | 158 | req->status = REQ_STATUS_RCVD; |
159 | if (req->tc->private) { | ||
160 | struct trans_rpage_info *rp = req->tc->private; | ||
161 | /*Release pages */ | ||
162 | p9_release_req_pages(rp); | ||
163 | if (rp->rp_alloc) | ||
164 | kfree(rp); | ||
165 | req->tc->private = NULL; | ||
166 | } | ||
158 | p9_client_cb(chan->client, req); | 167 | p9_client_cb(chan->client, req); |
159 | } else { | 168 | } else { |
160 | spin_unlock_irqrestore(&chan->lock, flags); | 169 | spin_unlock_irqrestore(&chan->lock, flags); |
@@ -203,6 +212,38 @@ static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req) | |||
203 | } | 212 | } |
204 | 213 | ||
205 | /** | 214 | /** |
215 | * pack_sg_list_p - Just like pack_sg_list. Instead of taking a buffer, | ||
216 | * this takes a list of pages. | ||
217 | * @sg: scatter/gather list to pack into | ||
218 | * @start: which segment of the sg_list to start at | ||
219 | * @pdata_off: Offset into the first page | ||
220 | * @**pdata: a list of pages to add into sg. | ||
221 | * @count: amount of data to pack into the scatter/gather list | ||
222 | */ | ||
223 | static int | ||
224 | pack_sg_list_p(struct scatterlist *sg, int start, int limit, size_t pdata_off, | ||
225 | struct page **pdata, int count) | ||
226 | { | ||
227 | int s; | ||
228 | int i = 0; | ||
229 | int index = start; | ||
230 | |||
231 | if (pdata_off) { | ||
232 | s = min((int)(PAGE_SIZE - pdata_off), count); | ||
233 | sg_set_page(&sg[index++], pdata[i++], s, pdata_off); | ||
234 | count -= s; | ||
235 | } | ||
236 | |||
237 | while (count) { | ||
238 | BUG_ON(index > limit); | ||
239 | s = min((int)PAGE_SIZE, count); | ||
240 | sg_set_page(&sg[index++], pdata[i++], s, 0); | ||
241 | count -= s; | ||
242 | } | ||
243 | return index-start; | ||
244 | } | ||
245 | |||
246 | /** | ||
206 | * p9_virtio_request - issue a request | 247 | * p9_virtio_request - issue a request |
207 | * @client: client instance issuing the request | 248 | * @client: client instance issuing the request |
208 | * @req: request to be issued | 249 | * @req: request to be issued |
@@ -212,22 +253,97 @@ static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req) | |||
212 | static int | 253 | static int |
213 | p9_virtio_request(struct p9_client *client, struct p9_req_t *req) | 254 | p9_virtio_request(struct p9_client *client, struct p9_req_t *req) |
214 | { | 255 | { |
215 | int in, out; | 256 | int in, out, inp, outp; |
216 | struct virtio_chan *chan = client->trans; | 257 | struct virtio_chan *chan = client->trans; |
217 | char *rdata = (char *)req->rc+sizeof(struct p9_fcall); | 258 | char *rdata = (char *)req->rc+sizeof(struct p9_fcall); |
218 | unsigned long flags; | 259 | unsigned long flags; |
219 | int err; | 260 | size_t pdata_off = 0; |
261 | struct trans_rpage_info *rpinfo = NULL; | ||
262 | int err, pdata_len = 0; | ||
220 | 263 | ||
221 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request\n"); | 264 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request\n"); |
222 | 265 | ||
223 | req_retry: | 266 | req_retry: |
224 | req->status = REQ_STATUS_SENT; | 267 | req->status = REQ_STATUS_SENT; |
225 | 268 | ||
269 | if (req->tc->pbuf_size && (req->tc->pubuf && P9_IS_USER_CONTEXT)) { | ||
270 | int nr_pages = p9_nr_pages(req); | ||
271 | int rpinfo_size = sizeof(struct trans_rpage_info) + | ||
272 | sizeof(struct page *) * nr_pages; | ||
273 | |||
274 | if (rpinfo_size <= (req->tc->capacity - req->tc->size)) { | ||
275 | /* We can use sdata */ | ||
276 | req->tc->private = req->tc->sdata + req->tc->size; | ||
277 | rpinfo = (struct trans_rpage_info *)req->tc->private; | ||
278 | rpinfo->rp_alloc = 0; | ||
279 | } else { | ||
280 | req->tc->private = kmalloc(rpinfo_size, GFP_NOFS); | ||
281 | if (!req->tc->private) { | ||
282 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: " | ||
283 | "private kmalloc returned NULL"); | ||
284 | return -ENOMEM; | ||
285 | } | ||
286 | rpinfo = (struct trans_rpage_info *)req->tc->private; | ||
287 | rpinfo->rp_alloc = 1; | ||
288 | } | ||
289 | |||
290 | err = p9_payload_gup(req, &pdata_off, &pdata_len, nr_pages, | ||
291 | req->tc->id == P9_TREAD ? 1 : 0); | ||
292 | if (err < 0) { | ||
293 | if (rpinfo->rp_alloc) | ||
294 | kfree(rpinfo); | ||
295 | return err; | ||
296 | } | ||
297 | } | ||
298 | |||
226 | spin_lock_irqsave(&chan->lock, flags); | 299 | spin_lock_irqsave(&chan->lock, flags); |
300 | |||
301 | /* Handle out VirtIO ring buffers */ | ||
227 | out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, req->tc->sdata, | 302 | out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, req->tc->sdata, |
228 | req->tc->size); | 303 | req->tc->size); |
229 | in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata, | 304 | |
230 | client->msize); | 305 | if (req->tc->pbuf_size && (req->tc->id == P9_TWRITE)) { |
306 | /* We have additional write payload buffer to take care */ | ||
307 | if (req->tc->pubuf && P9_IS_USER_CONTEXT) { | ||
308 | outp = pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM, | ||
309 | pdata_off, rpinfo->rp_data, pdata_len); | ||
310 | } else { | ||
311 | char *pbuf = req->tc->pubuf ? req->tc->pubuf : | ||
312 | req->tc->pkbuf; | ||
313 | outp = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM, pbuf, | ||
314 | req->tc->pbuf_size); | ||
315 | } | ||
316 | out += outp; | ||
317 | } | ||
318 | |||
319 | /* Handle in VirtIO ring buffers */ | ||
320 | if (req->tc->pbuf_size && | ||
321 | ((req->tc->id == P9_TREAD) || (req->tc->id == P9_TREADDIR))) { | ||
322 | /* | ||
323 | * Take care of additional Read payload. | ||
324 | * 11 is the read/write header = PDU Header(7) + IO Size (4). | ||
325 | * Arrange in such a way that server places header in the | ||
326 | * alloced memory and payload onto the user buffer. | ||
327 | */ | ||
328 | inp = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM, rdata, 11); | ||
329 | /* | ||
330 | * Running executables in the filesystem may result in | ||
331 | * a read request with kernel buffer as opposed to user buffer. | ||
332 | */ | ||
333 | if (req->tc->pubuf && P9_IS_USER_CONTEXT) { | ||
334 | in = pack_sg_list_p(chan->sg, out+inp, VIRTQUEUE_NUM, | ||
335 | pdata_off, rpinfo->rp_data, pdata_len); | ||
336 | } else { | ||
337 | char *pbuf = req->tc->pubuf ? req->tc->pubuf : | ||
338 | req->tc->pkbuf; | ||
339 | in = pack_sg_list(chan->sg, out+inp, VIRTQUEUE_NUM, | ||
340 | pbuf, req->tc->pbuf_size); | ||
341 | } | ||
342 | in += inp; | ||
343 | } else { | ||
344 | in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM, rdata, | ||
345 | client->msize); | ||
346 | } | ||
231 | 347 | ||
232 | err = virtqueue_add_buf(chan->vq, chan->sg, out, in, req->tc); | 348 | err = virtqueue_add_buf(chan->vq, chan->sg, out, in, req->tc); |
233 | if (err < 0) { | 349 | if (err < 0) { |
@@ -246,6 +362,8 @@ req_retry: | |||
246 | P9_DPRINTK(P9_DEBUG_TRANS, | 362 | P9_DPRINTK(P9_DEBUG_TRANS, |
247 | "9p debug: " | 363 | "9p debug: " |
248 | "virtio rpc add_buf returned failure"); | 364 | "virtio rpc add_buf returned failure"); |
365 | if (rpinfo && rpinfo->rp_alloc) | ||
366 | kfree(rpinfo); | ||
249 | return -EIO; | 367 | return -EIO; |
250 | } | 368 | } |
251 | } | 369 | } |
@@ -448,6 +566,7 @@ static struct p9_trans_module p9_virtio_trans = { | |||
448 | .request = p9_virtio_request, | 566 | .request = p9_virtio_request, |
449 | .cancel = p9_virtio_cancel, | 567 | .cancel = p9_virtio_cancel, |
450 | .maxsize = PAGE_SIZE*16, | 568 | .maxsize = PAGE_SIZE*16, |
569 | .pref = P9_TRANS_PREF_PAYLOAD_SEP, | ||
451 | .def = 0, | 570 | .def = 0, |
452 | .owner = THIS_MODULE, | 571 | .owner = THIS_MODULE, |
453 | }; | 572 | }; |