Commit bd81ccea authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-3.7' of git://linux-nfs.org/~bfields/linux

Pull nfsd update from J Bruce Fields:
 "Another relatively quiet cycle.  There was some progress on my
  remaining 4.1 todo's, but a couple of them were just of the form
  "check that we do X correctly", so didn't have much affect on the
  code.

  Other than that, a bunch of cleanup and some bugfixes (including an
  annoying NFSv4.0 state leak and a busy-loop in the server that could
  cause it to peg the CPU without making progress)."

* 'for-3.7' of git://linux-nfs.org/~bfields/linux: (46 commits)
  UAPI: (Scripted) Disintegrate include/linux/sunrpc
  UAPI: (Scripted) Disintegrate include/linux/nfsd
  nfsd4: don't allow reclaims of expired clients
  nfsd4: remove redundant callback probe
  nfsd4: expire old client earlier
  nfsd4: separate session allocation and initialization
  nfsd4: clean up session allocation
  nfsd4: minor free_session cleanup
  nfsd4: new_conn_from_crses should only allocate
  nfsd4: separate connection allocation and initialization
  nfsd4: reject bad forechannel attrs earlier
  nfsd4: enforce per-client sessions/no-sessions distinction
  nfsd4: set cl_minorversion at create time
  nfsd4: don't pin clientids to pseudoflavors
  nfsd4: fix bind_conn_to_session xdr comment
  nfsd4: cast readlink() bug argument
  NFSD: pass null terminated buf to kstrtouint()
  nfsd: remove duplicate init in nfsd4_cb_recall
  nfsd4: eliminate redundant nfs4_free_stateid
  fs/nfsd/nfs4idmap.c: adjust inconsistent IS_ERR and PTR_ERR
  ...
