From patchwork Sun Aug 29 16:29:05 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Latchesar Ionkov X-Patchwork-Id: 140651 Received: from lists.sourceforge.net (lists.sourceforge.net [216.34.181.88]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o7TGTTCH025625 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Sun, 29 Aug 2010 16:30:05 GMT Received: from localhost ([127.0.0.1] helo=sfs-ml-2.v29.ch3.sourceforge.com) by sfs-ml-2.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1Opkkv-0006hm-QR; Sun, 29 Aug 2010 16:29:17 +0000 Received: from sog-mx-4.v43.ch3.sourceforge.com ([172.29.43.194] helo=mx.sourceforge.net) by sfs-ml-2.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1Opkku-0006hX-Lw for v9fs-developer@lists.sourceforge.net; Sun, 29 Aug 2010 16:29:16 +0000 Received-SPF: pass (sog-mx-4.v43.ch3.sourceforge.com: domain of gmail.com designates 209.85.214.175 as permitted sender) client-ip=209.85.214.175; envelope-from=lionkov@gmail.com; helo=mail-iw0-f175.google.com; Received: from mail-iw0-f175.google.com ([209.85.214.175]) by sog-mx-4.v43.ch3.sourceforge.com with esmtp (Exim 4.69) id 1Opkkt-0004rj-3E for v9fs-developer@lists.sourceforge.net; Sun, 29 Aug 2010 16:29:16 +0000 Received: by iwn2 with SMTP id 2so5181274iwn.34 for ; Sun, 29 Aug 2010 09:29:09 -0700 (PDT) Received: by 10.231.174.196 with SMTP id u4mr4106002ibz.19.1283099349783; Sun, 29 Aug 2010 09:29:09 -0700 (PDT) Received: from valinor (174-28-37-12.albq.qwest.net [174.28.37.12]) by mx.google.com with ESMTPS id g31sm6344471ibh.22.2010.08.29.09.29.07 (version=TLSv1/SSLv3 cipher=RC4-MD5); Sun, 29 Aug 2010 09:29:08 -0700 (PDT) Date: Sun, 29 Aug 2010 10:29:05 -0600 From: Latchesar Ionkov To: v9fs-developer@lists.sourceforge.net Message-ID: <20100829162904.GA3691@valinor> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.20 (2009-06-14) X-Spam-Score: -1.6 (-) X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -1.5 SPF_CHECK_PASS SPF reports sender host as permitted sender for sender-domain 0.0 FREEMAIL_FROM Sender email is freemail (lionkov[at]gmail.com) -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.214.175 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.0 T_TO_NO_BRKTS_FREEMAIL T_TO_NO_BRKTS_FREEMAIL X-Headers-End: 1Opkkt-0004rj-3E Subject: [V9fs-developer] [RFC] [PATCH] 9p/net: add zero copy support X-BeenThere: v9fs-developer@lists.sourceforge.net X-Mailman-Version: 2.1.9 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: v9fs-developer-bounces@lists.sourceforge.net X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Sun, 29 Aug 2010 16:30:06 +0000 (UTC) diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index a8de812..d2f5597 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -27,6 +27,8 @@ #ifndef NET_9P_H #define NET_9P_H +#include + /** * enum p9_debug_flags - bits for mount time debug parameter * @P9_DEBUG_ERROR: more verbose error messages including original error string @@ -653,8 +655,17 @@ struct p9_fcall { size_t capacity; uint8_t *sdata; + + uint8_t *pkt; /* area that holds the fcall header */ + int sgnum; /* number of entries in sgpkt */ + struct scatterlist *sgpkt; /* NULL if no sg is used */ }; +static inline int p9_fcall_has_sg(struct p9_fcall *fc) +{ + return fc->sgpkt != NULL; +} + struct p9_idpool; int p9_errstr2errno(char *errstr, int len); diff --git a/include/net/9p/transport.h b/include/net/9p/transport.h index 6d5886e..4912dc1 100644 --- a/include/net/9p/transport.h +++ b/include/net/9p/transport.h @@ -43,11 +43,16 @@ * BUGS: the transport module list isn't protected. */ +enum { + P9_TRANS_SG = 1, /* support for scatterlists */ +}; + struct p9_trans_module { struct list_head list; char *name; /* name of transport */ int maxsize; /* max message size of transport */ int def; /* this transport should be default */ + int flags; /* P9_TRANS_* flags */ struct module *owner; int (*create)(struct p9_client *, const char *, char *); void (*close) (struct p9_client *); @@ -60,4 +65,10 @@ void v9fs_unregister_trans(struct p9_trans_module *m); struct p9_trans_module *v9fs_get_trans_by_name(const substring_t *name); struct p9_trans_module *v9fs_get_default_trans(void); void v9fs_put_trans(struct p9_trans_module *m); + +static int inline p9_trans_sg_support(struct p9_trans_module *ts) +{ + return ts->flags & P9_TRANS_SG; +} + #endif /* NET_9P_TRANSPORT_H */ diff --git a/net/9p/client.c b/net/9p/client.c index 9eb7250..2a464ad 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -93,7 +93,7 @@ static int get_protocol_version(const substring_t *name) } static struct p9_req_t * -p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...); +p9_client_rpc(struct p9_client *c, int8_t type, int rcsg, const char *fmt, ...); /** * parse_options - parse mount options into client structure @@ -243,13 +243,11 @@ static struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag) return ERR_PTR(-ENOMEM); } req->tc->sdata = (char *) req->tc + sizeof(struct p9_fcall); - req->tc->capacity = c->msize; req->rc->sdata = (char *) req->rc + sizeof(struct p9_fcall); - req->rc->capacity = c->msize; } - p9pdu_reset(req->tc); - p9pdu_reset(req->rc); + p9pdu_reset(req->tc, c->msize); + p9pdu_reset(req->rc, c->msize); req->tc->tag = tag-1; req->status = REQ_STATUS_ALLOC; @@ -360,6 +358,11 @@ static void p9_free_req(struct p9_client *c, struct p9_req_t *r) int tag = r->tc->tag; P9_DPRINTK(P9_DEBUG_MUX, "clnt %p req %p tag: %d\n", c, r, tag); + if (p9_trans_sg_support(c->trans)) { + p9pdu_free_sg(r->tc); + p9pdu_free_sg(r->rc); + } + r->status = REQ_STATUS_IDLE; if (tag != P9_NOTAG && p9_idpool_check(tag, c->tagpool)) p9_idpool_put(tag, c->tagpool); @@ -428,6 +431,42 @@ rewind_and_exit: } EXPORT_SYMBOL(p9_parse_header); +static int p9_read_error_sg(struct p9_fcall *rc, int proto_version, + char **ename, int *ecode) +{ + int err, n; + int16_t len, count; + char *str; + + /* the string len is in the header */ + err = p9pdu_readf(rc, proto_version, "w", &len); + if (err < 0) + return err; + + count = len; + if ((proto_version == p9_proto_2000u) || + (proto_version == p9_proto_2000L)) + count += 4; + + str = kmalloc(count + 1, GFP_KERNEL); + if (!str) + return -ENOMEM; + + n = sg_copy_to_buffer(rc->sgpkt, rc->sgnum, str, count); + if (n != count) { + kfree(str); + return -EINVAL; + } + + if ((proto_version == p9_proto_2000u) || + (proto_version == p9_proto_2000L)) + *ecode = le32_to_cpu(*(__le32 *) &str[len]); + + str[len] = '\0'; + *ename = str; + return 0; +} + /** * p9_check_errors - check 9p packet for error return and process it * @c: current client instance @@ -453,9 +492,18 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req) if (type == P9_RERROR) { int ecode; char *ename; + struct p9_fcall *rc; - err = p9pdu_readf(req->rc, c->proto_version, "s?d", + /* error handling is complicated because of the + * Tread/Rerror case if scatterlist is used */ + rc = req->rc; + if (rc->sgpkt != NULL) + err = p9_read_error_sg(req->rc, c->proto_version, &ename, &ecode); + else + err = p9pdu_readf(req->rc, c->proto_version, "s?d", + &ename, &ecode); + if (err) { P9_DPRINTK(P9_DEBUG_ERROR, "couldn't parse error%d\n", err); @@ -502,7 +550,7 @@ static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq) P9_DPRINTK(P9_DEBUG_9P, ">>> TFLUSH tag %d\n", oldtag); - req = p9_client_rpc(c, P9_TFLUSH, "w", oldtag); + req = p9_client_rpc(c, P9_TFLUSH, 0, "w", oldtag); if (IS_ERR(req)) return PTR_ERR(req); @@ -522,13 +570,16 @@ static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq) * p9_client_rpc - issue a request and wait for a response * @c: client session * @type: type of request + * @rcsg: flag if the response would be read in a scatterlist + * in that case there are three additional parameters at the end + * of the va_list -- int32_t hdrsz, int32_t count, void __user *udata * @fmt: protocol format string (see protocol.c) * * Returns request structure (which client must free using p9_free_req) */ static struct p9_req_t * -p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) +p9_client_rpc(struct p9_client *c, int8_t type, int rcsg, const char *fmt, ...) { va_list ap; int tag, err; @@ -563,13 +614,27 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) if (IS_ERR(req)) return req; + /* set up the outgoing and incoming buffers */ + va_start(ap, fmt); + /* marshall the data */ p9pdu_prepare(req->tc, tag, type); - va_start(ap, fmt); err = p9pdu_vwritef(req->tc, c->proto_version, fmt, ap); - va_end(ap); p9pdu_finalize(req->tc); + /* prepare the incoming buffer */ + if (rcsg && p9_trans_sg_support(c->trans_mod)) { + int32_t hdrsz, count; + const void __user *udata; + + hdrsz = va_arg(ap, int32_t); + count = va_arg(ap, int32_t); + udata = va_arg(ap, const void __user *); + + p9pdu_prepare_sg(req->rc, hdrsz, count, 0, udata); + } + va_end(ap); + err = c->trans_mod->request(c, req); if (err < 0) { c->status = Disconnected; @@ -683,15 +748,15 @@ int p9_client_version(struct p9_client *c) switch (c->proto_version) { case p9_proto_2000L: - req = p9_client_rpc(c, P9_TVERSION, "ds", + req = p9_client_rpc(c, P9_TVERSION, 0, "ds", c->msize, "9P2000.L"); break; case p9_proto_2000u: - req = p9_client_rpc(c, P9_TVERSION, "ds", + req = p9_client_rpc(c, P9_TVERSION, 0, "ds", c->msize, "9P2000.u"); break; case p9_proto_legacy: - req = p9_client_rpc(c, P9_TVERSION, "ds", + req = p9_client_rpc(c, P9_TVERSION, 0, "ds", c->msize, "9P2000"); break; default: @@ -856,7 +921,7 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, goto error; } - req = p9_client_rpc(clnt, P9_TATTACH, "ddss?d", fid->fid, + req = p9_client_rpc(clnt, P9_TATTACH, 0, "ddss?d", fid->fid, afid ? afid->fid : P9_NOFID, uname, aname, n_uname); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -905,7 +970,7 @@ p9_client_auth(struct p9_client *clnt, char *uname, u32 n_uname, char *aname) goto error; } - req = p9_client_rpc(clnt, P9_TAUTH, "dss?d", + req = p9_client_rpc(clnt, P9_TAUTH, 0, "dss?d", afid ? afid->fid : P9_NOFID, uname, aname, n_uname); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -964,7 +1029,7 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames, P9_DPRINTK(P9_DEBUG_9P, ">>> TWALK fids %d,%d nwname %d wname[0] %s\n", oldfid->fid, fid->fid, nwname, wnames ? wnames[0] : NULL); - req = p9_client_rpc(clnt, P9_TWALK, "ddT", oldfid->fid, fid->fid, + req = p9_client_rpc(clnt, P9_TWALK, 0, "ddT", oldfid->fid, fid->fid, nwname, wnames); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -1030,9 +1095,9 @@ int p9_client_open(struct p9_fid *fid, int mode) return -EINVAL; if (p9_is_proto_dotl(clnt)) - req = p9_client_rpc(clnt, P9_TLOPEN, "dd", fid->fid, mode); + req = p9_client_rpc(clnt, P9_TLOPEN, 0, "dd", fid->fid, mode); else - req = p9_client_rpc(clnt, P9_TOPEN, "db", fid->fid, mode); + req = p9_client_rpc(clnt, P9_TOPEN, 0, "db", fid->fid, mode); if (IS_ERR(req)) { err = PTR_ERR(req); goto error; @@ -1074,8 +1139,8 @@ int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode, if (ofid->mode != -1) return -EINVAL; - req = p9_client_rpc(clnt, P9_TLCREATE, "dsddd", ofid->fid, name, flags, - mode, gid); + req = p9_client_rpc(clnt, P9_TLCREATE, 0, "dsddd", ofid->fid, name, + flags, mode, gid); if (IS_ERR(req)) { err = PTR_ERR(req); goto error; @@ -1119,7 +1184,7 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode, if (fid->mode != -1) return -EINVAL; - req = p9_client_rpc(clnt, P9_TCREATE, "dsdb?s", fid->fid, name, perm, + req = p9_client_rpc(clnt, P9_TCREATE, 0, "dsdb?s", fid->fid, name, perm, mode, extension); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -1158,8 +1223,8 @@ int p9_client_symlink(struct p9_fid *dfid, char *name, char *symtgt, gid_t gid, dfid->fid, name, symtgt); clnt = dfid->clnt; - req = p9_client_rpc(clnt, P9_TSYMLINK, "dssd", dfid->fid, name, symtgt, - gid); + req = p9_client_rpc(clnt, P9_TSYMLINK, 0, "dssd", dfid->fid, name, + symtgt, gid); if (IS_ERR(req)) { err = PTR_ERR(req); goto error; @@ -1189,7 +1254,7 @@ int p9_client_link(struct p9_fid *dfid, struct p9_fid *oldfid, char *newname) P9_DPRINTK(P9_DEBUG_9P, ">>> TLINK dfid %d oldfid %d newname %s\n", dfid->fid, oldfid->fid, newname); clnt = dfid->clnt; - req = p9_client_rpc(clnt, P9_TLINK, "dds", dfid->fid, oldfid->fid, + req = p9_client_rpc(clnt, P9_TLINK, 0, "dds", dfid->fid, oldfid->fid, newname); if (IS_ERR(req)) return PTR_ERR(req); @@ -1210,7 +1275,7 @@ int p9_client_clunk(struct p9_fid *fid) err = 0; clnt = fid->clnt; - req = p9_client_rpc(clnt, P9_TCLUNK, "d", fid->fid); + req = p9_client_rpc(clnt, P9_TCLUNK, 0, "d", fid->fid); if (IS_ERR(req)) { err = PTR_ERR(req); goto error; @@ -1236,7 +1301,7 @@ int p9_client_remove(struct p9_fid *fid) err = 0; clnt = fid->clnt; - req = p9_client_rpc(clnt, P9_TREMOVE, "d", fid->fid); + req = p9_client_rpc(clnt, P9_TREMOVE, 0, "d", fid->fid); if (IS_ERR(req)) { err = PTR_ERR(req); goto error; @@ -1255,7 +1320,7 @@ int p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, u32 count) { - int err, rsize, total; + int err, rsize, total, sgsupport; struct p9_client *clnt; struct p9_req_t *req; char *dataptr; @@ -1273,13 +1338,24 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, if (count < rsize) rsize = count; - req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset, rsize); + sgsupport = udata && p9_trans_sg_support(clnt->trans_mod); + if (sgsupport) + req = p9_client_rpc(clnt, P9_TREAD, 1, "dqd", fid->fid, offset, + rsize, 11, rsize, udata); + else + req = p9_client_rpc(clnt, P9_TREAD, 0, "dqd", fid->fid, offset, + rsize); + if (IS_ERR(req)) { err = PTR_ERR(req); goto error; } - err = p9pdu_readf(req->rc, clnt->proto_version, "D", &count, &dataptr); + if (sgsupport) + err = p9pdu_readf(req->rc, clnt->proto_version, "d", &count); + else + err = p9pdu_readf(req->rc, clnt->proto_version, "D", &count, &dataptr); + if (err) { p9pdu_dump(1, req->rc); goto free_and_error; @@ -1287,15 +1363,16 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, P9_DPRINTK(P9_DEBUG_9P, "<<< RREAD count %d\n", count); - if (data) { - memmove(data, dataptr, count); - } + if (!sgsupport) { + if (data) + memmove(data, dataptr, count); - if (udata) { - err = copy_to_user(udata, dataptr, count); - if (err) { - err = -EFAULT; - goto free_and_error; + if (udata) { + err = copy_to_user(udata, dataptr, count); + if (err) { + err = -EFAULT; + goto free_and_error; + } } } @@ -1330,10 +1407,13 @@ p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, if (count < rsize) rsize = count; if (data) - req = p9_client_rpc(clnt, P9_TWRITE, "dqD", fid->fid, offset, + req = p9_client_rpc(clnt, P9_TWRITE, 0, "dqD", fid->fid, offset, rsize, data); + else if (p9_trans_sg_support(clnt->trans_mod)) + req = p9_client_rpc(clnt, P9_TWRITE, 0, "dqu", fid->fid, offset, + rsize, udata); else - req = p9_client_rpc(clnt, P9_TWRITE, "dqU", fid->fid, offset, + req = p9_client_rpc(clnt, P9_TWRITE, 0, "dqU", fid->fid, offset, rsize, udata); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -1374,7 +1454,7 @@ struct p9_wstat *p9_client_stat(struct p9_fid *fid) err = 0; clnt = fid->clnt; - req = p9_client_rpc(clnt, P9_TSTAT, "d", fid->fid); + req = p9_client_rpc(clnt, P9_TSTAT, 0, "d", fid->fid); if (IS_ERR(req)) { err = PTR_ERR(req); goto error; @@ -1425,7 +1505,7 @@ struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid, err = 0; clnt = fid->clnt; - req = p9_client_rpc(clnt, P9_TGETATTR, "dq", fid->fid, request_mask); + req = p9_client_rpc(clnt, P9_TGETATTR, 0, "dq", fid->fid, request_mask); if (IS_ERR(req)) { err = PTR_ERR(req); goto error; @@ -1516,7 +1596,7 @@ int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst) wst->name, wst->uid, wst->gid, wst->muid, wst->extension, wst->n_uid, wst->n_gid, wst->n_muid); - req = p9_client_rpc(clnt, P9_TWSTAT, "dwS", fid->fid, wst->size+2, wst); + req = p9_client_rpc(clnt, P9_TWSTAT, 0, "dwS", fid->fid, wst->size+2, wst); if (IS_ERR(req)) { err = PTR_ERR(req); goto error; @@ -1547,7 +1627,7 @@ int p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *p9attr) p9attr->size, p9attr->atime_sec, p9attr->atime_nsec, p9attr->mtime_sec, p9attr->mtime_nsec); - req = p9_client_rpc(clnt, P9_TSETATTR, "dI", fid->fid, p9attr); + req = p9_client_rpc(clnt, P9_TSETATTR, 0, "dI", fid->fid, p9attr); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -1571,7 +1651,7 @@ int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb) P9_DPRINTK(P9_DEBUG_9P, ">>> TSTATFS fid %d\n", fid->fid); - req = p9_client_rpc(clnt, P9_TSTATFS, "d", fid->fid); + req = p9_client_rpc(clnt, P9_TSTATFS, 0, "d", fid->fid); if (IS_ERR(req)) { err = PTR_ERR(req); goto error; @@ -1611,7 +1691,7 @@ int p9_client_rename(struct p9_fid *fid, struct p9_fid *newdirfid, char *name) P9_DPRINTK(P9_DEBUG_9P, ">>> TRENAME fid %d newdirfid %d name %s\n", fid->fid, newdirfid->fid, name); - req = p9_client_rpc(clnt, P9_TRENAME, "dds", fid->fid, + req = p9_client_rpc(clnt, P9_TRENAME, 0, "dds", fid->fid, newdirfid->fid, name); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -1649,7 +1729,7 @@ struct p9_fid *p9_client_xattrwalk(struct p9_fid *file_fid, ">>> TXATTRWALK file_fid %d, attr_fid %d name %s\n", file_fid->fid, attr_fid->fid, attr_name); - req = p9_client_rpc(clnt, P9_TXATTRWALK, "dds", + req = p9_client_rpc(clnt, P9_TXATTRWALK, 0, "dds", file_fid->fid, attr_fid->fid, attr_name); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -1688,7 +1768,7 @@ int p9_client_xattrcreate(struct p9_fid *fid, const char *name, fid->fid, name, (long long)attr_size, flags); err = 0; clnt = fid->clnt; - req = p9_client_rpc(clnt, P9_TXATTRCREATE, "dsqd", + req = p9_client_rpc(clnt, P9_TXATTRCREATE, 0, "dsqd", fid->fid, name, attr_size, flags); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -1722,7 +1802,8 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) if (count < rsize) rsize = count; - req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, offset, rsize); + req = p9_client_rpc(clnt, P9_TREADDIR, 0, "dqd", fid->fid, offset, + rsize); if (IS_ERR(req)) { err = PTR_ERR(req); goto error; @@ -1760,7 +1841,7 @@ int p9_client_mknod_dotl(struct p9_fid *fid, char *name, int mode, clnt = fid->clnt; P9_DPRINTK(P9_DEBUG_9P, ">>> TMKNOD fid %d name %s mode %d major %d " "minor %d\n", fid->fid, name, mode, MAJOR(rdev), MINOR(rdev)); - req = p9_client_rpc(clnt, P9_TMKNOD, "dsdddd", fid->fid, name, mode, + req = p9_client_rpc(clnt, P9_TMKNOD, 0, "dsdddd", fid->fid, name, mode, MAJOR(rdev), MINOR(rdev), gid); if (IS_ERR(req)) return PTR_ERR(req); @@ -1791,7 +1872,7 @@ int p9_client_mkdir_dotl(struct p9_fid *fid, char *name, int mode, clnt = fid->clnt; P9_DPRINTK(P9_DEBUG_9P, ">>> TMKDIR fid %d name %s mode %d gid %d\n", fid->fid, name, mode, gid); - req = p9_client_rpc(clnt, P9_TMKDIR, "dsdd", fid->fid, name, mode, + req = p9_client_rpc(clnt, P9_TMKDIR, 0, "dsdd", fid->fid, name, mode, gid); if (IS_ERR(req)) return PTR_ERR(req); diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 3acd3af..72291e8 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include "protocol.h" @@ -60,7 +61,7 @@ void p9pdu_dump(int way, struct p9_fcall *pdu) { int i, n; - u8 *data = pdu->sdata; + u8 *data = pdu->pkt; int datalen = pdu->size; char buf[255]; int buflen = 255; @@ -105,7 +106,7 @@ EXPORT_SYMBOL(p9stat_free); static size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size) { size_t len = MIN(pdu->size - pdu->offset, size); - memcpy(data, &pdu->sdata[pdu->offset], len); + memcpy(data, &pdu->pkt[pdu->offset], len); pdu->offset += len; return size - len; } @@ -113,7 +114,7 @@ static size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size) static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size) { size_t len = MIN(pdu->capacity - pdu->size, size); - memcpy(&pdu->sdata[pdu->size], data, len); + memcpy(&pdu->pkt[pdu->size], data, len); pdu->size += len; return size - len; } @@ -122,7 +123,7 @@ static size_t pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size) { size_t len = MIN(pdu->capacity - pdu->size, size); - int err = copy_from_user(&pdu->sdata[pdu->size], udata, len); + int err = copy_from_user(&pdu->pkt[pdu->size], udata, len); if (err) printk(KERN_WARNING "pdu_write_u returning: %d\n", err); @@ -130,6 +131,26 @@ pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size) return size - len; } +static size_t +pdu_wmap_u(struct p9_fcall *pdu, const char __user *udata, size_t size) +{ + int err; + size_t count; + + count = MIN(pdu->capacity - pdu->size, size); + err = p9pdu_prepare_sg(pdu, pdu->size, count, 1, udata); + if (err < 0) + return err; + + /* if we adjusted pkt so the header can fit in a page (unlikely), + * we need to move the header data itself */ + if (pdu->pkt != pdu->sdata) + memmove(pdu->pkt, pdu->sdata, pdu->size); + + pdu->size += count; + return size - count; +} + /* b - int8_t w - int16_t @@ -259,7 +280,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, *count = MIN(*count, pdu->size - pdu->offset); - *data = &pdu->sdata[pdu->offset]; + *data = &pdu->pkt[pdu->offset]; } } break; @@ -473,6 +494,16 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, errcode = -EFAULT; } break; + case 'u':{ + int32_t count = va_arg(ap, int32_t); + const char __user *udata = + va_arg(ap, const void __user *); + errcode = p9pdu_writef(pdu, proto_version, "d", + count); + if (!errcode && pdu_wmap_u(pdu, udata, count)) + errcode = -EFAULT; + } + break; case 'T':{ int16_t nwname = va_arg(ap, int); const char **wnames = va_arg(ap, const char **); @@ -583,6 +614,7 @@ int p9stat_read(char *buf, int len, struct p9_wstat *st, int proto_version) fake_pdu.size = len; fake_pdu.capacity = len; fake_pdu.sdata = buf; + fake_pdu.pkt = buf; fake_pdu.offset = 0; ret = p9pdu_readf(&fake_pdu, proto_version, "S", st); @@ -602,9 +634,10 @@ int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type) int p9pdu_finalize(struct p9_fcall *pdu) { - int size = pdu->size; + int size; int err; + size = pdu->size; pdu->size = 0; err = p9pdu_writef(pdu, 0, "d", size); pdu->size = size; @@ -620,10 +653,102 @@ int p9pdu_finalize(struct p9_fcall *pdu) return err; } -void p9pdu_reset(struct p9_fcall *pdu) +void p9pdu_reset(struct p9_fcall *pdu, size_t capacity) { pdu->offset = 0; pdu->size = 0; + pdu->pkt = pdu->sdata; + pdu->capacity = capacity; + pdu->sgpkt = NULL; + pdu->sgnum = 0; +} + +int p9pdu_prepare_sg(struct p9_fcall *pdu, int32_t hdrsz, int32_t count, + int rw, const void __user *data) +{ + int i, m, nent, off, err; + uint8_t *p, *edata; + struct page *pages[32]; + + nent = min(count/PAGE_SIZE + 2, ARRAY_SIZE(pages)); + edata = pdu->pkt + pdu->capacity; + + /* The pdu->sdata pkt area looks like: + * -- padding to ensure the header fits in a page -- + * -- header -- + * -- array of scatterlist structs -- + * -- trailer (rest of the space) -- + * + * The trailer is going to be used only if Tread gets Rerror + * response and the user data buffer (+ header) is not big + * enough to hold the all of the response data */ + + /* fcall header */ + BUG_ON(hdrsz > PAGE_SIZE); + off = ((unsigned long) pdu->pkt) & ~PAGE_MASK; + if (off+hdrsz > PAGE_SIZE) { + pdu->pkt += PAGE_SIZE - off; + off = 0; + } + + p = pdu->pkt + hdrsz; + p += 8 - ((unsigned long) p)%8; /* align */ + pdu->sgpkt = (struct scatterlist *) p; + if ((uint8_t *)(&pdu->sgpkt[nent+1]) > edata) { + /* no enough space for the scatterlist array */ + err = -ENOMEM; + goto error; + } + + sg_init_table(pdu->sgpkt, nent + 2); + sg_set_buf(&pdu->sgpkt[0], pdu->pkt, hdrsz); + + /* udata */ + err = get_user_pages_fast((unsigned long) data, nent, rw, pages); + if (err < 0) + goto error; + + off = ((unsigned long) data) & ~PAGE_MASK; + for(i = 0; count > 0; off = 0, i++) { + m = min((int)(PAGE_SIZE - off), count); + sg_set_page(&pdu->sgpkt[i+1], pages[i], m, off); + count -= m; + } + + /* fcall trailer (in case of Tread/Rerror) */ + p = (uint8_t *) &pdu->sgpkt[nent+1]; + sg_set_buf(&pdu->sgpkt[i+1], p, pdu->capacity - (p - pdu->sdata)); + sg_mark_end(&pdu->sgpkt[i+1]); + pdu->sgnum = i+1; + + /* make sure the header doesn't go over the scatterlist array */ + pdu->capacity = hdrsz; + return 0; + +error: + pdu->sgpkt = NULL; + return err; +} + +void p9pdu_free_sg(struct p9_fcall *pdu) +{ + int i; + struct scatterlist *sg; + struct page *p; + + if (!pdu->sgpkt) + return; + + /* pdu->sgpkt[0] and the last one point to pdu->pkt and + * shouldn't be unmapped */ + for(i=1, sg=&pdu->sgpkt[1]; i < pdu->sgnum; i++, sg = sg_next(sg)) { + p = sg_page(sg); + kunmap(p); + put_page(p); + } + + pdu->sgnum = 0; + pdu->sgpkt = NULL; } int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, @@ -636,6 +761,7 @@ int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, fake_pdu.size = len; fake_pdu.capacity = len; fake_pdu.sdata = buf; + fake_pdu.pkt = buf; fake_pdu.offset = 0; ret = p9pdu_readf(&fake_pdu, proto_version, "Qqbs", &dirent->qid, diff --git a/net/9p/protocol.h b/net/9p/protocol.h index 2431c0f..a711b56 100644 --- a/net/9p/protocol.h +++ b/net/9p/protocol.h @@ -31,4 +31,7 @@ int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...); int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type); int p9pdu_finalize(struct p9_fcall *pdu); void p9pdu_dump(int, struct p9_fcall *); -void p9pdu_reset(struct p9_fcall *pdu); +void p9pdu_reset(struct p9_fcall *pdu, size_t msize); +int p9pdu_prepare_sg(struct p9_fcall *pdu, int32_t hdrsz, int32_t count, + int rw, const void __user *data); +void p9pdu_free_sg(struct p9_fcall *pdu); diff --git a/net/9p/util.c b/net/9p/util.c index e048701..f8c35f8 100644 --- a/net/9p/util.c +++ b/net/9p/util.c @@ -66,7 +66,7 @@ struct p9_idpool *p9_idpool_create(void) EXPORT_SYMBOL(p9_idpool_create); /** - * p9_idpool_destroy - create a new per-connection id pool + * p9_idpool_destroy - destroy the per-connection id pool * @p: idpool to destory */