Esempio n. 1
0
File: ioctx.c Progetto: kainz/diod
static int
_get_one_file (Path path, char *s, DynStr *ds)
{
    int unique, shared;

    xpthread_mutex_lock (&path->lock);
    _count_ioctx (path->ioctx, &shared, &unique);
    aspf (&ds->s, &ds->len, "%d %d %d %s\n", path->refcount, shared, unique, s);
    xpthread_mutex_unlock (&path->lock);
    return 0;
}
Esempio n. 2
0
File: ioctx.c Progetto: kainz/diod
static int
_ioctx_decref (IOCtx ioctx)
{
    int n;

    xpthread_mutex_lock (&ioctx->lock);
    n = --ioctx->refcount;
    xpthread_mutex_unlock (&ioctx->lock);

    return n;
}
Esempio n. 3
0
/* Clear all state associated with conn out of the srv.
 * No more I/O is possible; we have disassociated the trans from the conn.
 */
static void
np_conn_reset(Npconn *conn)
{
	int reqslen;
	Npsrv *srv;
	Npreq *preqs, **reqs;

	xpthread_mutex_lock(&conn->lock);
	conn->resetting = 1;
	xpthread_mutex_unlock(&conn->lock);
	
	xpthread_mutex_lock(&conn->srv->lock);
	srv = conn->srv;
	preqs = _get_waiting_reqs (conn);
	if (_get_working_reqs (conn, &reqs, &reqslen) < 0) {
		xpthread_mutex_unlock(&conn->srv->lock);
		goto error;
	}
	xpthread_mutex_unlock(&conn->srv->lock);

	_flush_waiting_reqs (preqs);
	_flush_working_reqs (reqs, reqslen);

	xpthread_mutex_lock(&srv->lock);
	while (_count_working_reqs (conn, 1) > 0)
		xpthread_cond_wait(&conn->resetcond, &srv->lock);
	xpthread_mutex_unlock(&srv->lock);

	xpthread_mutex_lock(&conn->lock);
	if (conn->fidpool) {
		np_fidpool_destroy(conn->fidpool);
		conn->fidpool = NULL;
	}
	conn->resetting = 0;
	xpthread_mutex_unlock(&conn->lock);

	_free_working_reqs (reqs, reqslen);
	return;
error:
	return;
}
Esempio n. 4
0
/* increment nonce and return new value */
uint32_t
nonce32(void)
{
#ifdef HAVE_ATOMIC_H
  return atomic_add_32_nv(&seq, 2);
#else
  xpthread_mutex_lock(&seq_mutex);
  seq += 2;
  xpthread_mutex_unlock(&seq_mutex);
  return seq;
#endif /* else !HAVE_ATOMIC_H */
}
Esempio n. 5
0
/* refcount++
 */