parents 98260daa a9ca4043
Administrative interfaces for nfsd
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note that normally these interfaces are used only by the utilities in
nfs-utils.
nfsd is controlled mainly by pseudofiles under the "nfsd" filesystem,
which is normally mounted at /proc/fs/nfsd/.
The server is always started by the first write of a nonzero value to
nfsd/threads.
Before doing that, NFSD can be told which sockets to listen on by
writing to nfsd/portlist; that write may be:
- an ascii-encoded file descriptor, which should refer to a
bound (and listening, for tcp) socket, or
- "transportname port", where transportname is currently either
"udp", "tcp", or "rdma".
If nfsd is started without doing any of these, then it will create one
udp and one tcp listener at port 2049 (see nfsd_init_socks).
On startup, nfsd and lockd grace periods start.
nfsd is shut down by a write of 0 to nfsd/threads. All locks and state
are thrown away at that point.
Between startup and shutdown, the number of threads may be adjusted up
or down by additional writes to nfsd/threads or by writes to
nfsd/pool_threads.
For more detail about files under nfsd/ and what they control, see
fs/nfsd/nfsctl.c; most of them have detailed comments.
Implementation notes
^^^^^^^^^^^^^^^^^^^^
Note that the rpc server requires the caller to serialize addition and
removal of listening sockets, and startup and shutdown of the server.
For nfsd this is done using nfsd_mutex.
......@@ -126,7 +126,7 @@ static void restart_grace(void)
static int
lockd(void *vrqstp)
{
int err = 0, preverr = 0;
int err = 0;
struct svc_rqst *rqstp = vrqstp;
/* try_to_freeze() is called from svc_recv() */
......@@ -165,21 +165,8 @@ lockd(void *vrqstp)
* recvfrom routine.
*/
err = svc_recv(rqstp, timeout);
if (err == -EAGAIN || err == -EINTR) {
preverr = err;
if (err == -EAGAIN || err == -EINTR)
continue;
}
if (err < 0) {
if (err != preverr) {
printk(KERN_WARNING "%s: unexpected error "
"from svc_recv (%d)\n", __func__, err);
preverr = err;
}
schedule_timeout_interruptible(HZ);
continue;
}
preverr = err;
dprintk("lockd: request from %s\n",
svc_print_addr(rqstp, buf, sizeof(buf)));
......
......@@ -1289,7 +1289,7 @@ EXPORT_SYMBOL(__break_lease);
void lease_get_mtime(struct inode *inode, struct timespec *time)
{
struct file_lock *flock = inode->i_flock;
if (flock && IS_LEASE(flock) && (flock->fl_type & F_WRLCK))
if (flock && IS_LEASE(flock) && (flock->fl_type == F_WRLCK))
*time = current_fs_time(inode->i_sb);
else
*time = inode->i_mtime;
......@@ -2185,8 +2185,8 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
} else {
seq_printf(f, "%s ",
(lease_breaking(fl))
? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ "
: (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
? (fl->fl_type == F_UNLCK) ? "UNLCK" : "READ "
: (fl->fl_type == F_WRLCK) ? "WRITE" : "READ ");
}
if (inode) {
#ifdef WE_CAN_BREAK_LSLK_NOW
......
......@@ -72,7 +72,7 @@ static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
static int
nfs4_callback_svc(void *vrqstp)
{
int err, preverr = 0;
int err;
struct svc_rqst *rqstp = vrqstp;
set_freezable();
......@@ -82,20 +82,8 @@ nfs4_callback_svc(void *vrqstp)
* Listen for a request on the socket
*/
err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
if (err == -EAGAIN || err == -EINTR) {
preverr = err;
if (err == -EAGAIN || err == -EINTR)
continue;
}
if (err < 0) {
if (err != preverr) {
printk(KERN_WARNING "NFS: %s: unexpected error "
"from svc_recv (%d)\n", __func__, err);
preverr = err;
}
schedule_timeout_uninterruptible(HZ);
continue;
}
preverr = err;
svc_process(rqstp);
}
return 0;
......
......@@ -218,8 +218,7 @@ static int nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p,
* There must be an encoding function for void results so svc_process
* will work properly.
*/
int
nfsaclsvc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
static int nfsaclsvc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
{
return xdr_ressize_check(rqstp, p);
}
......
......@@ -247,7 +247,7 @@ nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
/* Now create the file and set attributes */
nfserr = do_nfsd_create(rqstp, dirfhp, argp->name, argp->len,
attr, newfhp,
argp->createmode, argp->verf, NULL, NULL);
argp->createmode, (u32 *)argp->verf, NULL, NULL);
RETURN_STATUS(nfserr);
}
......
......@@ -1028,7 +1028,6 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp)
cb->cb_msg.rpc_cred = callback_cred;
cb->cb_ops = &nfsd4_cb_recall_ops;
dp->dl_retries = 1;
INIT_LIST_HEAD(&cb->cb_per_client);
cb->cb_done = true;
......
......@@ -478,7 +478,7 @@ nfsd_idmap_init(struct net *net)
goto destroy_idtoname_cache;
nn->nametoid_cache = cache_create_net(&nametoid_cache_template, net);
if (IS_ERR(nn->nametoid_cache)) {
rv = PTR_ERR(nn->idtoname_cache);
rv = PTR_ERR(nn->nametoid_cache);
goto unregister_idtoname_cache;
}
rv = cache_register_net(nn->nametoid_cache, net);
......@@ -598,7 +598,7 @@ numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namel
/* Just to make sure it's null-terminated: */
memcpy(buf, name, namelen);
buf[namelen] = '\0';
ret = kstrtouint(name, 10, id);
ret = kstrtouint(buf, 10, id);
return ret == 0;
}
......
......@@ -370,7 +370,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
break;
case NFS4_OPEN_CLAIM_PREVIOUS:
open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
status = nfs4_check_open_reclaim(&open->op_clientid);
status = nfs4_check_open_reclaim(&open->op_clientid, cstate->minorversion);
if (status)
goto out;
case NFS4_OPEN_CLAIM_FH:
......@@ -1054,8 +1054,8 @@ struct nfsd4_operation {
char *op_name;
/* Try to get response size before operation */
nfsd4op_rsize op_rsize_bop;
stateid_setter op_get_currentstateid;
stateid_getter op_set_currentstateid;
stateid_getter op_get_currentstateid;
stateid_setter op_set_currentstateid;
};
static struct nfsd4_operation nfsd4_ops[];
......
......@@ -758,7 +758,7 @@ static void nfsd4_put_drc_mem(int slotsize, int num)
spin_unlock(&nfsd_drc_lock);
}
static struct nfsd4_session *alloc_session(int slotsize, int numslots)
static struct nfsd4_session *__alloc_session(int slotsize, int numslots)
{
struct nfsd4_session *new;
int mem, i;
......@@ -852,35 +852,28 @@ static int nfsd4_register_conn(struct nfsd4_conn *conn)
return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
}
static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses, u32 dir)
static void nfsd4_init_conn(struct svc_rqst *rqstp, struct nfsd4_conn *conn, struct nfsd4_session *ses)
{
struct nfsd4_conn *conn;
int ret;
conn = alloc_conn(rqstp, dir);
if (!conn)
return nfserr_jukebox;
nfsd4_hash_conn(conn, ses);
ret = nfsd4_register_conn(conn);
if (ret)
/* oops; xprt is already down: */
nfsd4_conn_lost(&conn->cn_xpt_user);
if (ses->se_client->cl_cb_state == NFSD4_CB_DOWN &&
dir & NFS4_CDFC4_BACK) {
if (conn->cn_flags & NFS4_CDFC4_BACK) {
/* callback channel may be back up */
nfsd4_probe_callback(ses->se_client);
}
return nfs_ok;
}
static __be32 nfsd4_new_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_session *ses)
static struct nfsd4_conn *alloc_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_create_session *cses)
{
u32 dir = NFS4_CDFC4_FORE;
if (ses->se_flags & SESSION4_BACK_CHAN)
if (cses->flags & SESSION4_BACK_CHAN)
dir |= NFS4_CDFC4_BACK;
return nfsd4_new_conn(rqstp, ses, dir);
return alloc_conn(rqstp, dir);
}
/* must be called under client_lock */
......@@ -903,20 +896,21 @@ static void nfsd4_del_conns(struct nfsd4_session *s)
spin_unlock(&clp->cl_lock);
}
static void __free_session(struct nfsd4_session *ses)
{
nfsd4_put_drc_mem(slot_bytes(&ses->se_fchannel), ses->se_fchannel.maxreqs);
free_session_slots(ses);
kfree(ses);
}
static void free_session(struct kref *kref)
{
struct nfsd4_session *ses;
int mem;
lockdep_assert_held(&client_lock);
ses = container_of(kref, struct nfsd4_session, se_ref);
nfsd4_del_conns(ses);
spin_lock(&nfsd_drc_lock);
mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
nfsd_drc_mem_used -= mem;
spin_unlock(&nfsd_drc_lock);
free_session_slots(ses);
kfree(ses);
__free_session(ses);
}
void nfsd4_put_session(struct nfsd4_session *ses)
......@@ -926,14 +920,10 @@ void nfsd4_put_session(struct nfsd4_session *ses)
spin_unlock(&client_lock);
}
static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan)
{
struct nfsd4_session *new;
struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
int numslots, slotsize;
__be32 status;
int idx;
/*
* Note decreasing slot size below client's request may
* make it difficult for client to function correctly, whereas
......@@ -946,12 +936,18 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
if (numslots < 1)
return NULL;
new = alloc_session(slotsize, numslots);
new = __alloc_session(slotsize, numslots);
if (!new) {
nfsd4_put_drc_mem(slotsize, fchan->maxreqs);
return NULL;
}
init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize);
return new;
}
static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses)
{
int idx;
new->se_client = clp;
gen_sessionid(new);
......@@ -970,14 +966,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
spin_unlock(&clp->cl_lock);
spin_unlock(&client_lock);
status = nfsd4_new_conn_from_crses(rqstp, new);
/* whoops: benny points out, status is ignored! (err, or bogus) */
if (status) {
spin_lock(&client_lock);
free_session(&new->se_ref);
spin_unlock(&client_lock);
return NULL;
}
if (cses->flags & SESSION4_BACK_CHAN) {
struct sockaddr *sa = svc_addr(rqstp);
/*
......@@ -990,7 +978,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa);
clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
}
nfsd4_probe_callback(clp);
return new;
}
......@@ -1131,7 +1118,7 @@ unhash_client_locked(struct nfs4_client *clp)
}
static void
expire_client(struct nfs4_client *clp)
destroy_client(struct nfs4_client *clp)
{
struct nfs4_openowner *oo;
struct nfs4_delegation *dp;
......@@ -1165,6 +1152,12 @@ expire_client(struct nfs4_client *clp)
spin_unlock(&client_lock);
}
static void expire_client(struct nfs4_client *clp)
{
nfsd4_client_record_remove(clp);
destroy_client(clp);
}
static void copy_verf(struct nfs4_client *target, nfs4_verifier *source)
{
memcpy(target->cl_verifier.data, source->data,
......@@ -1223,10 +1216,26 @@ static bool groups_equal(struct group_info *g1, struct group_info *g2)
return true;
}
/*
* RFC 3530 language requires clid_inuse be returned when the
* "principal" associated with a requests differs from that previously
* used. We use uid, gid's, and gss principal string as our best
* approximation. We also don't want to allow non-gss use of a client
* established using gss: in theory cr_principal should catch that
* change, but in practice cr_principal can be null even in the gss case
* since gssd doesn't always pass down a principal string.
*/
static bool is_gss_cred(struct svc_cred *cr)
{
/* Is cr_flavor one of the gss "pseudoflavors"?: */
return (cr->cr_flavor > RPC_AUTH_MAXFLAVOR);
}
static bool
same_creds(struct svc_cred *cr1, struct svc_cred *cr2)
{
if ((cr1->cr_flavor != cr2->cr_flavor)
if ((is_gss_cred(cr1) != is_gss_cred(cr2))
|| (cr1->cr_uid != cr2->cr_uid)
|| (cr1->cr_gid != cr2->cr_gid)
|| !groups_equal(cr1->cr_group_info, cr2->cr_group_info))
......@@ -1340,13 +1349,15 @@ move_to_confirmed(struct nfs4_client *clp)
}
static struct nfs4_client *
find_confirmed_client(clientid_t *clid)
find_confirmed_client(clientid_t *clid, bool sessions)
{
struct nfs4_client *clp;
unsigned int idhashval = clientid_hashval(clid->cl_id);
list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) {
if (same_clid(&clp->cl_clientid, clid)) {
if ((bool)clp->cl_minorversion != sessions)
return NULL;
renew_client(clp);
return clp;
}
......@@ -1355,14 +1366,17 @@ find_confirmed_client(clientid_t *clid)
}
static struct nfs4_client *
find_unconfirmed_client(clientid_t *clid)
find_unconfirmed_client(clientid_t *clid, bool sessions)
{
struct nfs4_client *clp;
unsigned int idhashval = clientid_hashval(clid->cl_id);
list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) {
if (same_clid(&clp->cl_clientid, clid))
if (same_clid(&clp->cl_clientid, clid)) {
if ((bool)clp->cl_minorversion != sessions)
return NULL;
return clp;
}
}
return NULL;
}
......@@ -1651,6 +1665,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
status = nfserr_jukebox;
goto out;
}
new->cl_minorversion = 1;
gen_clid(new);
add_to_unconfirmed(new, strhashval);
......@@ -1743,67 +1758,71 @@ nfsd4_create_session(struct svc_rqst *rqstp,
struct sockaddr *sa = svc_addr(rqstp);
struct nfs4_client *conf, *unconf;
struct nfsd4_session *new;
struct nfsd4_conn *conn;
struct nfsd4_clid_slot *cs_slot = NULL;
bool confirm_me = false;
__be32 status = 0;
if (cr_ses->flags & ~SESSION4_FLAG_MASK_A)
return nfserr_inval;
if (check_forechannel_attrs(cr_ses->fore_channel))
return nfserr_toosmall;
new = alloc_session(&cr_ses->fore_channel);
if (!new)
return nfserr_jukebox;
status = nfserr_jukebox;
conn = alloc_conn_from_crses(rqstp, cr_ses);
if (!conn)
goto out_free_session;
nfs4_lock_state();
unconf = find_unconfirmed_client(&cr_ses->clientid);
conf = find_confirmed_client(&cr_ses->clientid);
unconf = find_unconfirmed_client(&cr_ses->clientid, true);
conf = find_confirmed_client(&cr_ses->clientid, true);
if (conf) {
cs_slot = &conf->cl_cs_slot;
status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
if (status == nfserr_replay_cache) {
status = nfsd4_replay_create_session(cr_ses, cs_slot);
goto out;
goto out_free_conn;
} else if (cr_ses->seqid != cs_slot->sl_seqid + 1) {
status = nfserr_seq_misordered;
goto out;
goto out_free_conn;
}
} else if (unconf) {
unsigned int hash;
struct nfs4_client *old;
if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
!rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
status = nfserr_clid_inuse;
goto out;
goto out_free_conn;
}
cs_slot = &unconf->cl_cs_slot;
status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
if (status) {
/* an unconfirmed replay returns misordered */
status = nfserr_seq_misordered;
goto out;
goto out_free_conn;
}
confirm_me = true;
hash = clientstr_hashval(unconf->cl_recdir);
old = find_confirmed_client_by_str(unconf->cl_recdir, hash);
if (old)
expire_client(old);
move_to_confirmed(unconf);
conf = unconf;
} else {
status = nfserr_stale_clientid;
goto out;
goto out_free_conn;
}
/*
* XXX: we should probably set this at creation time, and check
* for consistent minorversion use throughout:
*/
conf->cl_minorversion = 1;
status = nfs_ok;
/*
* We do not support RDMA or persistent sessions
*/
cr_ses->flags &= ~SESSION4_PERSIST;
cr_ses->flags &= ~SESSION4_RDMA;
status = nfserr_toosmall;
if (check_forechannel_attrs(cr_ses->fore_channel))
goto out;
init_session(rqstp, new, conf, cr_ses);
nfsd4_init_conn(rqstp, conn, new);
status = nfserr_jukebox;
new = alloc_init_session(rqstp, conf, cr_ses);
if (!new)
goto out;
status = nfs_ok;
memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
NFS4_MAX_SESSIONID_LEN);
memcpy(&cr_ses->fore_channel, &new->se_fchannel,
......@@ -1813,18 +1832,15 @@ nfsd4_create_session(struct svc_rqst *rqstp,
/* cache solo and embedded create sessions under the state lock */
nfsd4_cache_create_session(cr_ses, cs_slot, status);
if (confirm_me) {
unsigned int hash = clientstr_hashval(unconf->cl_recdir);
struct nfs4_client *old =
find_confirmed_client_by_str(conf->cl_recdir, hash);
if (old)
expire_client(old);
move_to_confirmed(conf);
}
out:
nfs4_unlock_state();
dprintk("%s returns %d\n", __func__, ntohl(status));
return status;
out_free_conn:
free_conn(conn);
out_free_session:
__free_session(new);
goto out;
}
static bool nfsd4_last_compound_op(struct svc_rqst *rqstp)
......@@ -1854,6 +1870,7 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
struct nfsd4_bind_conn_to_session *bcts)
{
__be32 status;
struct nfsd4_conn *conn;
if (!nfsd4_last_compound_op(rqstp))
return nfserr_not_only_op;
......@@ -1870,9 +1887,13 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
return nfserr_badsession;
status = nfsd4_map_bcts_dir(&bcts->dir);
if (!status)
nfsd4_new_conn(rqstp, cstate->session, bcts->dir);
return status;
if (status)
return status;
conn = alloc_conn(rqstp, bcts->dir);
if (!conn)
return nfserr_jukebox;
nfsd4_init_conn(rqstp, conn, cstate->session);
return nfs_ok;
}
static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid)
......@@ -2085,8 +2106,8 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
__be32 status = 0;
nfs4_lock_state();
unconf = find_unconfirmed_client(&dc->clientid);
conf = find_confirmed_client(&dc->clientid);
unconf = find_unconfirmed_client(&dc->clientid, true);
conf = find_confirmed_client(&dc->clientid, true);
if (conf) {
clp = conf;
......@@ -2200,10 +2221,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
copy_clid(new, conf);
else /* case 4 (new client) or cases 2, 3 (client reboot): */
gen_clid(new);
/*
* XXX: we should probably set this at creation time, and check
* for consistent minorversion use throughout:
*/
new->cl_minorversion = 0;
gen_callback(new, setclid, rqstp);
add_to_unconfirmed(new, strhashval);
......@@ -2232,8 +2249,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
return nfserr_stale_clientid;
nfs4_lock_state();
conf = find_confirmed_client(clid);
unconf = find_unconfirmed_client(clid);
conf = find_confirmed_client(clid, false);
unconf = find_unconfirmed_client(clid, false);
/*
* We try hard to give out unique clientid's, so if we get an
* attempt to confirm the same clientid with a different cred,
......@@ -2262,10 +2279,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
unsigned int hash = clientstr_hashval(unconf->cl_recdir);
conf = find_confirmed_client_by_str(unconf->cl_recdir, hash);
if (conf) {
nfsd4_client_record_remove(conf);
if (conf)
expire_client(conf);
}
move_to_confirmed(unconf);
nfsd4_probe_callback(unconf);
}
......@@ -2447,16 +2462,20 @@ same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner,
}
static struct nfs4_openowner *
find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open)
find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, bool sessions)
{
struct nfs4_stateowner *so;
struct nfs4_openowner *oo;
struct nfs4_client *clp;
list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) {
if (!so->so_is_open_owner)
continue;
if (same_owner_str(so, &open->op_owner, &open->op_clientid)) {
oo = openowner(so);
clp = oo->oo_owner.so_client;
if ((bool)clp->cl_minorversion != sessions)
return NULL;
renew_client(oo->oo_owner.so_client);
return oo;
}
......@@ -2600,10 +2619,10 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
return nfserr_jukebox;
strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner);
oo = find_openstateowner_str(strhashval, open);
oo = find_openstateowner_str(strhashval, open, cstate->minorversion);
open->op_openowner = oo;
if (!oo) {
clp = find_confirmed_client(clientid);
clp = find_confirmed_client(clientid, cstate->minorversion);
if (clp == NULL)
return nfserr_expired;
goto new_owner;
......@@ -2705,11 +2724,6 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st
return nfs_ok;
}
static void nfs4_free_stateid(struct nfs4_ol_stateid *s)
{
kmem_cache_free(stateid_slab, s);
}
static inline int nfs4_access_to_access(u32 nfs4_access)
{
int flags = 0;
......@@ -3087,7 +3101,7 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)
if (open->op_file)
nfsd4_free_file(open->op_file);
if (open->op_stp)
nfs4_free_stateid(open->op_stp);
free_generic_stateid(open->op_stp);
}
__be32
......@@ -3104,7 +3118,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_stale_clientid;
if (STALE_CLIENTID(clid, nn))
goto out;
clp = find_confirmed_client(clid);
clp = find_confirmed_client(clid, cstate->minorversion);
status = nfserr_expired;
if (clp == NULL) {
/* We assume the client took too long to RENEW. */
......@@ -3180,7 +3194,6 @@ nfs4_laundromat(void)
clp = list_entry(pos, struct nfs4_client, cl_lru);
dprintk("NFSD: purging unused client (clientid %08x)\n",
clp->cl_clientid.cl_id);
nfsd4_client_record_remove(clp);
expire_client(clp);
}
spin_lock(&recall_lock);
......@@ -3372,7 +3385,7 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
return nfs_ok;
}
static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s)
static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s, bool sessions)
{
struct nfs4_client *cl;
struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
......@@ -3381,7 +3394,7 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, s
return nfserr_bad_stateid;
if (STALE_STATEID(stateid, nn))
return nfserr_stale_stateid;
cl = find_confirmed_client(&stateid->si_opaque.so_clid);
cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions);
if (!cl)
return nfserr_expired;
*s = find_stateid_by_type(cl, stateid, typemask);
......@@ -3414,7 +3427,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
return check_special_stateids(net, current_fh, stateid, flags);
status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s);
status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s, cstate->minorversion);
if (status)
return status;
status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
......@@ -3564,7 +3577,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
seqid, STATEID_VAL(stateid));
*stpp = NULL;
status = nfsd4_lookup_stateid(stateid, typemask, &s);
status = nfsd4_lookup_stateid(stateid, typemask, &s, cstate->minorversion);
if (status)
return status;
*stpp = openlockstateid(s);
......@@ -3765,6 +3778,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
nfsd4_close_open_stateid(stp);
release_last_closed_stateid(oo);
oo->oo_last_closed_stid = stp;
if (list_empty(&oo->oo_owner.so_stateids)) {
......@@ -3801,7 +3815,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
inode = cstate->current_fh.fh_dentry->d_inode;
nfs4_lock_state();
status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s);
status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, cstate->minorversion);
if (status)
goto out;
dp = delegstateid(s);
......@@ -4045,8 +4059,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfs4_lockowner *lock_sop = NULL;
struct nfs4_ol_stateid *lock_stp;
struct file *filp = NULL;
struct file_lock file_lock;
struct file_lock conflock;
struct file_lock *file_lock = NULL;
struct file_lock *conflock = NULL;
__be32 status = 0;
bool new_state = false;
int lkflg;
......@@ -4116,21 +4130,28 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim)
goto out;
locks_init_lock(&file_lock);
file_lock = locks_alloc_lock();
if (!file_lock) {
dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
status = nfserr_jukebox;
goto out;
}
locks_init_lock(file_lock);
switch (lock->lk_type) {
case NFS4_READ_LT:
case NFS4_READW_LT:
filp = find_readable_file(lock_stp->st_file);
if (filp)
get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
file_lock.fl_type = F_RDLCK;
file_lock->fl_type = F_RDLCK;
break;
case NFS4_WRITE_LT:
case NFS4_WRITEW_LT:
filp = find_writeable_file(lock_stp->st_file);
if (filp)
get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
file_lock.fl_type = F_WRLCK;
file_lock->fl_type = F_WRLCK;
break;
default:
status = nfserr_inval;
......@@ -4140,22 +4161,23 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_openmode;
goto out;
}
file_lock.fl_owner = (fl_owner_t)lock_sop;
file_lock.fl_pid = current->tgid;
file_lock.fl_file = filp;
file_lock.fl_flags = FL_POSIX;
file_lock.fl_lmops = &nfsd_posix_mng_ops;
file_lock.fl_start = lock->lk_offset;
file_lock.fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
nfs4_transform_lock_offset(&file_lock);
/*
* Try to lock the file in the VFS.
* Note: locks.c uses the BKL to protect the inode's lock list.
*/
file_lock->fl_owner = (fl_owner_t)lock_sop;
file_lock->fl_pid = current->tgid;
file_lock->fl_file = filp;
file_lock->fl_flags = FL_POSIX;
file_lock->fl_lmops = &nfsd_posix_mng_ops;
file_lock->fl_start = lock->lk_offset;
file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
nfs4_transform_lock_offset(file_lock);
conflock = locks_alloc_lock();
if (!conflock) {
dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
status = nfserr_jukebox;
goto out;
}
err = vfs_lock_file(filp, F_SETLK, &file_lock, &conflock);
err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
switch (-err) {
case 0: /* success! */
update_stateid(&lock_stp->st_stid.sc_stateid);
......@@ -4166,7 +4188,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
case (EAGAIN): /* conflock holds conflicting lock */
status = nfserr_denied;
dprintk("NFSD: nfsd4_lock: conflicting lock found!\n");
nfs4_set_lock_denied(&conflock, &lock->lk_denied);
nfs4_set_lock_denied(conflock, &lock->lk_denied);
break;
case (EDEADLK):
status = nfserr_deadlock;
......@@ -4181,6 +4203,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
release_lockowner(lock_sop);
if (!cstate->replay_owner)
nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
if (conflock)
locks_free_lock(conflock);
return status;
}
......@@ -4209,7 +4235,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_lockt *lockt)
{
struct inode *inode;
struct file_lock file_lock;
struct file_lock *file_lock = NULL;
struct nfs4_lockowner *lo;
__be32 status;
struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
......@@ -4230,15 +4256,21 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out;
inode = cstate->current_fh.fh_dentry->d_inode;
locks_init_lock(&file_lock);
file_lock = locks_alloc_lock();
if (!file_lock) {
dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
status = nfserr_jukebox;
goto out;
}
locks_init_lock(file_lock);
switch (lockt->lt_type) {
case NFS4_READ_LT:
case NFS4_READW_LT:
file_lock.fl_type = F_RDLCK;
file_lock->fl_type = F_RDLCK;
break;
case NFS4_WRITE_LT:
case NFS4_WRITEW_LT:
file_lock.fl_type = F_WRLCK;
file_lock->fl_type = F_WRLCK;
break;
default:
dprintk("NFSD: nfs4_lockt: bad lock type!\n");
......@@ -4248,25 +4280,27 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner);
if (lo)
file_lock.fl_owner = (fl_owner_t)lo;
file_lock.fl_pid = current->tgid;
file_lock.fl_flags = FL_POSIX;
file_lock->fl_owner = (fl_owner_t)lo;
file_lock->fl_pid = current->tgid;
file_lock->fl_flags = FL_POSIX;
file_lock.fl_start = lockt->lt_offset;
file_lock.fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length);
file_lock->fl_start = lockt->lt_offset;
file_lock->fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length);
nfs4_transform_lock_offset(&file_lock);
nfs4_transform_lock_offset(file_lock);
status = nfsd_test_lock(rqstp, &cstate->current_fh, &file_lock);
status = nfsd_test_lock(rqstp, &cstate->current_fh, file_lock);
if (status)
goto out;
if (file_lock.fl_type != F_UNLCK) {
if (file_lock->fl_type != F_UNLCK) {
status = nfserr_denied;
nfs4_set_lock_denied(&file_lock, &lockt->lt_denied);
nfs4_set_lock_denied(file_lock, &lockt->lt_denied);
}
out:
nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
return status;
}
......@@ -4276,7 +4310,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
{
struct nfs4_ol_stateid *stp;
struct file *filp = NULL;
struct file_lock file_lock;
struct file_lock *file_lock = NULL;
__be32 status;
int err;
......@@ -4298,23 +4332,29 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_lock_range;
goto out;
}
BUG_ON(!filp);
locks_init_lock(&file_lock);
file_lock.fl_type = F_UNLCK;
file_lock.fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
file_lock.fl_pid = current->tgid;
file_lock.fl_file = filp;
file_lock.fl_flags = FL_POSIX;
file_lock.fl_lmops = &nfsd_posix_mng_ops;
file_lock.fl_start = locku->lu_offset;
file_lock.fl_end = last_byte_offset(locku->lu_offset, locku->lu_length);
nfs4_transform_lock_offset(&file_lock);
file_lock = locks_alloc_lock();
if (!file_lock) {
dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
status = nfserr_jukebox;
goto out;
}
locks_init_lock(file_lock);
file_lock->fl_type = F_UNLCK;
file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
file_lock->fl_pid = current->tgid;
file_lock->fl_file = filp;
file_lock->fl_flags = FL_POSIX;
file_lock->fl_lmops = &nfsd_posix_mng_ops;
file_lock->fl_start = locku->lu_offset;
file_lock->fl_end = last_byte_offset(locku->lu_offset,
locku->lu_length);
nfs4_transform_lock_offset(file_lock);
/*
* Try to unlock the file in the VFS.
*/
err = vfs_lock_file(filp, F_SETLK, &file_lock, NULL);
err = vfs_lock_file(filp, F_SETLK, file_lock, NULL);
if (err) {
dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n");
goto out_nfserr;
......@@ -4328,6 +4368,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
out:
if (!cstate->replay_owner)
nfs4_unlock_state();
if (file_lock)
locks_free_lock(file_lock);
return status;
out_nfserr:
......@@ -4501,12 +4543,12 @@ nfsd4_find_reclaim_client(struct nfs4_client *clp)
* Called from OPEN. Look for clientid in reclaim list.
*/
__be32
nfs4_check_open_reclaim(clientid_t *clid)
nfs4_check_open_reclaim(clientid_t *clid, bool sessions)
{
struct nfs4_client *clp;
/* find clientid in conf_id_hashtbl */
clp = find_confirmed_client(clid);
clp = find_confirmed_client(clid, sessions);
if (clp == NULL)
return nfserr_reclaim_bad;
......@@ -4522,7 +4564,6 @@ void nfsd_forget_clients(u64 num)
nfs4_lock_state();
list_for_each_entry_safe(clp, next, &client_lru, cl_lru) {
nfsd4_client_record_remove(clp);
expire_client(clp);
if (++count == num)
break;
......@@ -4582,7 +4623,7 @@ void nfsd_forget_openowners(u64 num)
printk(KERN_INFO "NFSD: Forgot %d open owners", count);
}
int nfsd_process_n_delegations(u64 num, struct list_head *list)
static int nfsd_process_n_delegations(u64 num, struct list_head *list)
{
int i, count = 0;
struct nfs4_file *fp, *fnext;
......@@ -4747,11 +4788,11 @@ __nfs4_state_shutdown(void)
for (i = 0; i < CLIENT_HASH_SIZE; i++) {
while (!list_empty(&conf_id_hashtbl[i])) {
clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash);
expire_client(clp);
destroy_client(clp);
}
while (!list_empty(&unconf_str_hashtbl[i])) {
clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash);
expire_client(clp);
destroy_client(clp);
}
}
INIT_LIST_HEAD(&reaplist);
......
......@@ -2659,7 +2659,7 @@ static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp,
RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 8);
WRITEMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN);
WRITE32(bcts->dir);
/* XXX: ? */
/* Sorry, we do not yet support RDMA over 4.1: */
WRITE32(0);
ADJUST_ARGS();
}
......
......@@ -406,7 +406,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
return rv;
if (newthreads < 0)
return -EINVAL;
rv = nfsd_svc(NFS_PORT, newthreads);
rv = nfsd_svc(newthreads);
if (rv < 0)
return rv;
} else
......@@ -682,25 +682,6 @@ static ssize_t __write_ports_addfd(char *buf)
return err;
}
/*
* A '-' followed by the 'name' of a socket means we close the socket.
*/
static ssize_t __write_ports_delfd(char *buf)
{
char *toclose;
int len = 0;
toclose = kstrdup(buf + 1, GFP_KERNEL);
if (toclose == NULL)
return -ENOMEM;
if (nfsd_serv != NULL)
len = svc_sock_names(nfsd_serv, buf,
SIMPLE_TRANSACTION_LIMIT, toclose);
kfree(toclose);
return len;
}
/*
* A transport listener is added by writing it's transport name and
* a port number.
......@@ -712,7 +693,7 @@ static ssize_t __write_ports_addxprt(char *buf)
int port, err;
struct net *net = &init_net;
if (sscanf(buf, "%15s %4u", transport, &port) != 2)
if (sscanf(buf, "%15s %5u", transport, &port) != 2)
return -EINVAL;
if (port < 1 || port > USHRT_MAX)
......@@ -746,31 +727,6 @@ static ssize_t __write_ports_addxprt(char *buf)
return err;
}
/*
* A transport listener is removed by writing a "-", it's transport
* name, and it's port number.
*/
static ssize_t __write_ports_delxprt(char *buf)
{
struct svc_xprt *xprt;
char transport[16];
int port;
if (sscanf(&buf[1], "%15s %4u", transport, &port) != 2)
return -EINVAL;
if (port < 1 || port > USHRT_MAX || nfsd_serv == NULL)
return -EINVAL;
xprt = svc_find_xprt(nfsd_serv, transport, &init_net, AF_UNSPEC, port);
if (xprt == NULL)
return -ENOTCONN;
svc_close_xprt(xprt);
svc_xprt_put(xprt);
return 0;
}
static ssize_t __write_ports(struct file *file, char *buf, size_t size)
{
if (size == 0)
......@@ -779,15 +735,9 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size)
if (isdigit(buf[0]))
return __write_ports_addfd(buf);
if (buf[0] == '-' && isdigit(buf[1]))
return __write_ports_delfd(buf);
if (isalpha(buf[0]))
return __write_ports_addxprt(buf);
if (buf[0] == '-' && isalpha(buf[1]))
return __write_ports_delxprt(buf);
return -EINVAL;
}
......@@ -825,21 +775,6 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size)
* OR
*
* Input:
* buf: C string containing a "-" followed
* by an integer value representing a
* previously passed in socket file
* descriptor
* size: non-zero length of C string in @buf
* Output:
* On success: NFS service no longer listens on that socket;
* passed-in buffer filled with a '\n'-terminated C
* string containing a unique name of the listener;
* return code is the size in bytes of the string
* On error: return code is a negative errno value
*
* OR
*
* Input:
* buf: C string containing a transport
* name and an unsigned integer value
* representing the port to listen on,
......@@ -848,19 +783,6 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size)
* Output:
* On success: returns zero; NFS service is started
* On error: return code is a negative errno value
*
* OR
*
* Input:
* buf: C string containing a "-" followed
* by a transport name and an unsigned
* integer value representing the port
* to listen on, separated by whitespace
* size: non-zero length of C string in @buf
* Output:
* On success: returns zero; NFS service no longer listens
* on that transport
* On error: return code is a negative errno value
*/
static ssize_t write_ports(struct file *file, char *buf, size_t size)
{
......@@ -1008,8 +930,6 @@ static ssize_t write_gracetime(struct file *file, char *buf, size_t size)
return nfsd4_write_time(file, buf, size, &nfsd4_grace);
}
extern char *nfs4_recoverydir(void);
static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
{
char *mesg = buf;
......
......@@ -65,7 +65,7 @@ extern const struct seq_operations nfs_exports_op;
/*
* Function prototypes.
*/
int nfsd_svc(unsigned short port, int nrservs);
int nfsd_svc(int nrservs);
int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp);
int nfsd_nrthreads(void);
......@@ -124,6 +124,7 @@ int nfs4_state_start(void);
void nfs4_state_shutdown(void);
void nfs4_reset_lease(time_t leasetime);
int nfs4_reset_recoverydir(char *recdir);
char * nfs4_recoverydir(void);
#else
static inline void nfs4_state_init(void) { }
static inline int nfsd4_init_slabs(void) { return 0; }
......@@ -132,6 +133,7 @@ static inline int nfs4_state_start(void) { return 0; }
static inline void nfs4_state_shutdown(void) { }
static inline void nfs4_reset_lease(time_t leasetime) { }
static inline int nfs4_reset_recoverydir(char *recdir) { return 0; }
static inline char * nfs4_recoverydir(void) {return NULL; }
#endif
/*
......
......@@ -183,18 +183,18 @@ int nfsd_nrthreads(void)
return rv;
}
static int nfsd_init_socks(int port)
static int nfsd_init_socks(void)
{
int error;
if (!list_empty(&nfsd_serv->sv_permsocks))
return 0;
error = svc_create_xprt(nfsd_serv, "udp", &init_net, PF_INET, port,
error = svc_create_xprt(nfsd_serv, "udp", &init_net, PF_INET, NFS_PORT,
SVC_SOCK_DEFAULTS);
if (error < 0)
return error;
error = svc_create_xprt(nfsd_serv, "tcp", &init_net, PF_INET, port,
error = svc_create_xprt(nfsd_serv, "tcp", &init_net, PF_INET, NFS_PORT,
SVC_SOCK_DEFAULTS);
if (error < 0)
return error;
......@@ -204,7 +204,7 @@ static int nfsd_init_socks(int port)
static bool nfsd_up = false;
static int nfsd_startup(unsigned short port, int nrservs)
static int nfsd_startup(int nrservs)
{
int ret;
......@@ -218,7 +218,7 @@ static int nfsd_startup(unsigned short port, int nrservs)
ret = nfsd_racache_init(2*nrservs);
if (ret)
return ret;
ret = nfsd_init_socks(port);
ret = nfsd_init_socks();
if (ret)
goto out_racache;
ret = lockd_up(&init_net);
......@@ -436,7 +436,7 @@ int nfsd_set_nrthreads(int n, int *nthreads)
* this is the first time nrservs is nonzero.
*/
int
nfsd_svc(unsigned short port, int nrservs)
nfsd_svc(int nrservs)
{
int error;
bool nfsd_up_before;
......@@ -458,7 +458,7 @@ nfsd_svc(unsigned short port, int nrservs)
nfsd_up_before = nfsd_up;
error = nfsd_startup(port, nrservs);
error = nfsd_startup(nrservs);
if (error)
goto out_destroy;
error = svc_set_num_threads(nfsd_serv, NULL, nrservs);
......@@ -487,7 +487,7 @@ static int
nfsd(void *vrqstp)
{
struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp;
int err, preverr = 0;
int err;
/* Lock module and set up kernel thread */
mutex_lock(&nfsd_mutex);
......@@ -534,16 +534,6 @@ nfsd(void *vrqstp)
;
if (err == -EINTR)
break;
else if (err < 0) {
if (err != preverr) {
printk(KERN_WARNING "%s: unexpected error "
"from svc_recv (%d)\n", __func__, -err);
preverr = err;
}
schedule_timeout_uninterruptible(HZ);
continue;
}
validate_process_creds();
svc_process(rqstp);
validate_process_creds();
......
......@@ -373,11 +373,7 @@ static inline struct nfs4_lockowner * lockowner(struct nfs4_stateowner *so)
return container_of(so, struct nfs4_lockowner, lo_owner);
}
/*
* nfs4_file: a file opened by some number of (open) nfs4_stateowners.
* o fi_perfile list is used to search for conflicting
* share_acces, share_deny on the file.
*/
/* nfs4_file: a file opened by some number of (open) nfs4_stateowners. */
struct nfs4_file {
atomic_t fi_ref;
struct list_head fi_hash; /* hash by "struct inode *" */
......@@ -459,7 +455,7 @@ extern void nfs4_unlock_state(void);
extern int nfs4_in_grace(void);
extern void nfs4_release_reclaim(void);
extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(struct nfs4_client *crp);
extern __be32 nfs4_check_open_reclaim(clientid_t *clid);
extern __be32 nfs4_check_open_reclaim(clientid_t *clid, bool sessions);
extern void nfs4_free_openowner(struct nfs4_openowner *);
extern void nfs4_free_lockowner(struct nfs4_lockowner *);
extern int set_callback_cred(void);
......
......@@ -1581,7 +1581,7 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
*/
oldfs = get_fs(); set_fs(KERNEL_DS);
host_err = inode->i_op->readlink(path.dentry, buf, *lenp);
host_err = inode->i_op->readlink(path.dentry, (char __user *)buf, *lenp);
set_fs(oldfs);
if (host_err < 0)
......
header-y += cld.h
header-y += debug.h
header-y += export.h
header-y += nfsfh.h
header-y += stats.h
......@@ -5,44 +5,15 @@
*
* Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef LINUX_NFSD_DEBUG_H
#define LINUX_NFSD_DEBUG_H
#include <linux/sunrpc/debug.h>
#include <uapi/linux/nfsd/debug.h>
/*
* Enable debugging for nfsd.
* Requires RPC_DEBUG.
*/
#ifdef RPC_DEBUG
# define NFSD_DEBUG 1
#endif
/*
* knfsd debug flags
*/
#define NFSDDBG_SOCK 0x0001
#define NFSDDBG_FH 0x0002
#define NFSDDBG_EXPORT 0x0004
#define NFSDDBG_SVC 0x0008
#define NFSDDBG_PROC 0x0010
#define NFSDDBG_FILEOP 0x0020
#define NFSDDBG_AUTH 0x0040
#define NFSDDBG_REPCACHE 0x0080
#define NFSDDBG_XDR 0x0100
#define NFSDDBG_LOCKD 0x0200
#define NFSDDBG_ALL 0x7FFF
#define NFSDDBG_NOCHANGE 0xFFFF
#ifdef __KERNEL__
# undef ifdebug
# ifdef NFSD_DEBUG
# define ifdebug(flag) if (nfsd_debug & NFSDDBG_##flag)
# else
# define ifdebug(flag) if (0)
# endif
#endif /* __KERNEL__ */
#endif /* LINUX_NFSD_DEBUG_H */
......@@ -6,58 +6,11 @@
*
* Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef NFSD_EXPORT_H
#define NFSD_EXPORT_H
# include <linux/types.h>
#ifdef __KERNEL__
# include <linux/nfsd/nfsfh.h>
#endif
/*
* Important limits for the exports stuff.
*/
#define NFSCLNT_IDMAX 1024
#define NFSCLNT_ADDRMAX 16
#define NFSCLNT_KEYMAX 32
/*
* Export flags.
*/
#define NFSEXP_READONLY 0x0001
#define NFSEXP_INSECURE_PORT 0x0002
#define NFSEXP_ROOTSQUASH 0x0004
#define NFSEXP_ALLSQUASH 0x0008
#define NFSEXP_ASYNC 0x0010
#define NFSEXP_GATHERED_WRITES 0x0020
/* 40 80 100 currently unused */
#define NFSEXP_NOHIDE 0x0200
#define NFSEXP_NOSUBTREECHECK 0x0400
#define NFSEXP_NOAUTHNLM 0x0800 /* Don't authenticate NLM requests - just trust */
#define NFSEXP_MSNFS 0x1000 /* do silly things that MS clients expect; no longer supported */
#define NFSEXP_FSID 0x2000
#define NFSEXP_CROSSMOUNT 0x4000
#define NFSEXP_NOACL 0x8000 /* reserved for possible ACL related use */
/*
* The NFSEXP_V4ROOT flag causes the kernel to give access only to NFSv4
* clients, and only to the single directory that is the root of the
* export; further lookup and readdir operations are treated as if every
* subdirectory was a mountpoint, and ignored if they are not themselves
* exported. This is used by nfsd and mountd to construct the NFSv4
* pseudofilesystem, which provides access only to paths leading to each
* exported filesystem.
*/
#define NFSEXP_V4ROOT 0x10000
/* All flags that we claim to support. (Note we don't support NOACL.) */
#define NFSEXP_ALLFLAGS 0x17E3F
/* The flags that may vary depending on security flavor: */
#define NFSEXP_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \
| NFSEXP_ALLSQUASH \
| NFSEXP_INSECURE_PORT)
#ifdef __KERNEL__
#include <uapi/linux/nfsd/export.h>
/*
* FS Locations
......@@ -154,7 +107,4 @@ static inline void exp_get(struct svc_export *exp)
}
struct svc_export * rqst_exp_find(struct svc_rqst *, int, u32 *);
#endif /* __KERNEL__ */
#endif /* NFSD_EXPORT_H */
......@@ -10,117 +10,11 @@
*
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _LINUX_NFSD_FH_H
#define _LINUX_NFSD_FH_H
#include <linux/types.h>
#include <linux/nfs.h>
#include <linux/nfs2.h>
#include <linux/nfs3.h>
#include <linux/nfs4.h>
#ifdef __KERNEL__
# include <linux/sunrpc/svc.h>
#endif
/*
* This is the old "dentry style" Linux NFSv2 file handle.
*
* The xino and xdev fields are currently used to transport the
* ino/dev of the exported inode.
*/
struct nfs_fhbase_old {
__u32 fb_dcookie; /* dentry cookie - always 0xfeebbaca */
__u32 fb_ino; /* our inode number */
__u32 fb_dirino; /* dir inode number, 0 for directories */
__u32 fb_dev; /* our device */
__u32 fb_xdev;
__u32 fb_xino;
__u32 fb_generation;
};
/*
* This is the new flexible, extensible style NFSv2/v3 file handle.
* by Neil Brown <neilb@cse.unsw.edu.au> - March 2000
*
* The file handle starts with a sequence of four-byte words.
* The first word contains a version number (1) and three descriptor bytes
* that tell how the remaining 3 variable length fields should be handled.
* These three bytes are auth_type, fsid_type and fileid_type.
*
* All four-byte values are in host-byte-order.
*
* The auth_type field specifies how the filehandle can be authenticated
* This might allow a file to be confirmed to be in a writable part of a
* filetree without checking the path from it up to the root.
* Current values:
* 0 - No authentication. fb_auth is 0 bytes long
* Possible future values:
* 1 - 4 bytes taken from MD5 hash of the remainer of the file handle
* prefixed by a secret and with the important export flags.
*
* The fsid_type identifies how the filesystem (or export point) is
* encoded.
* Current values:
* 0 - 4 byte device id (ms-2-bytes major, ls-2-bytes minor), 4byte inode number
* NOTE: we cannot use the kdev_t device id value, because kdev_t.h
* says we mustn't. We must break it up and reassemble.
* 1 - 4 byte user specified identifier
* 2 - 4 byte major, 4 byte minor, 4 byte inode number - DEPRECATED
* 3 - 4 byte device id, encoded for user-space, 4 byte inode number
* 4 - 4 byte inode number and 4 byte uuid
* 5 - 8 byte uuid
* 6 - 16 byte uuid
* 7 - 8 byte inode number and 16 byte uuid
*
* The fileid_type identified how the file within the filesystem is encoded.
* This is (will be) passed to, and set by, the underlying filesystem if it supports
* filehandle operations. The filesystem must not use the value '0' or '0xff' and may
* only use the values 1 and 2 as defined below:
* Current values:
* 0 - The root, or export point, of the filesystem. fb_fileid is 0 bytes.
* 1 - 32bit inode number, 32 bit generation number.
* 2 - 32bit inode number, 32 bit generation number, 32 bit parent directory inode number.
*
*/
struct nfs_fhbase_new {
__u8 fb_version; /* == 1, even => nfs_fhbase_old */
__u8 fb_auth_type;
__u8 fb_fsid_type;
__u8 fb_fileid_type;
__u32 fb_auth[1];
/* __u32 fb_fsid[0]; floating */
/* __u32 fb_fileid[0]; floating */
};
struct knfsd_fh {
unsigned int fh_size; /* significant for NFSv3.
* Points to the current size while building
* a new file handle
*/
union {
struct nfs_fhbase_old fh_old;
__u32 fh_pad[NFS4_FHSIZE/4];
struct nfs_fhbase_new fh_new;
} fh_base;
};
#define ofh_dcookie fh_base.fh_old.fb_dcookie
#define ofh_ino fh_base.fh_old.fb_ino
#define ofh_dirino fh_base.fh_old.fb_dirino
#define ofh_dev fh_base.fh_old.fb_dev
#define ofh_xdev fh_base.fh_old.fb_xdev
#define ofh_xino fh_base.fh_old.fb_xino
#define ofh_generation fh_base.fh_old.fb_generation
#define fh_version fh_base.fh_new.fb_version
#define fh_fsid_type fh_base.fh_new.fb_fsid_type
#define fh_auth_type fh_base.fh_new.fb_auth_type
#define fh_fileid_type fh_base.fh_new.fb_fileid_type
#define fh_auth fh_base.fh_new.fb_auth
#define fh_fsid fh_base.fh_new.fb_auth
#ifdef __KERNEL__
#include <uapi/linux/nfsd/nfsfh.h>
static inline __u32 ino_t_to_u32(ino_t ino)
{
......@@ -166,7 +60,4 @@ typedef struct svc_fh {
} svc_fh;
#endif /* __KERNEL__ */
#endif /* _LINUX_NFSD_FH_H */
......@@ -5,16 +5,11 @@
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef LINUX_NFSD_STATS_H
#define LINUX_NFSD_STATS_H
#include <linux/nfs4.h>
/* thread usage wraps very million seconds (approx one fortnight) */
#define NFSD_USAGE_WRAP (HZ*1000000)
#include <uapi/linux/nfsd/stats.h>
#ifdef __KERNEL__
struct nfsd_stats {
unsigned int rchits; /* repcache hits */
......@@ -47,5 +42,4 @@ extern struct svc_stat nfsd_svcstats;
void nfsd_stat_init(void);
void nfsd_stat_shutdown(void);
#endif /* __KERNEL__ */
#endif /* LINUX_NFSD_STATS_H */
......@@ -5,28 +5,11 @@
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _LINUX_SUNRPC_DEBUG_H_
#define _LINUX_SUNRPC_DEBUG_H_
/*
* RPC debug facilities
*/
#define RPCDBG_XPRT 0x0001
#define RPCDBG_CALL 0x0002
#define RPCDBG_DEBUG 0x0004
#define RPCDBG_NFS 0x0008
#define RPCDBG_AUTH 0x0010
#define RPCDBG_BIND 0x0020
#define RPCDBG_SCHED 0x0040
#define RPCDBG_TRANS 0x0080
#define RPCDBG_SVCXPRT 0x0100
#define RPCDBG_SVCDSP 0x0200
#define RPCDBG_MISC 0x0400
#define RPCDBG_CACHE 0x0800
#define RPCDBG_ALL 0x7fff
#include <uapi/linux/sunrpc/debug.h>
#ifdef __KERNEL__
/*
* Enable RPC debugging/profiling.
......@@ -87,24 +70,4 @@ void rpc_register_sysctl(void);
void rpc_unregister_sysctl(void);
#endif
#endif /* __KERNEL__ */
/*
* Declarations for the sysctl debug interface, which allows to read or
* change the debug flags for rpc, nfs, nfsd, and lockd. Since the sunrpc
* module currently registers its sysctl table dynamically, the sysctl path
* for module FOO is <CTL_SUNRPC, CTL_FOODEBUG>.
*/
enum {
CTL_RPCDEBUG = 1,
CTL_NFSDEBUG,
CTL_NFSDDEBUG,
CTL_NLMDEBUG,
CTL_SLOTTABLE_UDP,
CTL_SLOTTABLE_TCP,
CTL_MIN_RESVPORT,
CTL_MAX_RESVPORT,
};
#endif /* _LINUX_SUNRPC_DEBUG_H_ */
......@@ -114,7 +114,6 @@ void svc_xprt_init(struct net *, struct svc_xprt_class *, struct svc_xprt *,
int svc_create_xprt(struct svc_serv *, const char *, struct net *,
const int, const unsigned short, int);
void svc_xprt_enqueue(struct svc_xprt *xprt);
void svc_xprt_received(struct svc_xprt *);
void svc_xprt_put(struct svc_xprt *xprt);
void svc_xprt_copy_addrs(struct svc_rqst *rqstp, struct svc_xprt *xprt);
void svc_close_xprt(struct svc_xprt *xprt);
......@@ -124,6 +123,7 @@ struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name,
struct net *net, const sa_family_t af,
const unsigned short port);
int svc_xprt_names(struct svc_serv *serv, char *buf, const int buflen);
void svc_add_new_perm_xprt(struct svc_serv *serv, struct svc_xprt *xprt);
static inline void svc_xprt_get(struct svc_xprt *xprt)
{
......@@ -166,8 +166,7 @@ static inline size_t svc_addr_len(const struct sockaddr *sa)
case AF_INET6:
return sizeof(struct sockaddr_in6);
}
return 0;
BUG();
}
static inline unsigned short svc_xprt_local_port(const struct svc_xprt *xprt)
......
......@@ -39,9 +39,6 @@ int svc_recv(struct svc_rqst *, long);
int svc_send(struct svc_rqst *);
void svc_drop(struct svc_rqst *);
void svc_sock_update_bufs(struct svc_serv *serv);
int svc_sock_names(struct svc_serv *serv, char *buf,
const size_t buflen,
const char *toclose);
int svc_addsock(struct svc_serv *serv, const int fd,
char *name_return, const size_t len);
void svc_init_xprt_sock(void);
......
# UAPI Header export list
header-y += cld.h
header-y += debug.h
header-y += export.h
header-y += nfsfh.h
header-y += stats.h
/*
* linux/include/linux/nfsd/debug.h
*
* Debugging-related stuff for nfsd
*
* Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _UAPILINUX_NFSD_DEBUG_H
#define _UAPILINUX_NFSD_DEBUG_H
#include <linux/sunrpc/debug.h>
/*
* Enable debugging for nfsd.
* Requires RPC_DEBUG.
*/
#ifdef RPC_DEBUG
# define NFSD_DEBUG 1
#endif
/*
* knfsd debug flags
*/
#define NFSDDBG_SOCK 0x0001
#define NFSDDBG_FH 0x0002
#define NFSDDBG_EXPORT 0x0004
#define NFSDDBG_SVC 0x0008
#define NFSDDBG_PROC 0x0010
#define NFSDDBG_FILEOP 0x0020
#define NFSDDBG_AUTH 0x0040
#define NFSDDBG_REPCACHE 0x0080
#define NFSDDBG_XDR 0x0100
#define NFSDDBG_LOCKD 0x0200
#define NFSDDBG_ALL 0x7FFF
#define NFSDDBG_NOCHANGE 0xFFFF
#endif /* _UAPILINUX_NFSD_DEBUG_H */
/*
* include/linux/nfsd/export.h
*
* Public declarations for NFS exports. The definitions for the
* syscall interface are in nfsctl.h
*
* Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _UAPINFSD_EXPORT_H
#define _UAPINFSD_EXPORT_H
# include <linux/types.h>
/*
* Important limits for the exports stuff.
*/
#define NFSCLNT_IDMAX 1024
#define NFSCLNT_ADDRMAX 16
#define NFSCLNT_KEYMAX 32
/*
* Export flags.
*/
#define NFSEXP_READONLY 0x0001
#define NFSEXP_INSECURE_PORT 0x0002
#define NFSEXP_ROOTSQUASH 0x0004
#define NFSEXP_ALLSQUASH 0x0008
#define NFSEXP_ASYNC 0x0010
#define NFSEXP_GATHERED_WRITES 0x0020
/* 40 80 100 currently unused */
#define NFSEXP_NOHIDE 0x0200
#define NFSEXP_NOSUBTREECHECK 0x0400
#define NFSEXP_NOAUTHNLM 0x0800 /* Don't authenticate NLM requests - just trust */
#define NFSEXP_MSNFS 0x1000 /* do silly things that MS clients expect; no longer supported */
#define NFSEXP_FSID 0x2000
#define NFSEXP_CROSSMOUNT 0x4000
#define NFSEXP_NOACL 0x8000 /* reserved for possible ACL related use */
/*
* The NFSEXP_V4ROOT flag causes the kernel to give access only to NFSv4
* clients, and only to the single directory that is the root of the
* export; further lookup and readdir operations are treated as if every
* subdirectory was a mountpoint, and ignored if they are not themselves
* exported. This is used by nfsd and mountd to construct the NFSv4
* pseudofilesystem, which provides access only to paths leading to each
* exported filesystem.
*/
#define NFSEXP_V4ROOT 0x10000
/* All flags that we claim to support. (Note we don't support NOACL.) */
#define NFSEXP_ALLFLAGS 0x17E3F
/* The flags that may vary depending on security flavor: */
#define NFSEXP_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \
| NFSEXP_ALLSQUASH \
| NFSEXP_INSECURE_PORT)
#endif /* _UAPINFSD_EXPORT_H */
/*
* include/linux/nfsd/nfsfh.h
*
* This file describes the layout of the file handles as passed
* over the wire.
*
* Earlier versions of knfsd used to sign file handles using keyed MD5
* or SHA. I've removed this code, because it doesn't give you more
* security than blocking external access to port 2049 on your firewall.
*
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _UAPI_LINUX_NFSD_FH_H
#define _UAPI_LINUX_NFSD_FH_H
#include <linux/types.h>
#include <linux/nfs.h>
#include <linux/nfs2.h>
#include <linux/nfs3.h>
#include <linux/nfs4.h>
/*
* This is the old "dentry style" Linux NFSv2 file handle.
*
* The xino and xdev fields are currently used to transport the
* ino/dev of the exported inode.
*/
struct nfs_fhbase_old {
__u32 fb_dcookie; /* dentry cookie - always 0xfeebbaca */
__u32 fb_ino; /* our inode number */
__u32 fb_dirino; /* dir inode number, 0 for directories */
__u32 fb_dev; /* our device */
__u32 fb_xdev;
__u32 fb_xino;
__u32 fb_generation;
};
/*
* This is the new flexible, extensible style NFSv2/v3 file handle.
* by Neil Brown <neilb@cse.unsw.edu.au> - March 2000
*
* The file handle starts with a sequence of four-byte words.
* The first word contains a version number (1) and three descriptor bytes
* that tell how the remaining 3 variable length fields should be handled.
* These three bytes are auth_type, fsid_type and fileid_type.
*
* All four-byte values are in host-byte-order.
*
* The auth_type field specifies how the filehandle can be authenticated
* This might allow a file to be confirmed to be in a writable part of a
* filetree without checking the path from it up to the root.
* Current values:
* 0 - No authentication. fb_auth is 0 bytes long
* Possible future values:
* 1 - 4 bytes taken from MD5 hash of the remainer of the file handle
* prefixed by a secret and with the important export flags.
*
* The fsid_type identifies how the filesystem (or export point) is
* encoded.
* Current values:
* 0 - 4 byte device id (ms-2-bytes major, ls-2-bytes minor), 4byte inode number
* NOTE: we cannot use the kdev_t device id value, because kdev_t.h
* says we mustn't. We must break it up and reassemble.
* 1 - 4 byte user specified identifier
* 2 - 4 byte major, 4 byte minor, 4 byte inode number - DEPRECATED
* 3 - 4 byte device id, encoded for user-space, 4 byte inode number
* 4 - 4 byte inode number and 4 byte uuid
* 5 - 8 byte uuid
* 6 - 16 byte uuid
* 7 - 8 byte inode number and 16 byte uuid
*
* The fileid_type identified how the file within the filesystem is encoded.
* This is (will be) passed to, and set by, the underlying filesystem if it supports
* filehandle operations. The filesystem must not use the value '0' or '0xff' and may
* only use the values 1 and 2 as defined below:
* Current values:
* 0 - The root, or export point, of the filesystem. fb_fileid is 0 bytes.
* 1 - 32bit inode number, 32 bit generation number.
* 2 - 32bit inode number, 32 bit generation number, 32 bit parent directory inode number.
*
*/
struct nfs_fhbase_new {
__u8 fb_version; /* == 1, even => nfs_fhbase_old */
__u8 fb_auth_type;
__u8 fb_fsid_type;
__u8 fb_fileid_type;
__u32 fb_auth[1];
/* __u32 fb_fsid[0]; floating */
/* __u32 fb_fileid[0]; floating */
};
struct knfsd_fh {
unsigned int fh_size; /* significant for NFSv3.
* Points to the current size while building
* a new file handle
*/
union {
struct nfs_fhbase_old fh_old;
__u32 fh_pad[NFS4_FHSIZE/4];
struct nfs_fhbase_new fh_new;
} fh_base;
};
#define ofh_dcookie fh_base.fh_old.fb_dcookie
#define ofh_ino fh_base.fh_old.fb_ino
#define ofh_dirino fh_base.fh_old.fb_dirino
#define ofh_dev fh_base.fh_old.fb_dev
#define ofh_xdev fh_base.fh_old.fb_xdev
#define ofh_xino fh_base.fh_old.fb_xino
#define ofh_generation fh_base.fh_old.fb_generation
#define fh_version fh_base.fh_new.fb_version
#define fh_fsid_type fh_base.fh_new.fb_fsid_type
#define fh_auth_type fh_base.fh_new.fb_auth_type
#define fh_fileid_type fh_base.fh_new.fb_fileid_type
#define fh_auth fh_base.fh_new.fb_auth
#define fh_fsid fh_base.fh_new.fb_auth
#endif /* _UAPI_LINUX_NFSD_FH_H */
/*
* linux/include/linux/nfsd/stats.h
*
* Statistics for NFS server.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _UAPILINUX_NFSD_STATS_H
#define _UAPILINUX_NFSD_STATS_H
#include <linux/nfs4.h>
/* thread usage wraps very million seconds (approx one fortnight) */
#define NFSD_USAGE_WRAP (HZ*1000000)
#endif /* _UAPILINUX_NFSD_STATS_H */
# UAPI Header export list
header-y += debug.h
/*
* linux/include/linux/sunrpc/debug.h
*
* Debugging support for sunrpc module
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _UAPI_LINUX_SUNRPC_DEBUG_H_
#define _UAPI_LINUX_SUNRPC_DEBUG_H_
/*
* RPC debug facilities
*/
#define RPCDBG_XPRT 0x0001
#define RPCDBG_CALL 0x0002
#define RPCDBG_DEBUG 0x0004
#define RPCDBG_NFS 0x0008
#define RPCDBG_AUTH 0x0010
#define RPCDBG_BIND 0x0020
#define RPCDBG_SCHED 0x0040
#define RPCDBG_TRANS 0x0080
#define RPCDBG_SVCXPRT 0x0100
#define RPCDBG_SVCDSP 0x0200
#define RPCDBG_MISC 0x0400
#define RPCDBG_CACHE 0x0800
#define RPCDBG_ALL 0x7fff
/*
* Declarations for the sysctl debug interface, which allows to read or
* change the debug flags for rpc, nfs, nfsd, and lockd. Since the sunrpc
* module currently registers its sysctl table dynamically, the sysctl path
* for module FOO is <CTL_SUNRPC, CTL_FOODEBUG>.
*/
enum {
CTL_RPCDEBUG = 1,
CTL_NFSDEBUG,
CTL_NFSDDEBUG,
CTL_NLMDEBUG,
CTL_SLOTTABLE_UDP,
CTL_SLOTTABLE_TCP,
CTL_MIN_RESVPORT,
CTL_MAX_RESVPORT,
};
#endif /* _UAPI_LINUX_SUNRPC_DEBUG_H_ */
......@@ -208,6 +208,35 @@ static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
return xcl->xcl_ops->xpo_create(serv, net, sap, len, flags);
}
/*
* svc_xprt_received conditionally queues the transport for processing
* by another thread. The caller must hold the XPT_BUSY bit and must
* not thereafter touch transport data.
*
* Note: XPT_DATA only gets cleared when a read-attempt finds no (or
* insufficient) data.
*/
static void svc_xprt_received(struct svc_xprt *xprt)
{
BUG_ON(!test_bit(XPT_BUSY, &xprt->xpt_flags));
/* As soon as we clear busy, the xprt could be closed and
* 'put', so we need a reference to call svc_xprt_enqueue with:
*/
svc_xprt_get(xprt);
clear_bit(XPT_BUSY, &xprt->xpt_flags);
svc_xprt_enqueue(xprt);
svc_xprt_put(xprt);
}
void svc_add_new_perm_xprt(struct svc_serv *serv, struct svc_xprt *new)
{
clear_bit(XPT_TEMP, &new->xpt_flags);
spin_lock_bh(&serv->sv_lock);
list_add(&new->xpt_list, &serv->sv_permsocks);
spin_unlock_bh(&serv->sv_lock);
svc_xprt_received(new);
}
int svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
struct net *net, const int family,
const unsigned short port, int flags)
......@@ -232,13 +261,8 @@ int svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
module_put(xcl->xcl_owner);
return PTR_ERR(newxprt);
}
clear_bit(XPT_TEMP, &newxprt->xpt_flags);
spin_lock_bh(&serv->sv_lock);
list_add(&newxprt->xpt_list, &serv->sv_permsocks);
spin_unlock_bh(&serv->sv_lock);
svc_add_new_perm_xprt(serv, newxprt);
newport = svc_xprt_local_port(newxprt);
clear_bit(XPT_BUSY, &newxprt->xpt_flags);
return newport;
}
err:
......@@ -394,27 +418,6 @@ static struct svc_xprt *svc_xprt_dequeue(struct svc_pool *pool)
return xprt;
}
/*
* svc_xprt_received conditionally queues the transport for processing
* by another thread. The caller must hold the XPT_BUSY bit and must
* not thereafter touch transport data.
*
* Note: XPT_DATA only gets cleared when a read-attempt finds no (or
* insufficient) data.
*/
void svc_xprt_received(struct svc_xprt *xprt)
{
BUG_ON(!test_bit(XPT_BUSY, &xprt->xpt_flags));
/* As soon as we clear busy, the xprt could be closed and
* 'put', so we need a reference to call svc_xprt_enqueue with:
*/
svc_xprt_get(xprt);
clear_bit(XPT_BUSY, &xprt->xpt_flags);
svc_xprt_enqueue(xprt);
svc_xprt_put(xprt);
}
EXPORT_SYMBOL_GPL(svc_xprt_received);
/**
* svc_reserve - change the space reserved for the reply to a request.
* @rqstp: The request in question
......@@ -565,33 +568,12 @@ static void svc_check_conn_limits(struct svc_serv *serv)
}
}
/*
* Receive the next request on any transport. This code is carefully
* organised not to touch any cachelines in the shared svc_serv
* structure, only cachelines in the local svc_pool.
*/
int svc_recv(struct svc_rqst *rqstp, long timeout)
int svc_alloc_arg(struct svc_rqst *rqstp)
{
struct svc_xprt *xprt = NULL;
struct svc_serv *serv = rqstp->rq_server;
struct svc_pool *pool = rqstp->rq_pool;
int len, i;
int pages;
struct xdr_buf *arg;
DECLARE_WAITQUEUE(wait, current);
long time_left;
dprintk("svc: server %p waiting for data (to = %ld)\n",
rqstp, timeout);
if (rqstp->rq_xprt)
printk(KERN_ERR
"svc_recv: service %p, transport not NULL!\n",
rqstp);
if (waitqueue_active(&rqstp->rq_wait))
printk(KERN_ERR
"svc_recv: service %p, wait queue active!\n",
rqstp);
struct svc_serv *serv = rqstp->rq_server;
struct xdr_buf *arg;
int pages;
int i;
/* now allocate needed pages. If we get a failure, sleep briefly */
pages = (serv->sv_max_mesg + PAGE_SIZE) / PAGE_SIZE;
......@@ -621,11 +603,15 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
arg->page_len = (pages-2)*PAGE_SIZE;
arg->len = (pages-1)*PAGE_SIZE;
arg->tail[0].iov_len = 0;
return 0;
}
try_to_freeze();
cond_resched();
if (signalled() || kthread_should_stop())
return -EINTR;
struct svc_xprt *svc_get_next_xprt(struct svc_rqst *rqstp, long timeout)
{
struct svc_xprt *xprt;
struct svc_pool *pool = rqstp->rq_pool;
DECLARE_WAITQUEUE(wait, current);
long time_left;
/* Normally we will wait up to 5 seconds for any required
* cache information to be provided.
......@@ -663,7 +649,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
if (kthread_should_stop()) {
set_current_state(TASK_RUNNING);
spin_unlock_bh(&pool->sp_lock);
return -EINTR;
return ERR_PTR(-EINTR);
}
add_wait_queue(&rqstp->rq_wait, &wait);
......@@ -684,48 +670,58 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
spin_unlock_bh(&pool->sp_lock);
dprintk("svc: server %p, no data yet\n", rqstp);
if (signalled() || kthread_should_stop())
return -EINTR;
return ERR_PTR(-EINTR);
else
return -EAGAIN;
return ERR_PTR(-EAGAIN);
}
}
spin_unlock_bh(&pool->sp_lock);
return xprt;
}
void svc_add_new_temp_xprt(struct svc_serv *serv, struct svc_xprt *newxpt)
{
spin_lock_bh(&serv->sv_lock);
set_bit(XPT_TEMP, &newxpt->xpt_flags);
list_add(&newxpt->xpt_list, &serv->sv_tempsocks);
serv->sv_tmpcnt++;
if (serv->sv_temptimer.function == NULL) {
/* setup timer to age temp transports */
setup_timer(&serv->sv_temptimer, svc_age_temp_xprts,
(unsigned long)serv);
mod_timer(&serv->sv_temptimer,
jiffies + svc_conn_age_period * HZ);
}
spin_unlock_bh(&serv->sv_lock);
svc_xprt_received(newxpt);
}
static int svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt)
{
struct svc_serv *serv = rqstp->rq_server;
int len = 0;
len = 0;
if (test_bit(XPT_CLOSE, &xprt->xpt_flags)) {
dprintk("svc_recv: found XPT_CLOSE\n");
svc_delete_xprt(xprt);
/* Leave XPT_BUSY set on the dead xprt: */
goto out;
return 0;
}
if (test_bit(XPT_LISTENER, &xprt->xpt_flags)) {
struct svc_xprt *newxpt;
/*
* We know this module_get will succeed because the
* listener holds a reference too
*/
__module_get(xprt->xpt_class->xcl_owner);
svc_check_conn_limits(xprt->xpt_server);
newxpt = xprt->xpt_ops->xpo_accept(xprt);
if (newxpt) {
/*
* We know this module_get will succeed because the
* listener holds a reference too
*/
__module_get(newxpt->xpt_class->xcl_owner);
svc_check_conn_limits(xprt->xpt_server);
spin_lock_bh(&serv->sv_lock);
set_bit(XPT_TEMP, &newxpt->xpt_flags);
list_add(&newxpt->xpt_list, &serv->sv_tempsocks);
serv->sv_tmpcnt++;
if (serv->sv_temptimer.function == NULL) {
/* setup timer to age temp transports */
setup_timer(&serv->sv_temptimer,
svc_age_temp_xprts,
(unsigned long)serv);
mod_timer(&serv->sv_temptimer,
jiffies + svc_conn_age_period * HZ);
}
spin_unlock_bh(&serv->sv_lock);
svc_xprt_received(newxpt);
}
if (newxpt)
svc_add_new_temp_xprt(serv, newxpt);
} else if (xprt->xpt_ops->xpo_has_wspace(xprt)) {
/* XPT_DATA|XPT_DEFERRED case: */
dprintk("svc: server %p, pool %u, transport %p, inuse=%d\n",
rqstp, pool->sp_id, xprt,
rqstp, rqstp->rq_pool->sp_id, xprt,
atomic_read(&xprt->xpt_ref.refcount));
rqstp->rq_deferred = svc_deferred_dequeue(xprt);
if (rqstp->rq_deferred)
......@@ -736,10 +732,51 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
rqstp->rq_reserved = serv->sv_max_mesg;
atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
}
/* clear XPT_BUSY: */
svc_xprt_received(xprt);
return len;
}
/*
* Receive the next request on any transport. This code is carefully
* organised not to touch any cachelines in the shared svc_serv
* structure, only cachelines in the local svc_pool.
*/
int svc_recv(struct svc_rqst *rqstp, long timeout)
{
struct svc_xprt *xprt = NULL;
struct svc_serv *serv = rqstp->rq_server;
int len, err;
dprintk("svc: server %p waiting for data (to = %ld)\n",
rqstp, timeout);
if (rqstp->rq_xprt)
printk(KERN_ERR
"svc_recv: service %p, transport not NULL!\n",
rqstp);
if (waitqueue_active(&rqstp->rq_wait))
printk(KERN_ERR
"svc_recv: service %p, wait queue active!\n",
rqstp);
err = svc_alloc_arg(rqstp);
if (err)
return err;
try_to_freeze();
cond_resched();
if (signalled() || kthread_should_stop())
return -EINTR;
xprt = svc_get_next_xprt(rqstp, timeout);
if (IS_ERR(xprt))
return PTR_ERR(xprt);
len = svc_handle_xprt(rqstp, xprt);
/* No data, incomplete (TCP) read, or accept() */
if (len == 0 || len == -EAGAIN)
if (len <= 0)
goto out;
clear_bit(XPT_OLD, &xprt->xpt_flags);
......@@ -917,16 +954,18 @@ void svc_close_xprt(struct svc_xprt *xprt)
}
EXPORT_SYMBOL_GPL(svc_close_xprt);
static void svc_close_list(struct list_head *xprt_list, struct net *net)
static void svc_close_list(struct svc_serv *serv, struct list_head *xprt_list, struct net *net)
{
struct svc_xprt *xprt;
spin_lock(&serv->sv_lock);
list_for_each_entry(xprt, xprt_list, xpt_list) {
if (xprt->xpt_net != net)
continue;
set_bit(XPT_CLOSE, &xprt->xpt_flags);
set_bit(XPT_BUSY, &xprt->xpt_flags);
}
spin_unlock(&serv->sv_lock);
}
static void svc_clear_pools(struct svc_serv *serv, struct net *net)
......@@ -949,24 +988,28 @@ static void svc_clear_pools(struct svc_serv *serv, struct net *net)
}
}
static void svc_clear_list(struct list_head *xprt_list, struct net *net)
static void svc_clear_list(struct svc_serv *serv, struct list_head *xprt_list, struct net *net)
{
struct svc_xprt *xprt;
struct svc_xprt *tmp;
LIST_HEAD(victims);
spin_lock(&serv->sv_lock);
list_for_each_entry_safe(xprt, tmp, xprt_list, xpt_list) {
if (xprt->xpt_net != net)
continue;
svc_delete_xprt(xprt);
list_move(&xprt->xpt_list, &victims);
}
list_for_each_entry(xprt, xprt_list, xpt_list)
BUG_ON(xprt->xpt_net == net);
spin_unlock(&serv->sv_lock);
list_for_each_entry_safe(xprt, tmp, &victims, xpt_list)
svc_delete_xprt(xprt);
}
void svc_close_net(struct svc_serv *serv, struct net *net)
{
svc_close_list(&serv->sv_tempsocks, net);
svc_close_list(&serv->sv_permsocks, net);
svc_close_list(serv, &serv->sv_tempsocks, net);
svc_close_list(serv, &serv->sv_permsocks, net);
svc_clear_pools(serv, net);
/*
......@@ -974,8 +1017,8 @@ void svc_close_net(struct svc_serv *serv, struct net *net)
* svc_xprt_enqueue will not add new entries without taking the
* sp_lock and checking XPT_BUSY.
*/
svc_clear_list(&serv->sv_tempsocks, net);
svc_clear_list(&serv->sv_permsocks, net);
svc_clear_list(serv, &serv->sv_tempsocks, net);
svc_clear_list(serv, &serv->sv_permsocks, net);
}
/*
......
......@@ -59,7 +59,7 @@
static struct svc_sock *svc_setup_socket(struct svc_serv *, struct socket *,
int *errp, int flags);
int flags);
static void svc_udp_data_ready(struct sock *, int);
static int svc_udp_recvfrom(struct svc_rqst *);
static int svc_udp_sendto(struct svc_rqst *);
......@@ -305,57 +305,6 @@ static int svc_one_sock_name(struct svc_sock *svsk, char *buf, int remaining)
return len;
}
/**
* svc_sock_names - construct a list of listener names in a string
* @serv: pointer to RPC service
* @buf: pointer to a buffer to fill in with socket names
* @buflen: size of the buffer to be filled
* @toclose: pointer to '\0'-terminated C string containing the name
* of a listener to be closed
*
* Fills in @buf with a '\n'-separated list of names of listener
* sockets. If @toclose is not NULL, the socket named by @toclose
* is closed, and is not included in the output list.
*
* Returns positive length of the socket name string, or a negative
* errno value on error.
*/
int svc_sock_names(struct svc_serv *serv, char *buf, const size_t buflen,
const char *toclose)
{
struct svc_sock *svsk, *closesk = NULL;
int len = 0;
if (!serv)
return 0;
spin_lock_bh(&serv->sv_lock);
list_for_each_entry(svsk, &serv->sv_permsocks, sk_xprt.xpt_list) {
int onelen = svc_one_sock_name(svsk, buf + len, buflen - len);
if (onelen < 0) {
len = onelen;
break;
}
if (toclose && strcmp(toclose, buf + len) == 0) {
closesk = svsk;
svc_xprt_get(&closesk->sk_xprt);
} else
len += onelen;
}
spin_unlock_bh(&serv->sv_lock);
if (closesk) {
/* Should unregister with portmap, but you cannot
* unregister just one protocol...
*/
svc_close_xprt(&closesk->sk_xprt);
svc_xprt_put(&closesk->sk_xprt);
} else if (toclose)
return -ENOENT;
return len;
}
EXPORT_SYMBOL_GPL(svc_sock_names);
/*
* Check input queue length
*/
......@@ -598,11 +547,9 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
dprintk("svc: recvfrom returned error %d\n", -err);
set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
}
return -EAGAIN;
return 0;
}
len = svc_addr_len(svc_addr(rqstp));
if (len == 0)
return -EAFNOSUPPORT;
rqstp->rq_addrlen = len;
if (skb->tstamp.tv64 == 0) {
skb->tstamp = ktime_get_real();
......@@ -620,10 +567,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
if (!svc_udp_get_dest_address(rqstp, cmh)) {
net_warn_ratelimited("svc: received unknown control message %d/%d; dropping RPC reply datagram\n",
cmh->cmsg_level, cmh->cmsg_type);
out_free:
trace_kfree_skb(skb, svc_udp_recvfrom);
skb_free_datagram_locked(svsk->sk_sk, skb);
return 0;
goto out_free;
}
rqstp->rq_daddrlen = svc_addr_len(svc_daddr(rqstp));
......@@ -662,6 +606,10 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
serv->sv_stats->netudpcnt++;
return len;
out_free:
trace_kfree_skb(skb, svc_udp_recvfrom);
skb_free_datagram_locked(svsk->sk_sk, skb);
return 0;
}
static int
......@@ -900,8 +848,9 @@ static struct svc_xprt *svc_tcp_accept(struct svc_xprt *xprt)
*/
newsock->sk->sk_sndtimeo = HZ*30;
if (!(newsvsk = svc_setup_socket(serv, newsock, &err,
(SVC_SOCK_ANONYMOUS | SVC_SOCK_TEMPORARY))))
newsvsk = svc_setup_socket(serv, newsock,
(SVC_SOCK_ANONYMOUS | SVC_SOCK_TEMPORARY));
if (IS_ERR(newsvsk))
goto failed;
svc_xprt_set_remote(&newsvsk->sk_xprt, sin, slen);
err = kernel_getsockname(newsock, sin, &slen);
......@@ -1174,13 +1123,13 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
if (len != -EAGAIN)
goto err_other;
dprintk("RPC: TCP recvfrom got EAGAIN\n");
return -EAGAIN;
return 0;
err_other:
printk(KERN_NOTICE "%s: recvfrom returned errno %d\n",
svsk->sk_xprt.xpt_server->sv_name, -len);
set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags);
err_noclose:
return -EAGAIN; /* record not complete */
return 0; /* record not complete */
}
/*
......@@ -1383,29 +1332,29 @@ EXPORT_SYMBOL_GPL(svc_sock_update_bufs);
*/
static struct svc_sock *svc_setup_socket(struct svc_serv *serv,
struct socket *sock,
int *errp, int flags)
int flags)
{
struct svc_sock *svsk;
struct sock *inet;
int pmap_register = !(flags & SVC_SOCK_ANONYMOUS);
int err = 0;
dprintk("svc: svc_setup_socket %p\n", sock);
if (!(svsk = kzalloc(sizeof(*svsk), GFP_KERNEL))) {
*errp = -ENOMEM;
return NULL;
}
svsk = kzalloc(sizeof(*svsk), GFP_KERNEL);
if (!svsk)
return ERR_PTR(-ENOMEM);
inet = sock->sk;
/* Register socket with portmapper */
if (*errp >= 0 && pmap_register)
*errp = svc_register(serv, sock_net(sock->sk), inet->sk_family,
if (pmap_register)
err = svc_register(serv, sock_net(sock->sk), inet->sk_family,
inet->sk_protocol,
ntohs(inet_sk(inet)->inet_sport));
if (*errp < 0) {
if (err < 0) {
kfree(svsk);
return NULL;
return ERR_PTR(err);
}
inet->sk_user_data = svsk;
......@@ -1450,42 +1399,38 @@ int svc_addsock(struct svc_serv *serv, const int fd, char *name_return,
int err = 0;
struct socket *so = sockfd_lookup(fd, &err);
struct svc_sock *svsk = NULL;
struct sockaddr_storage addr;
struct sockaddr *sin = (struct sockaddr *)&addr;
int salen;
if (!so)
return err;
err = -EAFNOSUPPORT;
if ((so->sk->sk_family != PF_INET) && (so->sk->sk_family != PF_INET6))
err = -EAFNOSUPPORT;
else if (so->sk->sk_protocol != IPPROTO_TCP &&
goto out;
err = -EPROTONOSUPPORT;
if (so->sk->sk_protocol != IPPROTO_TCP &&
so->sk->sk_protocol != IPPROTO_UDP)
err = -EPROTONOSUPPORT;
else if (so->state > SS_UNCONNECTED)
err = -EISCONN;
else {
if (!try_module_get(THIS_MODULE))
err = -ENOENT;
else
svsk = svc_setup_socket(serv, so, &err,
SVC_SOCK_DEFAULTS);
if (svsk) {
struct sockaddr_storage addr;
struct sockaddr *sin = (struct sockaddr *)&addr;
int salen;
if (kernel_getsockname(svsk->sk_sock, sin, &salen) == 0)
svc_xprt_set_local(&svsk->sk_xprt, sin, salen);
clear_bit(XPT_TEMP, &svsk->sk_xprt.xpt_flags);
spin_lock_bh(&serv->sv_lock);
list_add(&svsk->sk_xprt.xpt_list, &serv->sv_permsocks);
spin_unlock_bh(&serv->sv_lock);
svc_xprt_received(&svsk->sk_xprt);
err = 0;
} else
module_put(THIS_MODULE);
}
if (err) {
sockfd_put(so);
return err;
goto out;
err = -EISCONN;
if (so->state > SS_UNCONNECTED)
goto out;
err = -ENOENT;
if (!try_module_get(THIS_MODULE))
goto out;
svsk = svc_setup_socket(serv, so, SVC_SOCK_DEFAULTS);
if (IS_ERR(svsk)) {
module_put(THIS_MODULE);
err = PTR_ERR(svsk);
goto out;
}
if (kernel_getsockname(svsk->sk_sock, sin, &salen) == 0)
svc_xprt_set_local(&svsk->sk_xprt, sin, salen);
svc_add_new_perm_xprt(serv, &svsk->sk_xprt);
return svc_one_sock_name(svsk, name_return, len);
out:
sockfd_put(so);
return err;
}
EXPORT_SYMBOL_GPL(svc_addsock);
......@@ -1563,11 +1508,13 @@ static struct svc_xprt *svc_create_socket(struct svc_serv *serv,
goto bummer;
}
if ((svsk = svc_setup_socket(serv, sock, &error, flags)) != NULL) {
svc_xprt_set_local(&svsk->sk_xprt, newsin, newlen);
return (struct svc_xprt *)svsk;
svsk = svc_setup_socket(serv, sock, flags);
if (IS_ERR(svsk)) {
error = PTR_ERR(svsk);
goto bummer;
}
svc_xprt_set_local(&svsk->sk_xprt, newsin, newlen);
return (struct svc_xprt *)svsk;
bummer:
dprintk("svc: svc_create_socket error = %d\n", -error);
sock_release(sock);
......
......@@ -578,10 +578,6 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id, size_t client_ird)
list_add_tail(&newxprt->sc_accept_q, &listen_xprt->sc_accept_q);
spin_unlock_bh(&listen_xprt->sc_lock);
/*
* Can't use svc_xprt_received here because we are not on a
* rqstp thread
*/
set_bit(XPT_CONN, &listen_xprt->sc_xprt.xpt_flags);
svc_xprt_enqueue(&listen_xprt->sc_xprt);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment