/*
* net/9p/clnt.c
*
* 9P Client
*
* Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to:
* Free Software Foundation
* 51 Franklin Street, Fifth Floor
* Boston, MA 02111-1301 USA
*
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/idr.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <net/9p/9p.h>
#include <linux/parser.h>
#include <net/9p/client.h>
#include <net/9p/transport.h>
#include "protocol.h"
/*
* Client Option Parsing (code inspired by NFS code)
* - a little lazy - parse all client options
*/
enum {
Opt_msize,
Opt_trans,
Opt_legacy,
Opt_err,
};
static const match_table_t tokens = {
{Opt_msize, "msize=%u"},
{Opt_legacy, "noextend"},
{Opt_trans, "trans=%s"},
{Opt_err, NULL},
};
static struct p9_req_t *
p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...);
/**
* parse_options - parse mount options into client structure
* @opts: options string passed from mount
* @clnt: existing v9fs client information
*
* Return 0 upon success, -ERRNO upon failure
*/
static int parse_opts(char *opts, struct p9_client *clnt)
{
char *options;
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
int ret = 0;
clnt->dotu = 1;
clnt->msize = 8192;
if (!opts)
return 0;
options = kstrdup(opts, GFP_KERNEL);
if (!options) {
P9_DPRINTK(P9_DEBUG_ERROR,
"failed to allocate copy of option string\n");
return -ENOMEM;
}
while ((p = strsep(&options, ",")) != NULL) {
int token;
if (!*p)
continue;
token = match_token(p, tokens, args);
if (token < Opt_trans) {
int r = match_int(&args[0], &option);
if (r < 0) {
P9_DPRINTK(P9_DEBUG_ERROR,
"integer field, but no integer?\n");
ret = r;
continue;
}
}
switch (token) {
case Opt_msize:
clnt->msize = option;
break;
case Opt_trans:
clnt->trans_mod = v9fs_get_trans_by_name(&args[0]);
break;
case Opt_legacy:
clnt->dotu = 0;
break;
default:
continue;
}
}
kfree(options);
return ret;
}
/**
* p9_tag_alloc - lookup/allocate a request by tag
* @c: client session to lookup tag within
* @tag: numeric id for transaction
*
* this is a simple array lookup, but will grow the
* request_slots as necessary to accomodate transaction
* ids which did not previously have a slot.
*
* this code relies on the client spinlock to manage locks, its
* possible we should switch to something else, but I'd rather
* stick with something low-overhead for the common case.
*
*/
static struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag)
{
unsigned long flags;
int row, col;
struct p9_req_t *req;
/* This looks up the original request by tag so we know which
* buffer to read the data into */
tag++;
if (tag >= c->max_tag) {
spin_lock_irqsave(&c->lock, flags);
/* check again since original check was outside of lock */
while (tag >= c->max_tag) {
row = (tag / P9_ROW_MAXTAG);
c->reqs[row] = kcalloc(P9_ROW_MAXTAG,
sizeof(struct p9_req_t), GFP_ATOMIC);
if (!c->reqs[row]) {
printk(KERN_ERR "Couldn't grow tag array\n");
spin_unlock_irqrestore(&c->lock, flags);
return ERR_PTR(-ENOMEM);
}
for (col = 0; col < P9_ROW_MAXTAG; col++) {
c->reqs[row][col].status = REQ_STATUS_IDLE;
c->reqs[row][col].tc = NULL;
}
c->max_tag += P9_ROW_MAXTAG;
}
spin_unlock_irqrestore(&c->lock, flags);
}
row = tag / P9_ROW_MAXTAG;
col = tag % P9_ROW_MAXTAG;
req = &c->reqs[row][col];
if (!req->tc) {
req->wq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
if (!req->wq) {
printk(KERN_ERR "Couldn't grow tag array\n");
return ERR_PTR(-ENOMEM);
}
init_waitqueue_head(req->wq);
req->tc = kmalloc(sizeof(struct p9_fcall)+c->msize,
GFP_KERNEL);
req->rc = kmalloc(sizeof(struct p9_fcall)+c->msize,
GFP_KERNEL);
if ((!req->tc) || (!req->rc)) {
printk(KERN_ERR "Couldn't grow tag array\n");
kfree(req->tc);
kfree(req->rc);
kfree
|