Npfid *
np_fid_incref (Npfid *f)
{
	NP_ASSERT(f != NULL);
	NP_ASSERT(f->magic == FID_MAGIC);

	xpthread_mutex_lock (&f->lock);
	f->refcount++;
	xpthread_mutex_unlock (&f->lock);

	return f;
}
Esempio n. 6
0
void
np_usercache_flush (Npsrv *srv)
{
    Npusercache *uc = srv->usercache;
    Npuser *u;

    xpthread_mutex_lock (&uc->lock);
    u = uc->users;
    while (u)
        u = _usercache_del (srv, NULL, u);
    xpthread_mutex_unlock (&uc->lock);
}
Esempio n. 7
0
void
np_conn_decref(Npconn *conn)
{
	xpthread_mutex_lock(&conn->lock);
	assert(conn->refcount > 0);
	conn->refcount--;
	if (conn->refcount) {
		xpthread_mutex_unlock(&conn->lock);
		return;
	}

	if (conn->fidpool) {
		np_fidpool_destroy(conn->fidpool);
		conn->fidpool = NULL;
	}
	
	xpthread_mutex_unlock(&conn->lock);
	pthread_mutex_destroy(&conn->lock);
	pthread_cond_destroy(&conn->resetcond);
	free(conn);
}
Esempio n. 8
0
static void
np_respond(Nptpool *tp, Npreq *req, Npfcall *rc)
{
	xpthread_mutex_lock(&tp->srv->lock);
	np_srv_remove_workreq(tp, req);
	xpthread_mutex_unlock(&tp->srv->lock);

	xpthread_mutex_lock(&req->lock);
	req->rcall = rc;
	if (req->rcall) {
		np_set_tag(req->rcall, req->tag);
		if (req->fid != NULL) {
			np_fid_decref(req->fid);
			req->fid = NULL;
		}
		np_conn_respond(req);		
	}

	xpthread_mutex_unlock(&req->lock);
	np_req_unref(req);
}
Esempio n. 9
0
void
np_fid_decref_bynum (Npconn *conn, u32 fid)
{
	Npfidpool *pool = conn->fidpool;
	int hash = fid % pool->size;
	int refcount = 0;
	Npfid *f;

	xpthread_mutex_lock (&pool->lock);
	if ((f = _lookup_fid (&pool->htable[hash], fid))) {
		xpthread_mutex_lock (&f->lock);
		refcount = --f->refcount;
		xpthread_mutex_unlock (&f->lock);

		if (refcount == 0) {
			_unlink_fid (&pool->htable[hash], f);
		}
	}
	xpthread_mutex_unlock (&pool->lock);

	if (f && refcount == 0)
		(void) _destroy_fid (f);
}
Esempio n. 10
0
/* Find a fid, then refcount++
 */
