@@ -27,6 +27,8 @@
#ifndef NET_9P_H
#define NET_9P_H
+#include <linux/scatterlist.h>
+
/**
* 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);
@@ -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 */
@@ -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);
@@ -31,6 +31,7 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/types.h>
+#include <linux/highmem.h>
#include <net/9p/9p.h>
#include <net/9p/client.h>
#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,
@@ -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);
@@ -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
*/