int p9_show_client_options(struct seq_file *m, struct p9_client *clnt)
{ if (clnt->msize != DEFAULT_MSIZE)
seq_printf(m, ",msize=%u", clnt->msize);
seq_printf(m, ",trans=%s", clnt->trans_mod->name);
switch (clnt->proto_version) { case p9_proto_legacy:
seq_puts(m, ",noextend"); break; case p9_proto_2000u:
seq_puts(m, ",version=9p2000.u"); break; case p9_proto_2000L: /* Default */ break;
}
if (clnt->trans_mod->show_options) return clnt->trans_mod->show_options(m, clnt); return 0;
}
EXPORT_SYMBOL(p9_show_client_options);
/* Some error codes are taken directly from the server replies, * make sure they are valid.
*/ staticint safe_errno(int err)
{ if (err > 0 || err < -MAX_ERRNO) {
p9_debug(P9_DEBUG_ERROR, "Invalid error code %d\n", err); return -EPROTO;
} return err;
}
/* Interpret mount option for protocol version */ staticint get_protocol_version(char *s)
{ int version = -EINVAL;
if (!strcmp(s, "9p2000")) {
version = p9_proto_legacy;
p9_debug(P9_DEBUG_9P, "Protocol version: Legacy\n");
} elseif (!strcmp(s, "9p2000.u")) {
version = p9_proto_2000u;
p9_debug(P9_DEBUG_9P, "Protocol version: 9P2000.u\n");
} elseif (!strcmp(s, "9p2000.L")) {
version = p9_proto_2000L;
p9_debug(P9_DEBUG_9P, "Protocol version: 9P2000.L\n");
} else {
pr_info("Unknown protocol version %s\n", s);
}
return version;
}
/** * parse_opts - parse mount options into client structure * @opts: options string passed from mount * @clnt: existing v9fs client information * * Return 0 upon success, -ERRNO upon failure
*/
staticint parse_opts(char *opts, struct p9_client *clnt)
{ char *options, *tmp_options; char *p;
substring_t args[MAX_OPT_ARGS]; int option; char *s; int ret = 0;
while ((p = strsep(&options, ",")) != NULL) { int token, r;
if (!*p) continue;
token = match_token(p, tokens, args); switch (token) { case Opt_msize:
r = match_int(&args[0], &option); if (r < 0) {
p9_debug(P9_DEBUG_ERROR, "integer field, but no integer?\n");
ret = r; continue;
} if (option < 4096) {
p9_debug(P9_DEBUG_ERROR, "msize should be at least 4k\n");
ret = -EINVAL; continue;
}
clnt->msize = option; break; case Opt_trans:
s = match_strdup(&args[0]); if (!s) {
ret = -ENOMEM;
p9_debug(P9_DEBUG_ERROR, "problem allocating copy of trans arg\n"); goto free_and_return;
}
v9fs_put_trans(clnt->trans_mod);
clnt->trans_mod = v9fs_get_trans_by_name(s); if (!clnt->trans_mod) {
pr_info("Could not find request transport: %s\n",
s);
ret = -EINVAL;
}
kfree(s); break; case Opt_legacy:
clnt->proto_version = p9_proto_legacy; break; case Opt_version:
s = match_strdup(&args[0]); if (!s) {
ret = -ENOMEM;
p9_debug(P9_DEBUG_ERROR, "problem allocating copy of version arg\n"); goto free_and_return;
}
r = get_protocol_version(s); if (r < 0)
ret = r; else
clnt->proto_version = r;
kfree(s); break; default: continue;
}
}
free_and_return: if (ret)
v9fs_put_trans(clnt->trans_mod);
kfree(tmp_options); return ret;
}
void p9_fcall_fini(struct p9_fcall *fc)
{ /* sdata can be NULL for interrupted requests in trans_rdma, * and kmem_cache_free does not do NULL-check for us
*/ if (unlikely(!fc->sdata)) return;
if (fc->cache)
kmem_cache_free(fc->cache, fc->sdata); else
kfree(fc->sdata);
}
EXPORT_SYMBOL(p9_fcall_fini);
staticstruct kmem_cache *p9_req_cache;
/** * p9_tag_alloc - Allocate a new request. * @c: Client session. * @type: Transaction type. * @t_size: Buffer size for holding this request * (automatic calculation by format template if 0). * @r_size: Buffer size for holding server's reply on this request * (automatic calculation by format template if 0). * @fmt: Format template for assembling 9p request message * (see p9pdu_vwritef). * @ap: Variable arguments to be fed to passed format template * (see p9pdu_vwritef). * * Context: Process context. * Return: Pointer to new request.
*/ staticstruct p9_req_t *
p9_tag_alloc(struct p9_client *c, int8_t type, uint t_size, uint r_size, constchar *fmt, va_list ap)
{ struct p9_req_t *req = kmem_cache_alloc(p9_req_cache, GFP_NOFS); int alloc_tsize; int alloc_rsize; int tag;
va_list apc;
if (p9_fcall_init(c, &req->tc, alloc_tsize)) goto free_req; if (p9_fcall_init(c, &req->rc, alloc_rsize)) goto free;
p9pdu_reset(&req->tc);
p9pdu_reset(&req->rc);
req->t_err = 0;
req->status = REQ_STATUS_ALLOC; /* refcount needs to be set to 0 before inserting into the idr * so p9_tag_lookup does not accept a request that is not fully * initialized. refcount_set to 2 below will mark request ready.
*/
refcount_set(&req->refcount, 0);
init_waitqueue_head(&req->wq);
INIT_LIST_HEAD(&req->req_list);
idr_preload(GFP_NOFS);
spin_lock_irq(&c->lock); if (type == P9_TVERSION)
tag = idr_alloc(&c->reqs, req, P9_NOTAG, P9_NOTAG + 1,
GFP_NOWAIT); else
tag = idr_alloc(&c->reqs, req, 0, P9_NOTAG, GFP_NOWAIT);
req->tc.tag = tag;
spin_unlock_irq(&c->lock);
idr_preload_end(); if (tag < 0) goto free;
/* Init ref to two because in the general case there is one ref * that is put asynchronously by a writer thread, one ref * temporarily given by p9_tag_lookup and put by p9_client_cb * in the recv thread, and one ref put by p9_req_put in the * main thread. The only exception is virtio that does not use * p9_tag_lookup but does not have a writer thread either * (the write happens synchronously in the request/zc_request * callback), so p9_client_cb eats the second ref there * as the pointer is duplicated directly by virtqueue_add_sgs()
*/
refcount_set(&req->refcount, 2);
/** * p9_tag_lookup - Look up a request by tag. * @c: Client session. * @tag: Transaction ID. * * Context: Any context. * Return: A request, or %NULL if there is no request with that tag.
*/ struct p9_req_t *p9_tag_lookup(struct p9_client *c, u16 tag)
{ struct p9_req_t *req;
rcu_read_lock();
again:
req = idr_find(&c->reqs, tag); if (req) { /* We have to be careful with the req found under rcu_read_lock * Thanks to SLAB_TYPESAFE_BY_RCU we can safely try to get the * ref again without corrupting other data, then check again * that the tag matches once we have the ref
*/ if (!p9_req_try_get(req)) goto again; if (req->tc.tag != tag) {
p9_req_put(c, req); goto again;
}
}
rcu_read_unlock();
return req;
}
EXPORT_SYMBOL(p9_tag_lookup);
/** * p9_tag_remove - Remove a tag. * @c: Client session. * @r: Request of reference. * * Context: Any context.
*/ staticvoid p9_tag_remove(struct p9_client *c, struct p9_req_t *r)
{ unsignedlong flags;
u16 tag = r->tc.tag;
/** * p9_tag_cleanup - cleans up tags structure and reclaims resources * @c: v9fs client struct * * This frees resources associated with the tags structure *
*/ staticvoid p9_tag_cleanup(struct p9_client *c)
{ struct p9_req_t *req; int id;
rcu_read_lock();
idr_for_each_entry(&c->reqs, req, id) {
pr_info("Tag %d still in use\n", id); if (p9_req_put(c, req) == 0)
pr_warn("Packet with tag %d has still references",
req->tc.tag);
}
rcu_read_unlock();
}
/** * p9_client_cb - call back from transport to client * @c: client state * @req: request received * @status: request status, one of REQ_STATUS_* *
*/ void p9_client_cb(struct p9_client *c, struct p9_req_t *req, int status)
{
p9_debug(P9_DEBUG_MUX, " tag %d\n", req->tc.tag);
/* This barrier is needed to make sure any change made to req before * the status change is visible to another thread
*/
smp_wmb();
WRITE_ONCE(req->status, status);
/** * p9_parse_header - parse header arguments out of a packet * @pdu: packet to parse * @size: size of packet * @type: type of request * @tag: tag of packet * @rewind: set if we need to rewind offset afterwards
*/
int
p9_parse_header(struct p9_fcall *pdu, int32_t *size, int8_t *type,
int16_t *tag, int rewind)
{
s8 r_type;
s16 r_tag;
s32 r_size; int offset = pdu->offset; int err;
rewind_and_exit: if (rewind)
pdu->offset = offset; return err;
}
EXPORT_SYMBOL(p9_parse_header);
/** * p9_check_errors - check 9p packet for error return and process it * @c: current client instance * @req: request to parse and check for error conditions * * returns error code if one is discovered, otherwise returns 0 * * this will have to be more complicated if we have multiple * error packet types
*/
staticint p9_check_errors(struct p9_client *c, struct p9_req_t *req)
{
s8 type; int err; int ecode;
err = p9_parse_header(&req->rc, NULL, &type, NULL, 0); if (req->rc.size > req->rc.capacity && !req->rc.zc) {
pr_err("requested packet size too big: %d does not fit %zu (type=%d)\n",
req->rc.size, req->rc.capacity, req->rc.id); return -EIO;
} /* dump the response from server * This should be after check errors which poplulate pdu_fcall.
*/
trace_9p_protocol_dump(c, &req->rc); if (err) {
p9_debug(P9_DEBUG_ERROR, "couldn't parse header %d\n", err); return err;
} if (type != P9_RERROR && type != P9_RLERROR) return 0;
/** * p9_client_flush - flush (cancel) a request * @c: client state * @oldreq: request to cancel * * This sents a flush for a particular request and links * the flush request to the original request. The current * code only supports a single flush request although the protocol * allows for multiple flush requests to be sent for a single request. *
*/
p9_debug(P9_DEBUG_9P, ">>> TFLUSH tag %d\n", oldtag);
req = p9_client_rpc(c, P9_TFLUSH, "w", oldtag); if (IS_ERR(req)) return PTR_ERR(req);
/* if we haven't received a response for oldreq, * remove it from the list
*/ if (READ_ONCE(oldreq->status) == REQ_STATUS_SENT) { if (c->trans_mod->cancelled)
c->trans_mod->cancelled(c, oldreq);
}
/* marshall the data */
p9pdu_prepare(&req->tc, req->tc.tag, type);
err = p9pdu_vwritef(&req->tc, c->proto_version, fmt, ap); if (err) goto reterr;
p9pdu_finalize(c, &req->tc);
trace_9p_client_req(c, type, req->tc.tag); return req;
reterr:
p9_req_put(c, req); /* We have to put also the 2nd reference as it won't be used */
p9_req_put(c, req); return ERR_PTR(err);
}
/** * p9_client_rpc - issue a request and wait for a response * @c: client session * @type: type of request * @fmt: protocol format string (see protocol.c) * * Returns request structure (which client must free using p9_req_put)
*/
staticstruct p9_req_t *
p9_client_rpc(struct p9_client *c, int8_t type, constchar *fmt, ...)
{
va_list ap; int sigpending, err; unsignedlong flags; struct p9_req_t *req; /* Passing zero for tsize/rsize to p9_client_prepare_req() tells it to * auto determine an appropriate (small) request/response size * according to actual message data being sent. Currently RDMA * transport is excluded from this response message size optimization, * as it would not cope with it, due to its pooled response buffers * (using an optimized request size for RDMA as well though).
*/ const uint tsize = 0; const uint rsize = c->trans_mod->pooled_rbuffers ? c->msize : 0;
if (c->trans_mod->cancel(c, req))
p9_client_flush(c, req);
/* if we received the response anyway, don't signal error */ if (READ_ONCE(req->status) == REQ_STATUS_RCVD)
err = 0;
}
recalc_sigpending: if (sigpending) {
spin_lock_irqsave(¤t->sighand->siglock, flags);
recalc_sigpending();
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
} if (err < 0) goto reterr;
/** * p9_client_zc_rpc - issue a request and wait for a response * @c: client session * @type: type of request * @uidata: destination for zero copy read * @uodata: source for zero copy write * @inlen: read buffer size * @olen: write buffer size * @in_hdrlen: reader header size, This is the size of response protocol data * @fmt: protocol format string (see protocol.c) * * Returns request structure (which client must free using p9_req_put)
*/ staticstruct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, struct iov_iter *uidata, struct iov_iter *uodata, int inlen, int olen, int in_hdrlen, constchar *fmt, ...)
{
va_list ap; int sigpending, err; unsignedlong flags; struct p9_req_t *req;
va_start(ap, fmt); /* We allocate a inline protocol data of only 4k bytes. * The actual content is passed in zero-copy fashion.
*/
req = p9_client_prepare_req(c, type, P9_ZC_HDR_SZ, P9_ZC_HDR_SZ, fmt, ap);
va_end(ap); if (IS_ERR(req)) return req;
if (c->trans_mod->cancel(c, req))
p9_client_flush(c, req);
/* if we received the response anyway, don't signal error */ if (READ_ONCE(req->status) == REQ_STATUS_RCVD)
err = 0;
}
recalc_sigpending: if (sigpending) {
spin_lock_irqsave(¤t->sighand->siglock, flags);
recalc_sigpending();
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
} if (err < 0) goto reterr;
err = clnt->trans_mod->create(clnt, dev_name, options); if (err) goto put_trans;
if (clnt->msize > clnt->trans_mod->maxsize) {
clnt->msize = clnt->trans_mod->maxsize;
pr_info("Limiting 'msize' to %d as this is the maximum " "supported by transport %s\n",
clnt->msize, clnt->trans_mod->name
);
}
if (clnt->msize < 4096) {
p9_debug(P9_DEBUG_ERROR, "Please specify a msize of at least 4k\n");
err = -EINVAL; goto close_trans;
}
err = p9_client_version(clnt); if (err) goto close_trans;
/* P9_HDRSZ + 4 is the smallest packet header we can have that is * followed by data accessed from userspace by read
*/
clnt->fcall_cache =
kmem_cache_create_usercopy(cache_name, clnt->msize,
0, 0, P9_HDRSZ + 4,
clnt->msize - (P9_HDRSZ + 4),
NULL);
p9_req_put(clnt, req);
error: /* Fid is not valid even after a failed clunk * If interrupted, retry once then give up and * leak fid until umount.
*/ if (err == -ERESTARTSYS) { if (retries++ == 0) goto again;
} else {
p9_fid_destroy(fid);
} return err;
}
EXPORT_SYMBOL(p9_client_clunk);
int p9_client_remove(struct p9_fid *fid)
{ int err = 0; struct p9_client *clnt; struct p9_req_t *req;
if (wst->name)
ret += strlen(wst->name); if (wst->uid)
ret += strlen(wst->uid); if (wst->gid)
ret += strlen(wst->gid); if (wst->muid)
ret += strlen(wst->muid);
if (proto_version == p9_proto_2000u ||
proto_version == p9_proto_2000L) { /* extension[s] n_uid[4] n_gid[4] n_muid[4] */
ret += 2 + 4 + 4 + 4; if (wst->extension)
ret += strlen(wst->extension);
}
return ret;
}
int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst)
{ int err = 0; struct p9_req_t *req; struct p9_client *clnt;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.