Npfid *
np_fid_find (Npconn *conn, u32 fid)
{
	Npfidpool *pool = conn->fidpool;
	int hash = fid % pool->size;
	Npfid *f;

	xpthread_mutex_lock (&pool->lock);
	if ((f = _lookup_fid (&pool->htable[hash], fid)))
		np_fid_incref (f);
	xpthread_mutex_unlock (&pool->lock);
	
	return f;
}
Esempio n. 11
0
int
np_srv_add_conn(Npsrv *srv, Npconn *conn)
{
	xpthread_mutex_lock(&srv->lock);
	conn->srv = srv;
	conn->next = srv->conns;
	srv->conns = conn;
	srv->conncount++;
	srv->connhistory++;
	xpthread_cond_signal(&srv->conncountcond);
	xpthread_mutex_unlock(&srv->lock);

	return 1;
}
Esempio n. 12
0
static char *
_ctl_get_tpools (void *a)
{
	Npsrv *srv = (Npsrv *)a;
	Nptpool *tp;
	Npreq *req;
	char *s = NULL;
	int n, len = 0;

	xpthread_mutex_lock(&srv->lock);
	for (tp = srv->tpool; tp != NULL; tp = tp->next) {
		xpthread_mutex_lock(&tp->lock);
		xpthread_mutex_lock(&tp->stats.lock);
		tp->stats.name = tp->name;
		tp->stats.numfids = tp->refcount;
		tp->stats.numreqs = 0;
		for (req = tp->reqs_first; req != NULL; req = req->next)
			tp->stats.numreqs++;
		for (req = tp->workreqs; req != NULL; req = req->next)
			tp->stats.numreqs++;
		n = np_encode_tpools_str (&s, &len, &tp->stats);
		xpthread_mutex_unlock(&tp->stats.lock);
		xpthread_mutex_unlock(&tp->lock);
		if (n < 0) {
			np_uerror (ENOMEM);
			goto error_unlock;
		}
	}
	xpthread_mutex_unlock(&srv->lock);
	return s;
error_unlock:
	xpthread_mutex_unlock(&srv->lock);
	if (s)
		free(s);
	return NULL;
}
Esempio n. 13
0
Npuser *
np_uname2user (Npsrv *srv, char *uname)
{
    Npusercache *uc = srv->usercache;
    Npuser *u = NULL;

    xpthread_mutex_lock (&uc->lock);
    if (!(u = _usercache_lookup (srv, uname, P9_NONUNAME)))
        if ((u = _real_lookup_byname (srv, uname)))
            _usercache_add (srv, u);
    xpthread_mutex_unlock (&uc->lock);
    if (u)
        np_user_incref (u);
    return u;
}
Esempio n. 14
0
Npuser *
np_uid2user (Npsrv *srv, uid_t uid)
{
    Npusercache *uc = srv->usercache;
    Npuser *u = NULL;

    xpthread_mutex_lock (&uc->lock);
    if (!(u = _usercache_lookup (srv, NULL, uid)))
        if ((u = _real_lookup_byuid (srv, uid)))
            _usercache_add (srv, u);
    xpthread_mutex_unlock (&uc->lock);
    if (u)
        np_user_incref (u);
    return u;
}
Esempio n. 15
0
File: conn.c Progetto: EuroCorp/diod
void
np_conn_respond(Npreq *req)
{
    int n;
    Npconn *conn = req->conn;
    Npsrv *srv = conn->srv;
    Npfcall *rc = req->rcall;

    _debug_trace (srv, rc);
    xpthread_mutex_lock(&conn->wlock);
    n = np_trans_send(conn->trans, rc);
    xpthread_mutex_unlock(&conn->wlock);
    if (n < 0)
        np_logerr (srv, "send to '%s'", conn->client_id);
}
Esempio n. 16
0
void
np_user_decref(Npuser *u)
{
    int n;

    if (!u)
        return;

    xpthread_mutex_lock (&u->lock);
    n = --u->refcount;
    xpthread_mutex_unlock (&u->lock);
    if (n > 0)
        return;
    _free_user (u);
}
Esempio n. 17
0
int
np_fidpool_count(Npfidpool *pool)
{
	int i;
	Npfid *f;
	int count = 0;

	xpthread_mutex_lock(&pool->lock);
	for(i = 0; i < pool->size; i++) {
		for (f = pool->htable[i]; f != NULL; f = f->next) {
			NP_ASSERT(f->magic == FID_MAGIC);
			count++;
		}
	}
	xpthread_mutex_unlock(&pool->lock);

	return count;
}
Esempio n. 18
0
File: fsys.c Progetto: eugmes/diod
static int
npc_rpc(Npcfsys *fs, Npfcall *tc, Npfcall **rcp)
{
	Npfcall *rc = NULL;
	u16 tag = P9_NOTAG;
	int n, ret = -1;

	if (!fs->trans) {
		np_uerror(ECONNABORTED);
		goto done;
	}
	if (tc->type != P9_TVERSION)
		tag = npc_get_id(fs->tagpool);
	np_set_tag(tc, tag);

	xpthread_mutex_lock(&fs->lock);
	n = np_trans_send (fs->trans, tc);
	if (n >= 0)
		n = np_trans_recv(fs->trans, &rc, fs->msize);
	xpthread_mutex_unlock(&fs->lock);
	if (n < 0)
		goto done;
	if (rc == NULL) {
		np_uerror (EPROTO); /* premature EOF */
		goto done;
	}
	if (tc->tag != rc->tag) {
		np_uerror (EPROTO); /* unmatched response */
		goto done;
	}
	if (rc->type == P9_RLERROR) {
		np_uerror (rc->u.rlerror.ecode);
		goto done;
	}
	*rcp = rc;
	ret = 0;
done:
	if (tag != P9_NOTAG)
		npc_put_id(fs->tagpool, tag);
	if (ret < 0 && rc != NULL)
		free (rc);
	return ret;
}
Esempio n. 19
0
int
np_srv_add_conn(Npsrv *srv, Npconn *conn)
{
	int ret;

	ret = 0;
	xpthread_mutex_lock(&srv->lock);
	np_conn_incref(conn);
	conn->srv = srv;
	conn->next = srv->conns;
	srv->conns = conn;
	ret = 1;
	srv->conncount++;
	srv->connhistory++;
	xpthread_cond_signal(&srv->conncountcond);
	xpthread_mutex_unlock(&srv->lock);

	return ret;
}
Esempio n. 20
0
void
np_srv_add_req(Npsrv *srv, Npreq *req)
{
	Nptpool *tp = NULL;

	if (req->fid)
		tp = req->fid->tpool;
	if (!tp)
		tp = srv->tpool;
	xpthread_mutex_lock(&tp->lock);
	req->prev = tp->reqs_last;
	if (tp->reqs_last)
		tp->reqs_last->next = req;
	tp->reqs_last = req;
	if (!tp->reqs_first)
		tp->reqs_first = req;
	xpthread_mutex_unlock(&tp->lock);
	xpthread_cond_signal(&tp->reqcond);
}
Esempio n. 21
0
File: ioctx.c Progetto: chaos/diod
int
ioctx_close (Npfid *fid, int seterrno)
{
    Fid *f = fid->aux;
    int n;
    int rc = 0;

    NP_ASSERT (f->ioctx != NULL);

    xpthread_mutex_lock (&f->path->lock);
    n = _ioctx_decref (f->ioctx);
    if (n == 0)
        _unlink_ioctx (&f->path->ioctx, f->ioctx);
    xpthread_mutex_unlock (&f->path->lock);
    if (n == 0)
        rc = _ioctx_close_destroy (f->ioctx, seterrno);
    f->ioctx = NULL;

    return rc;
}
Esempio n. 22
0
int
np_fidpool_destroy(Npfidpool *pool)
{
	int i;
	Npfid *f;
	int unclunked = 0;

	xpthread_mutex_lock(&pool->lock);
	for(i = 0; i < pool->size; i++) {
		f = pool->htable[i];
		while (f != NULL) {
			f = _destroy_fid (f);
			unclunked++;
		}
	}
	xpthread_mutex_unlock (&pool->lock);
	pthread_mutex_destroy (&pool->lock);
	free(pool);

	return unclunked;
}
Esempio n. 23
0
File: pool.c Progetto: eugmes/diod
u32
npc_get_id(Npcpool *p)
{
    int i, n;
    u32 ret;
    u8 *pt;

    xpthread_mutex_lock(&p->lock);

again:
    for(i = 0; i < p->msize; i++)
        if (p->map[i] != 0xFF)
            break;

    if (i>=p->msize && p->msize*8<p->maxid) {
        n = p->msize + 32;
        if (n*8 > p->maxid)
            n = p->maxid/8 + 1;

        pt = realloc(p->map, n);
        if (pt) {
            memset(pt + p->msize, 0, n - p->msize);
            p->map = pt;
            i = p->msize;
            p->msize = n;
        }
    }

    if (i >= p->msize) {
        pthread_cond_wait(&p->cond, &p->lock);
        goto again;
    }

    ret = m2id[p->map[i]];
    p->map[i] |= 1 << ret;
    ret += i * 8;

    xpthread_mutex_unlock(&p->lock);
    return ret;
}
Esempio n. 24
0
Npreq *np_req_alloc(Npconn *conn, Npfcall *tc) {
	Npreq *req;

	req = NULL;
	xpthread_mutex_lock(&reqpool.lock);
	if (reqpool.reqlist) {
		req = reqpool.reqlist;
		reqpool.reqlist = req->next;
		reqpool.reqnum--;
	}
	xpthread_mutex_unlock(&reqpool.lock);

	if (!req) {
		req = malloc(sizeof(*req));
		if (!req)
			return NULL;
	}

	np_conn_incref(conn);
	pthread_mutex_init(&req->lock, NULL);
	req->refcount = 1;
	req->conn = conn;
	req->tag = tc->tag;
	req->tcall = tc;
	req->rcall = NULL;
	req->responded = 0;
	req->flushreq = NULL;
	req->next = NULL;
	req->prev = NULL;
	req->wthread = NULL;
	req->fid = NULL;

	np_preprocess_request (req); /* assigns req->fid */

	return req;
}
Esempio n. 25
0
void
np_tpool_select (Npreq *req)
{
	Npsrv *srv = req->conn->srv;
	Nptpool *tp = NULL;

	NP_ASSERT (srv->tpool != NULL);
	if (!req->fid || req->fid->tpool)
		return;

	xpthread_mutex_lock (&srv->lock);
	if ((srv->flags & SRV_FLAGS_TPOOL_SINGLE) || !req->fid->aname
						  || *req->fid->aname != '/') {
		tp = srv->tpool;
	}
	if (!tp) {
		for (tp = srv->tpool; tp != NULL; tp = tp->next) {
			if (!strcmp (req->fid->aname, tp->name))
				break;
		}
	}
	if (!tp) {
		tp = np_tpool_create(srv, req->fid->aname);
		if (tp) {
			NP_ASSERT (srv->tpool); /* default tpool */
			tp->next = srv->tpool->next;
			srv->tpool->next = tp;
		} else
			np_logerr (srv, "np_tpool_create %s", req->fid->aname);
	}
	if (tp) {
		np_tpool_incref (tp);
		req->fid->tpool = tp;
	}
	xpthread_mutex_unlock (&srv->lock);
}
Esempio n. 26
0
/* Per-connection read thread.
 */
static void *
np_conn_read_proc(void *a)
{
	int i, n, size;
	Npsrv *srv;
	Npconn *conn = (Npconn *)a;
	Nptrans *trans;
	Npreq *req;
	Npfcall *fc, *fc1;

	pthread_detach(pthread_self());
	np_conn_incref(conn);
	srv = conn->srv;
	fc = _alloc_npfcall(conn->msize);
	n = 0;
	while (fc && conn->trans && (i = np_trans_read(conn->trans, fc->pkt + n, conn->msize - n)) > 0) {
		n += i;
again:
		size = np_peek_size (fc->pkt, n);
		if (size == 0 || n < size)
			continue;

		/* Corruption on the transport, unhandled op, etc.
		 * is fatal to the connection.  We could consider returning
		 * an error to the client here.   However, various kernels
		 * may not handle that well, depending on where it happens.
		 */
		if (!np_deserialize(fc, fc->pkt)) {
			_debug_trace (srv, fc);
			np_logerr (srv, "protocol error - "
				   "dropping connection to '%s'",
				   conn->client_id);
			break;
		}
		if ((srv->flags & SRV_FLAGS_DEBUG_9PTRACE))
			_debug_trace (srv, fc);

		/* Replace fc, and copy any data past the current packet
		 * to the replacement.
		 */
		fc1 = _alloc_npfcall(conn->msize);
		if (!fc1) {
			np_logerr (srv, "out of memory in receive path - "
				   "dropping connection to '%s'",
				   conn->client_id);
			break;
		}
		if (n > size)
			memmove(fc1->pkt, fc->pkt + size, n - size);
		n -= size;

		/* Encapsulate fc in a request and hand to srv worker threads.
		 * In np_req_alloc, req->fid is looked up/initialized.
		 */
		req = np_req_alloc(conn, fc);
		if (!req) {
			np_logerr (srv, "out of memory in receive path - "
				   "dropping connection to '%s'",
				   conn->client_id);
			break;
		}
		np_srv_add_req(srv, req);
		xpthread_mutex_lock(&conn->lock);
		conn->reqs_in++;
		xpthread_mutex_unlock(&conn->lock);
		fc = fc1;
		if (n > 0)
			goto again;

	}
	/* Just got EOF on read, or some other fatal error for the
	 * connection like out of memory.
	 */

	xpthread_mutex_lock(&conn->lock);
	trans = conn->trans;
	conn->trans = NULL;
	if (fc)
		_free_npfcall(fc);
	xpthread_mutex_unlock(&conn->lock);

	np_srv_remove_conn(conn->srv, conn);
	np_conn_reset(conn);

	if (trans)
		np_trans_destroy(trans);

	np_conn_decref(conn);
	return NULL;
}
Esempio n. 27
0
File: conn.c Progetto: EuroCorp/diod
/* Per-connection read thread.
 */
static void *
np_conn_read_proc(void *a)
{
    Npconn *conn = (Npconn *)a;
    Npsrv *srv = conn->srv;
    Npreq *req;
    Npfcall *fc;

    pthread_detach(pthread_self());

    for (;;) {
        if (np_trans_recv(conn->trans, &fc, conn->msize) < 0) {
            np_logerr (srv, "recv error - "
                       "dropping connection to '%s'",
                       conn->client_id);
            break;
        }
        if (!fc) /* EOF */
            break;
        _debug_trace (srv, fc);

        /* Encapsulate fc in a request and hand to srv worker threads.
         * In np_req_alloc, req->fid is looked up/initialized.
         */
        req = np_req_alloc(conn, fc);
        if (!req) {
            np_logmsg (srv, "out of memory in receive path - "
                       "dropping connection to '%s'",
                       conn->client_id);
            free (fc);
            break;
        }

        /* Enqueue request for processing by next available worker
         * thread, except P9_TFLUSH which is handled immediately.
         */
        if (fc->type == P9_TFLUSH) {
            if (np_flush (req, fc)) {
                np_req_respond_flush (req);
                np_req_unref(req);
            }
            xpthread_mutex_lock (&srv->lock);
            srv->tpool->stats.nreqs[P9_TFLUSH]++;
            xpthread_mutex_unlock (&srv->lock);
        } else {
            xpthread_mutex_lock(&srv->lock);
            np_srv_add_req(srv, req);
            xpthread_mutex_unlock(&srv->lock);
        }
    }
    /* Just got EOF on read, or some other fatal error for the
     * connection like out of memory.
     */

    np_conn_flush (conn);

    xpthread_mutex_lock(&conn->lock);
    while (conn->refcount > 0)
        xpthread_cond_wait(&conn->refcond, &conn->lock);
    xpthread_mutex_unlock(&conn->lock);
    np_conn_destroy(conn);

    return NULL;
}
Esempio n. 28
0
Npfcall*
np_flush(Npreq *req, Npfcall *tc)
{
	u16 oldtag = tc->u.tflush.oldtag;
	Npreq *creq = NULL;
	Npconn *conn = req->conn;
	Npfcall *ret = NULL;
	Nptpool *tp;

	xpthread_mutex_lock(&conn->srv->lock);
	// check pending requests
	for (tp = conn->srv->tpool; tp != NULL; tp = tp->next) {
		xpthread_mutex_lock(&tp->lock);
		for(creq = tp->reqs_first; creq != NULL; creq = creq->next) {
			if (creq->conn==conn && creq->tag==oldtag) {
				np_srv_remove_req(tp, creq);
				xpthread_mutex_lock(&creq->lock);
				np_conn_respond(creq); /* doesn't send anything */
				xpthread_mutex_unlock(&creq->lock);
				np_req_unref(creq);
				ret = np_create_rflush();
				creq = NULL;
				xpthread_mutex_unlock(&tp->lock);
				goto done;
			}
		}
		xpthread_mutex_unlock(&tp->lock);
	}

	// check working requests
	for (tp = conn->srv->tpool; tp != NULL; tp = tp->next) {
		xpthread_mutex_lock(&tp->lock);
		creq = tp->workreqs;
		while (creq != NULL) {
			if (creq->conn==conn && creq->tag==oldtag) {
				np_req_ref(creq);
				xpthread_mutex_lock(&creq->lock);
				req->flushreq = creq->flushreq;
				creq->flushreq = req;
				xpthread_mutex_unlock(&creq->lock);
				xpthread_mutex_unlock(&tp->lock);
				goto done;
			}
			creq = creq->next;
		}
		xpthread_mutex_unlock(&tp->lock);
	}

	// if not found, return P9_RFLUSH
	if (!creq)
		ret = np_create_rflush();

done:
	xpthread_mutex_unlock(&conn->srv->lock);

	// if working request found, try to flush it
	if (creq && req->conn->srv->flush) {
		(*req->conn->srv->flush)(creq);
		np_req_unref(creq);
	}

	return ret;
}
Esempio n. 29
0
static Npfcall*
np_process_request(Npreq *req, Npstats *stats)
{
	Npfcall *rc = NULL;
	Npfcall *tc = req->tcall;
	int ecode, valid_op = 1;
	u64 rbytes = 0, wbytes = 0;

	np_uerror(0);
	switch (tc->type) {
		case P9_TSTATFS:
			rc = np_statfs(req, tc);
			break;
		case P9_TLOPEN:
			rc = np_lopen(req, tc);
			break;
		case P9_TLCREATE:
			rc = np_lcreate(req, tc);
			break;
		case P9_TSYMLINK:
			rc = np_symlink(req, tc);
			break;
		case P9_TMKNOD:
			rc = np_mknod(req, tc);
			break;
		case P9_TRENAME:
			rc = np_rename(req, tc);
			break;
		case P9_TREADLINK:
			rc = np_readlink(req, tc);
			break;
		case P9_TGETATTR:
			rc = np_getattr(req, tc);
			break;
		case P9_TSETATTR:
			rc = np_setattr(req, tc);
			break;
		case P9_TXATTRWALK:
			rc = np_xattrwalk(req, tc);
			break;
		case P9_TXATTRCREATE:
			rc = np_xattrcreate(req, tc);
			break;
		case P9_TREADDIR:
			rc = np_readdir(req, tc);
			break;
		case P9_TFSYNC:
			rc = np_fsync(req, tc);
			break;
		case P9_TLOCK:
			rc = np_lock(req, tc);
			break;
		case P9_TGETLOCK:
			rc = np_getlock(req, tc);
			break;
		case P9_TLINK:
			rc = np_link(req, tc);
			break;
		case P9_TMKDIR:
			rc = np_mkdir(req, tc);
			break;
		case P9_TVERSION:
			rc = np_version(req, tc);
			break;
		case P9_TAUTH:
			rc = np_auth(req, tc);
			break;
		case P9_TATTACH:
			rc = np_attach(req, tc);
			break;
		case P9_TFLUSH:
			rc = np_flush(req, tc);
			break;
		case P9_TWALK:
			rc = np_walk(req, tc);
			break;
		case P9_TREAD:
			rc = np_read(req, tc);
			rbytes = rc->u.rread.count;
			break;
		case P9_TWRITE:
			rc = np_write(req, tc);
			wbytes = rc->u.rwrite.count;
			break;
		case P9_TCLUNK:
			rc = np_clunk(req, tc);
			break;
		case P9_TREMOVE:
			rc = np_remove(req, tc);
			break;
		default: /* N.B. shouldn't get here - unhandled ops are
			  * caught in np_deserialize ().
			  */
			np_uerror(ENOSYS);
			valid_op = 0;
			break;
	}
	if ((ecode = np_rerror())) {
		if (rc)
			free(rc);
		rc = np_create_rlerror(ecode);
	}
	if (valid_op) {
		xpthread_mutex_lock (&stats->lock);
		stats->rbytes += rbytes;
		stats->wbytes += wbytes;
		stats->nreqs[tc->type]++;
		xpthread_mutex_unlock (&stats->lock);
	}

	return rc;
}