提交 b9d919a4 编写于 作者: L Linus Torvalds

Merge branch 'nfs-for-2.6.38' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6

* 'nfs-for-2.6.38' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6: (89 commits)
  NFS fix the setting of exchange id flag
  NFS: Don't use vm_map_ram() in readdir
  NFSv4: Ensure continued open and lockowner name uniqueness
  NFS: Move cl_delegations to the nfs_server struct
  NFS: Introduce nfs_detach_delegations()
  NFS: Move cl_state_owners and related fields to the nfs_server struct
  NFS: Allow walking nfs_client.cl_superblocks list outside client.c
  pnfs: layout roc code
  pnfs: update nfs4_callback_recallany to handle layouts
  pnfs: add CB_LAYOUTRECALL handling
  pnfs: CB_LAYOUTRECALL xdr code
  pnfs: change lo refcounting to atomic_t
  pnfs: check that partial LAYOUTGET return is ignored
  pnfs: add layout to client list before sending rpc
  pnfs: serialize LAYOUTGET(openstateid)
  pnfs: layoutget rpc code cleanup
  pnfs: change how lsegs are removed from layout list
  pnfs: change layout state seqlock to a spinlock
  pnfs: add prefix to struct pnfs_layout_hdr fields
  pnfs: add prefix to struct pnfs_layout_segment fields
  ...
......@@ -4,7 +4,7 @@
obj-$(CONFIG_LOCKD) += lockd.o
lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
svcproc.o svcsubs.o mon.o xdr.o grace.o
lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o
lockd-objs-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \
svcshare.o svcproc.o svcsubs.o mon.o xdr.o grace.o
lockd-objs-$(CONFIG_LOCKD_V4) += clnt4xdr.o xdr4.o svc4proc.o
lockd-objs := $(lockd-objs-y)
/*
* linux/fs/lockd/clnt4xdr.c
*
* XDR functions to encode/decode NLM version 4 RPC arguments and results.
*
* NLM client-side only.
*
* Copyright (C) 2010, Oracle. All rights reserved.
*/
#include <linux/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/stats.h>
#include <linux/lockd/lockd.h>
#define NLMDBG_FACILITY NLMDBG_XDR
#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
#endif
#if (NLMCLNT_OHSIZE > NLM_MAXSTRLEN)
# error "NLM host name cannot be larger than NLM's maximum string length!"
#endif
/*
* Declare the space requirements for NLM arguments and replies as
* number of 32bit-words
*/
#define NLM4_void_sz (0)
#define NLM4_cookie_sz (1+(NLM_MAXCOOKIELEN>>2))
#define NLM4_caller_sz (1+(NLMCLNT_OHSIZE>>2))
#define NLM4_owner_sz (1+(NLMCLNT_OHSIZE>>2))
#define NLM4_fhandle_sz (1+(NFS3_FHSIZE>>2))
#define NLM4_lock_sz (5+NLM4_caller_sz+NLM4_owner_sz+NLM4_fhandle_sz)
#define NLM4_holder_sz (6+NLM4_owner_sz)
#define NLM4_testargs_sz (NLM4_cookie_sz+1+NLM4_lock_sz)
#define NLM4_lockargs_sz (NLM4_cookie_sz+4+NLM4_lock_sz)
#define NLM4_cancargs_sz (NLM4_cookie_sz+2+NLM4_lock_sz)
#define NLM4_unlockargs_sz (NLM4_cookie_sz+NLM4_lock_sz)
#define NLM4_testres_sz (NLM4_cookie_sz+1+NLM4_holder_sz)
#define NLM4_res_sz (NLM4_cookie_sz+1)
#define NLM4_norep_sz (0)
static s64 loff_t_to_s64(loff_t offset)
{
s64 res;
if (offset >= NLM4_OFFSET_MAX)
res = NLM4_OFFSET_MAX;
else if (offset <= -NLM4_OFFSET_MAX)
res = -NLM4_OFFSET_MAX;
else
res = offset;
return res;
}
static void nlm4_compute_offsets(const struct nlm_lock *lock,
u64 *l_offset, u64 *l_len)
{
const struct file_lock *fl = &lock->fl;
BUG_ON(fl->fl_start > NLM4_OFFSET_MAX);
BUG_ON(fl->fl_end > NLM4_OFFSET_MAX &&
fl->fl_end != OFFSET_MAX);
*l_offset = loff_t_to_s64(fl->fl_start);
if (fl->fl_end == OFFSET_MAX)
*l_len = 0;
else
*l_len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
}
/*
* Handle decode buffer overflows out-of-line.
*/
static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
{
dprintk("lockd: %s prematurely hit the end of our receive buffer. "
"Remaining buffer length is %tu words.\n",
func, xdr->end - xdr->p);
}
/*
* Encode/decode NLMv4 basic data types
*
* Basic NLMv4 data types are defined in Appendix II, section 6.1.4
* of RFC 1813: "NFS Version 3 Protocol Specification" and in Chapter
* 10 of X/Open's "Protocols for Interworking: XNFS, Version 3W".
*
* Not all basic data types have their own encoding and decoding
* functions. For run-time efficiency, some data types are encoded
* or decoded inline.
*/
static void encode_bool(struct xdr_stream *xdr, const int value)
{
__be32 *p;
p = xdr_reserve_space(xdr, 4);
*p = value ? xdr_one : xdr_zero;
}
static void encode_int32(struct xdr_stream *xdr, const s32 value)
{
__be32 *p;
p = xdr_reserve_space(xdr, 4);
*p = cpu_to_be32(value);
}
/*
* typedef opaque netobj<MAXNETOBJ_SZ>
*/
static void encode_netobj(struct xdr_stream *xdr,
const u8 *data, const unsigned int length)
{
__be32 *p;
BUG_ON(length > XDR_MAX_NETOBJ);
p = xdr_reserve_space(xdr, 4 + length);
xdr_encode_opaque(p, data, length);
}
static int decode_netobj(struct xdr_stream *xdr,
struct xdr_netobj *obj)
{
u32 length;
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
goto out_overflow;
length = be32_to_cpup(p++);
if (unlikely(length > XDR_MAX_NETOBJ))
goto out_size;
obj->len = length;
obj->data = (u8 *)p;
return 0;
out_size:
dprintk("NFS: returned netobj was too long: %u\n", length);
return -EIO;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
/*
* netobj cookie;
*/
static void encode_cookie(struct xdr_stream *xdr,
const struct nlm_cookie *cookie)
{
BUG_ON(cookie->len > NLM_MAXCOOKIELEN);
encode_netobj(xdr, (u8 *)&cookie->data, cookie->len);
}
static int decode_cookie(struct xdr_stream *xdr,
struct nlm_cookie *cookie)
{
u32 length;
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
goto out_overflow;
length = be32_to_cpup(p++);
/* apparently HPUX can return empty cookies */
if (length == 0)
goto out_hpux;
if (length > NLM_MAXCOOKIELEN)
goto out_size;
p = xdr_inline_decode(xdr, length);
if (unlikely(p == NULL))
goto out_overflow;
cookie->len = length;
memcpy(cookie->data, p, length);
return 0;
out_hpux:
cookie->len = 4;
memset(cookie->data, 0, 4);
return 0;
out_size:
dprintk("NFS: returned cookie was too long: %u\n", length);
return -EIO;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
/*
* netobj fh;
*/
static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh)
{
BUG_ON(fh->size > NFS3_FHSIZE);
encode_netobj(xdr, (u8 *)&fh->data, fh->size);
}
/*
* enum nlm4_stats {
* NLM4_GRANTED = 0,
* NLM4_DENIED = 1,
* NLM4_DENIED_NOLOCKS = 2,
* NLM4_BLOCKED = 3,
* NLM4_DENIED_GRACE_PERIOD = 4,
* NLM4_DEADLCK = 5,
* NLM4_ROFS = 6,
* NLM4_STALE_FH = 7,
* NLM4_FBIG = 8,
* NLM4_FAILED = 9
* };
*
* struct nlm4_stat {
* nlm4_stats stat;
* };
*
* NB: we don't swap bytes for the NLM status values. The upper
* layers deal directly with the status value in network byte
* order.
*/
static void encode_nlm4_stat(struct xdr_stream *xdr,
const __be32 stat)
{
__be32 *p;
BUG_ON(be32_to_cpu(stat) > NLM_FAILED);
p = xdr_reserve_space(xdr, 4);
*p = stat;
}
static int decode_nlm4_stat(struct xdr_stream *xdr, __be32 *stat)
{
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
goto out_overflow;
if (unlikely(*p > nlm4_failed))
goto out_bad_xdr;
*stat = *p;
return 0;
out_bad_xdr:
dprintk("%s: server returned invalid nlm4_stats value: %u\n",
__func__, be32_to_cpup(p));
return -EIO;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
/*
* struct nlm4_holder {
* bool exclusive;
* int32 svid;
* netobj oh;
* uint64 l_offset;
* uint64 l_len;
* };
*/
static void encode_nlm4_holder(struct xdr_stream *xdr,
const struct nlm_res *result)
{
const struct nlm_lock *lock = &result->lock;
u64 l_offset, l_len;
__be32 *p;
encode_bool(xdr, lock->fl.fl_type == F_RDLCK);
encode_int32(xdr, lock->svid);
encode_netobj(xdr, lock->oh.data, lock->oh.len);
p = xdr_reserve_space(xdr, 4 + 4);
nlm4_compute_offsets(lock, &l_offset, &l_len);
p = xdr_encode_hyper(p, l_offset);
xdr_encode_hyper(p, l_len);
}
static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result)
{
struct nlm_lock *lock = &result->lock;
struct file_lock *fl = &lock->fl;
u64 l_offset, l_len;
u32 exclusive;
int error;
__be32 *p;
s32 end;
memset(lock, 0, sizeof(*lock));
locks_init_lock(fl);
p = xdr_inline_decode(xdr, 4 + 4);
if (unlikely(p == NULL))
goto out_overflow;
exclusive = be32_to_cpup(p++);
lock->svid = be32_to_cpup(p);
fl->fl_pid = (pid_t)lock->svid;
error = decode_netobj(xdr, &lock->oh);
if (unlikely(error))
goto out;
p = xdr_inline_decode(xdr, 8 + 8);
if (unlikely(p == NULL))
goto out_overflow;
fl->fl_flags = FL_POSIX;
fl->fl_type = exclusive != 0 ? F_WRLCK : F_RDLCK;
p = xdr_decode_hyper(p, &l_offset);
xdr_decode_hyper(p, &l_len);
end = l_offset + l_len - 1;
fl->fl_start = (loff_t)l_offset;
if (l_len == 0 || end < 0)
fl->fl_end = OFFSET_MAX;
else
fl->fl_end = (loff_t)end;
error = 0;
out:
return error;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
/*
* string caller_name<LM_MAXSTRLEN>;
*/
static void encode_caller_name(struct xdr_stream *xdr, const char *name)
{
/* NB: client-side does not set lock->len */
u32 length = strlen(name);
__be32 *p;
BUG_ON(length > NLM_MAXSTRLEN);
p = xdr_reserve_space(xdr, 4 + length);
xdr_encode_opaque(p, name, length);
}
/*
* struct nlm4_lock {
* string caller_name<LM_MAXSTRLEN>;
* netobj fh;
* netobj oh;
* int32 svid;
* uint64 l_offset;
* uint64 l_len;
* };
*/
static void encode_nlm4_lock(struct xdr_stream *xdr,
const struct nlm_lock *lock)
{
u64 l_offset, l_len;
__be32 *p;
encode_caller_name(xdr, lock->caller);
encode_fh(xdr, &lock->fh);
encode_netobj(xdr, lock->oh.data, lock->oh.len);
p = xdr_reserve_space(xdr, 4 + 8 + 8);
*p++ = cpu_to_be32(lock->svid);
nlm4_compute_offsets(lock, &l_offset, &l_len);
p = xdr_encode_hyper(p, l_offset);
xdr_encode_hyper(p, l_len);
}
/*
* NLMv4 XDR encode functions
*
* NLMv4 argument types are defined in Appendix II of RFC 1813:
* "NFS Version 3 Protocol Specification" and Chapter 10 of X/Open's
* "Protocols for Interworking: XNFS, Version 3W".
*/
/*
* struct nlm4_testargs {
* netobj cookie;
* bool exclusive;
* struct nlm4_lock alock;
* };
*/
static void nlm4_xdr_enc_testargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_args *args)
{
const struct nlm_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
encode_nlm4_lock(xdr, lock);
}
/*
* struct nlm4_lockargs {
* netobj cookie;
* bool block;
* bool exclusive;
* struct nlm4_lock alock;
* bool reclaim;
* int state;
* };
*/
static void nlm4_xdr_enc_lockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_args *args)
{
const struct nlm_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
encode_nlm4_lock(xdr, lock);
encode_bool(xdr, args->reclaim);
encode_int32(xdr, args->state);
}
/*
* struct nlm4_cancargs {
* netobj cookie;
* bool block;
* bool exclusive;
* struct nlm4_lock alock;
* };
*/
static void nlm4_xdr_enc_cancargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_args *args)
{
const struct nlm_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
encode_nlm4_lock(xdr, lock);
}
/*
* struct nlm4_unlockargs {
* netobj cookie;
* struct nlm4_lock alock;
* };
*/
static void nlm4_xdr_enc_unlockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_args *args)
{
const struct nlm_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_nlm4_lock(xdr, lock);
}
/*
* struct nlm4_res {
* netobj cookie;
* nlm4_stat stat;
* };
*/
static void nlm4_xdr_enc_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_res *result)
{
encode_cookie(xdr, &result->cookie);
encode_nlm4_stat(xdr, result->status);
}
/*
* union nlm4_testrply switch (nlm4_stats stat) {
* case NLM4_DENIED:
* struct nlm4_holder holder;
* default:
* void;
* };
*
* struct nlm4_testres {
* netobj cookie;
* nlm4_testrply test_stat;
* };
*/
static void nlm4_xdr_enc_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_res *result)
{
encode_cookie(xdr, &result->cookie);
encode_nlm4_stat(xdr, result->status);
if (result->status == nlm_lck_denied)
encode_nlm4_holder(xdr, result);
}
/*
* NLMv4 XDR decode functions
*
* NLMv4 argument types are defined in Appendix II of RFC 1813:
* "NFS Version 3 Protocol Specification" and Chapter 10 of X/Open's
* "Protocols for Interworking: XNFS, Version 3W".
*/
/*
* union nlm4_testrply switch (nlm4_stats stat) {
* case NLM4_DENIED:
* struct nlm4_holder holder;
* default:
* void;
* };
*
* struct nlm4_testres {
* netobj cookie;
* nlm4_testrply test_stat;
* };
*/
static int decode_nlm4_testrply(struct xdr_stream *xdr,
struct nlm_res *result)
{
int error;
error = decode_nlm4_stat(xdr, &result->status);
if (unlikely(error))
goto out;
if (result->status == nlm_lck_denied)
error = decode_nlm4_holder(xdr, result);
out:
return error;
}
static int nlm4_xdr_dec_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
struct nlm_res *result)
{
int error;
error = decode_cookie(xdr, &result->cookie);
if (unlikely(error))
goto out;
error = decode_nlm4_testrply(xdr, result);
out:
return error;
}
/*
* struct nlm4_res {
* netobj cookie;
* nlm4_stat stat;
* };
*/
static int nlm4_xdr_dec_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
struct nlm_res *result)
{
int error;
error = decode_cookie(xdr, &result->cookie);
if (unlikely(error))
goto out;
error = decode_nlm4_stat(xdr, &result->status);
out:
return error;
}
/*
* For NLM, a void procedure really returns nothing
*/
#define nlm4_xdr_dec_norep NULL
#define PROC(proc, argtype, restype) \
[NLMPROC_##proc] = { \
.p_proc = NLMPROC_##proc, \
.p_encode = (kxdreproc_t)nlm4_xdr_enc_##argtype, \
.p_decode = (kxdrdproc_t)nlm4_xdr_dec_##restype, \
.p_arglen = NLM4_##argtype##_sz, \
.p_replen = NLM4_##restype##_sz, \
.p_statidx = NLMPROC_##proc, \
.p_name = #proc, \
}
static struct rpc_procinfo nlm4_procedures[] = {
PROC(TEST, testargs, testres),
PROC(LOCK, lockargs, res),
PROC(CANCEL, cancargs, res),
PROC(UNLOCK, unlockargs, res),
PROC(GRANTED, testargs, res),
PROC(TEST_MSG, testargs, norep),
PROC(LOCK_MSG, lockargs, norep),
PROC(CANCEL_MSG, cancargs, norep),
PROC(UNLOCK_MSG, unlockargs, norep),
PROC(GRANTED_MSG, testargs, norep),
PROC(TEST_RES, testres, norep),
PROC(LOCK_RES, res, norep),
PROC(CANCEL_RES, res, norep),
PROC(UNLOCK_RES, res, norep),
PROC(GRANTED_RES, res, norep),
};
struct rpc_version nlm_version4 = {
.number = 4,
.nrprocs = ARRAY_SIZE(nlm4_procedures),
.procs = nlm4_procedures,
};
......@@ -79,7 +79,7 @@ EXPORT_SYMBOL_GPL(nlmclnt_init);
*/
void nlmclnt_done(struct nlm_host *host)
{
nlm_release_host(host);
nlmclnt_release_host(host);
lockd_down();
}
EXPORT_SYMBOL_GPL(nlmclnt_done);
......@@ -273,7 +273,7 @@ reclaimer(void *ptr)
spin_unlock(&nlm_blocked_lock);
/* Release host handle after use */
nlm_release_host(host);
nlmclnt_release_host(host);
lockd_down();
return 0;
}
......@@ -58,7 +58,7 @@ static void nlm_put_lockowner(struct nlm_lockowner *lockowner)
return;
list_del(&lockowner->list);
spin_unlock(&lockowner->host->h_lock);
nlm_release_host(lockowner->host);
nlmclnt_release_host(lockowner->host);
kfree(lockowner);
}
......@@ -207,22 +207,22 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
printk("nlm_alloc_call: failed, waiting for memory\n");
schedule_timeout_interruptible(5*HZ);
}
nlm_release_host(host);
nlmclnt_release_host(host);
return NULL;
}
void nlm_release_call(struct nlm_rqst *call)
void nlmclnt_release_call(struct nlm_rqst *call)
{
if (!atomic_dec_and_test(&call->a_count))
return;
nlm_release_host(call->a_host);
nlmclnt_release_host(call->a_host);
nlmclnt_release_lockargs(call);
kfree(call);
}
static void nlmclnt_rpc_release(void *data)
{
nlm_release_call(data);
nlmclnt_release_call(data);
}
static int nlm_wait_on_grace(wait_queue_head_t *queue)
......@@ -436,7 +436,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
status = nlm_stat_to_errno(req->a_res.status);
}
out:
nlm_release_call(req);
nlmclnt_release_call(req);
return status;
}
......@@ -593,7 +593,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
out_unblock:
nlmclnt_finish_block(block);
out:
nlm_release_call(req);
nlmclnt_release_call(req);
return status;
out_unlock:
/* Fatal error: ensure that we remove the lock altogether */
......@@ -694,7 +694,7 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
/* What to do now? I'm out of my depth... */
status = -ENOLCK;
out:
nlm_release_call(req);
nlmclnt_release_call(req);
return status;
}
......@@ -755,7 +755,7 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl
NLMPROC_CANCEL, &nlmclnt_cancel_ops);
if (status == 0 && req->a_res.status == nlm_lck_denied)
status = -ENOLCK;
nlm_release_call(req);
nlmclnt_release_call(req);
return status;
}
......
/*
* linux/fs/lockd/clntxdr.c
*
* XDR functions to encode/decode NLM version 3 RPC arguments and results.
* NLM version 3 is backwards compatible with NLM versions 1 and 2.
*
* NLM client-side only.
*
* Copyright (C) 2010, Oracle. All rights reserved.
*/
#include <linux/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/stats.h>
#include <linux/lockd/lockd.h>
#define NLMDBG_FACILITY NLMDBG_XDR
#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
#endif
/*
* Declare the space requirements for NLM arguments and replies as
* number of 32bit-words
*/
#define NLM_cookie_sz (1+(NLM_MAXCOOKIELEN>>2))
#define NLM_caller_sz (1+(NLMCLNT_OHSIZE>>2))
#define NLM_owner_sz (1+(NLMCLNT_OHSIZE>>2))
#define NLM_fhandle_sz (1+(NFS2_FHSIZE>>2))
#define NLM_lock_sz (3+NLM_caller_sz+NLM_owner_sz+NLM_fhandle_sz)
#define NLM_holder_sz (4+NLM_owner_sz)
#define NLM_testargs_sz (NLM_cookie_sz+1+NLM_lock_sz)
#define NLM_lockargs_sz (NLM_cookie_sz+4+NLM_lock_sz)
#define NLM_cancargs_sz (NLM_cookie_sz+2+NLM_lock_sz)
#define NLM_unlockargs_sz (NLM_cookie_sz+NLM_lock_sz)
#define NLM_testres_sz (NLM_cookie_sz+1+NLM_holder_sz)
#define NLM_res_sz (NLM_cookie_sz+1)
#define NLM_norep_sz (0)
static s32 loff_t_to_s32(loff_t offset)
{
s32 res;
if (offset >= NLM_OFFSET_MAX)
res = NLM_OFFSET_MAX;
else if (offset <= -NLM_OFFSET_MAX)
res = -NLM_OFFSET_MAX;
else
res = offset;
return res;
}
static void nlm_compute_offsets(const struct nlm_lock *lock,
u32 *l_offset, u32 *l_len)
{
const struct file_lock *fl = &lock->fl;
BUG_ON(fl->fl_start > NLM_OFFSET_MAX);
BUG_ON(fl->fl_end > NLM_OFFSET_MAX &&
fl->fl_end != OFFSET_MAX);
*l_offset = loff_t_to_s32(fl->fl_start);
if (fl->fl_end == OFFSET_MAX)
*l_len = 0;
else
*l_len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
}
/*
* Handle decode buffer overflows out-of-line.
*/
static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
{
dprintk("lockd: %s prematurely hit the end of our receive buffer. "
"Remaining buffer length is %tu words.\n",
func, xdr->end - xdr->p);
}
/*
* Encode/decode NLMv3 basic data types
*
* Basic NLMv3 data types are not defined in an IETF standards
* document. X/Open has a description of these data types that
* is useful. See Chapter 10 of "Protocols for Interworking:
* XNFS, Version 3W".
*
* Not all basic data types have their own encoding and decoding
* functions. For run-time efficiency, some data types are encoded
* or decoded inline.
*/
static void encode_bool(struct xdr_stream *xdr, const int value)
{
__be32 *p;
p = xdr_reserve_space(xdr, 4);
*p = value ? xdr_one : xdr_zero;
}
static void encode_int32(struct xdr_stream *xdr, const s32 value)
{
__be32 *p;
p = xdr_reserve_space(xdr, 4);
*p = cpu_to_be32(value);
}
/*
* typedef opaque netobj<MAXNETOBJ_SZ>
*/
static void encode_netobj(struct xdr_stream *xdr,
const u8 *data, const unsigned int length)
{
__be32 *p;
BUG_ON(length > XDR_MAX_NETOBJ);
p = xdr_reserve_space(xdr, 4 + length);
xdr_encode_opaque(p, data, length);
}
static int decode_netobj(struct xdr_stream *xdr,
struct xdr_netobj *obj)
{
u32 length;
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
goto out_overflow;
length = be32_to_cpup(p++);
if (unlikely(length > XDR_MAX_NETOBJ))
goto out_size;
obj->len = length;
obj->data = (u8 *)p;
return 0;
out_size:
dprintk("NFS: returned netobj was too long: %u\n", length);
return -EIO;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
/*
* netobj cookie;
*/
static void encode_cookie(struct xdr_stream *xdr,
const struct nlm_cookie *cookie)
{
BUG_ON(cookie->len > NLM_MAXCOOKIELEN);
encode_netobj(xdr, (u8 *)&cookie->data, cookie->len);
}
static int decode_cookie(struct xdr_stream *xdr,
struct nlm_cookie *cookie)
{
u32 length;
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
goto out_overflow;
length = be32_to_cpup(p++);
/* apparently HPUX can return empty cookies */
if (length == 0)
goto out_hpux;
if (length > NLM_MAXCOOKIELEN)
goto out_size;
p = xdr_inline_decode(xdr, length);
if (unlikely(p == NULL))
goto out_overflow;
cookie->len = length;
memcpy(cookie->data, p, length);
return 0;
out_hpux:
cookie->len = 4;
memset(cookie->data, 0, 4);
return 0;
out_size:
dprintk("NFS: returned cookie was too long: %u\n", length);
return -EIO;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
/*
* netobj fh;
*/
static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh)
{
BUG_ON(fh->size != NFS2_FHSIZE);
encode_netobj(xdr, (u8 *)&fh->data, NFS2_FHSIZE);
}
/*
* enum nlm_stats {
* LCK_GRANTED = 0,
* LCK_DENIED = 1,
* LCK_DENIED_NOLOCKS = 2,
* LCK_BLOCKED = 3,
* LCK_DENIED_GRACE_PERIOD = 4
* };
*
*
* struct nlm_stat {
* nlm_stats stat;
* };
*
* NB: we don't swap bytes for the NLM status values. The upper
* layers deal directly with the status value in network byte
* order.
*/
static void encode_nlm_stat(struct xdr_stream *xdr,
const __be32 stat)
{
__be32 *p;
BUG_ON(be32_to_cpu(stat) > NLM_LCK_DENIED_GRACE_PERIOD);
p = xdr_reserve_space(xdr, 4);
*p = stat;
}
static int decode_nlm_stat(struct xdr_stream *xdr,
__be32 *stat)
{
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
goto out_overflow;
if (unlikely(*p > nlm_lck_denied_grace_period))
goto out_enum;
*stat = *p;
return 0;
out_enum:
dprintk("%s: server returned invalid nlm_stats value: %u\n",
__func__, be32_to_cpup(p));
return -EIO;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
/*
* struct nlm_holder {
* bool exclusive;
* int uppid;
* netobj oh;
* unsigned l_offset;
* unsigned l_len;
* };
*/
static void encode_nlm_holder(struct xdr_stream *xdr,
const struct nlm_res *result)
{
const struct nlm_lock *lock = &result->lock;
u32 l_offset, l_len;
__be32 *p;
encode_bool(xdr, lock->fl.fl_type == F_RDLCK);
encode_int32(xdr, lock->svid);
encode_netobj(xdr, lock->oh.data, lock->oh.len);
p = xdr_reserve_space(xdr, 4 + 4);
nlm_compute_offsets(lock, &l_offset, &l_len);
*p++ = cpu_to_be32(l_offset);
*p = cpu_to_be32(l_len);
}
static int decode_nlm_holder(struct xdr_stream *xdr, struct nlm_res *result)
{
struct nlm_lock *lock = &result->lock;
struct file_lock *fl = &lock->fl;
u32 exclusive, l_offset, l_len;
int error;
__be32 *p;
s32 end;
memset(lock, 0, sizeof(*lock));
locks_init_lock(fl);
p = xdr_inline_decode(xdr, 4 + 4);
if (unlikely(p == NULL))
goto out_overflow;
exclusive = be32_to_cpup(p++);
lock->svid = be32_to_cpup(p);
fl->fl_pid = (pid_t)lock->svid;
error = decode_netobj(xdr, &lock->oh);
if (unlikely(error))
goto out;
p = xdr_inline_decode(xdr, 4 + 4);
if (unlikely(p == NULL))
goto out_overflow;
fl->fl_flags = FL_POSIX;
fl->fl_type = exclusive != 0 ? F_WRLCK : F_RDLCK;
l_offset = be32_to_cpup(p++);
l_len = be32_to_cpup(p);
end = l_offset + l_len - 1;
fl->fl_start = (loff_t)l_offset;
if (l_len == 0 || end < 0)
fl->fl_end = OFFSET_MAX;
else
fl->fl_end = (loff_t)end;
error = 0;
out:
return error;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
}
/*
* string caller_name<LM_MAXSTRLEN>;
*/
static void encode_caller_name(struct xdr_stream *xdr, const char *name)
{
/* NB: client-side does not set lock->len */
u32 length = strlen(name);
__be32 *p;
BUG_ON(length > NLM_MAXSTRLEN);
p = xdr_reserve_space(xdr, 4 + length);
xdr_encode_opaque(p, name, length);
}
/*
* struct nlm_lock {
* string caller_name<LM_MAXSTRLEN>;
* netobj fh;
* netobj oh;
* int uppid;
* unsigned l_offset;
* unsigned l_len;
* };
*/
static void encode_nlm_lock(struct xdr_stream *xdr,
const struct nlm_lock *lock)
{
u32 l_offset, l_len;
__be32 *p;
encode_caller_name(xdr, lock->caller);
encode_fh(xdr, &lock->fh);
encode_netobj(xdr, lock->oh.data, lock->oh.len);
p = xdr_reserve_space(xdr, 4 + 4 + 4);
*p++ = cpu_to_be32(lock->svid);
nlm_compute_offsets(lock, &l_offset, &l_len);
*p++ = cpu_to_be32(l_offset);
*p = cpu_to_be32(l_len);
}
/*
* NLMv3 XDR encode functions
*
* NLMv3 argument types are defined in Chapter 10 of The Open Group's
* "Protocols for Interworking: XNFS, Version 3W".
*/
/*
* struct nlm_testargs {
* netobj cookie;
* bool exclusive;
* struct nlm_lock alock;
* };
*/
static void nlm_xdr_enc_testargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_args *args)
{
const struct nlm_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
encode_nlm_lock(xdr, lock);
}
/*
* struct nlm_lockargs {
* netobj cookie;
* bool block;
* bool exclusive;
* struct nlm_lock alock;
* bool reclaim;
* int state;
* };
*/
static void nlm_xdr_enc_lockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_args *args)
{
const struct nlm_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
encode_nlm_lock(xdr, lock);
encode_bool(xdr, args->reclaim);
encode_int32(xdr, args->state);
}
/*
* struct nlm_cancargs {
* netobj cookie;
* bool block;
* bool exclusive;
* struct nlm_lock alock;
* };
*/
static void nlm_xdr_enc_cancargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_args *args)
{
const struct nlm_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
encode_nlm_lock(xdr, lock);
}
/*
* struct nlm_unlockargs {
* netobj cookie;
* struct nlm_lock alock;
* };
*/
static void nlm_xdr_enc_unlockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_args *args)
{
const struct nlm_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_nlm_lock(xdr, lock);
}
/*
* struct nlm_res {
* netobj cookie;
* nlm_stat stat;
* };
*/
static void nlm_xdr_enc_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_res *result)
{
encode_cookie(xdr, &result->cookie);
encode_nlm_stat(xdr, result->status);
}
/*
* union nlm_testrply switch (nlm_stats stat) {
* case LCK_DENIED:
* struct nlm_holder holder;
* default:
* void;
* };
*
* struct nlm_testres {
* netobj cookie;
* nlm_testrply test_stat;
* };
*/
static void encode_nlm_testrply(struct xdr_stream *xdr,
const struct nlm_res *result)
{
if (result->status == nlm_lck_denied)
encode_nlm_holder(xdr, result);
}
static void nlm_xdr_enc_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
const struct nlm_res *result)
{
encode_cookie(xdr, &result->cookie);
encode_nlm_stat(xdr, result->status);
encode_nlm_testrply(xdr, result);
}
/*
* NLMv3 XDR decode functions
*
* NLMv3 result types are defined in Chapter 10 of The Open Group's
* "Protocols for Interworking: XNFS, Version 3W".
*/
/*
* union nlm_testrply switch (nlm_stats stat) {
* case LCK_DENIED:
* struct nlm_holder holder;
* default:
* void;
* };
*
* struct nlm_testres {
* netobj cookie;
* nlm_testrply test_stat;
* };
*/
static int decode_nlm_testrply(struct xdr_stream *xdr,
struct nlm_res *result)
{
int error;
error = decode_nlm_stat(xdr, &result->status);
if (unlikely(error))
goto out;
if (result->status == nlm_lck_denied)
error = decode_nlm_holder(xdr, result);
out:
return error;
}
static int nlm_xdr_dec_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
struct nlm_res *result)
{
int error;
error = decode_cookie(xdr, &result->cookie);
if (unlikely(error))
goto out;
error = decode_nlm_testrply(xdr, result);
out:
return error;
}
/*
* struct nlm_res {
* netobj cookie;
* nlm_stat stat;
* };
*/
static int nlm_xdr_dec_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
struct nlm_res *result)
{
int error;
error = decode_cookie(xdr, &result->cookie);
if (unlikely(error))
goto out;
error = decode_nlm_stat(xdr, &result->status);
out:
return error;
}
/*
* For NLM, a void procedure really returns nothing
*/
#define nlm_xdr_dec_norep NULL
#define PROC(proc, argtype, restype) \
[NLMPROC_##proc] = { \
.p_proc = NLMPROC_##proc, \
.p_encode = (kxdreproc_t)nlm_xdr_enc_##argtype, \
.p_decode = (kxdrdproc_t)nlm_xdr_dec_##restype, \
.p_arglen = NLM_##argtype##_sz, \
.p_replen = NLM_##restype##_sz, \
.p_statidx = NLMPROC_##proc, \
.p_name = #proc, \
}
static struct rpc_procinfo nlm_procedures[] = {
PROC(TEST, testargs, testres),
PROC(LOCK, lockargs, res),
PROC(CANCEL, cancargs, res),
PROC(UNLOCK, unlockargs, res),
PROC(GRANTED, testargs, res),
PROC(TEST_MSG, testargs, norep),
PROC(LOCK_MSG, lockargs, norep),
PROC(CANCEL_MSG, cancargs, norep),
PROC(UNLOCK_MSG, unlockargs, norep),
PROC(GRANTED_MSG, testargs, norep),
PROC(TEST_RES, testres, norep),
PROC(LOCK_RES, res, norep),
PROC(CANCEL_RES, res, norep),
PROC(UNLOCK_RES, res, norep),
PROC(GRANTED_RES, res, norep),
};
static struct rpc_version nlm_version1 = {
.number = 1,
.nrprocs = ARRAY_SIZE(nlm_procedures),
.procs = nlm_procedures,
};
static struct rpc_version nlm_version3 = {
.number = 3,
.nrprocs = ARRAY_SIZE(nlm_procedures),
.procs = nlm_procedures,
};
static struct rpc_version *nlm_versions[] = {
[1] = &nlm_version1,
[3] = &nlm_version3,
#ifdef CONFIG_LOCKD_V4
[4] = &nlm_version4,
#endif
};
static struct rpc_stat nlm_rpc_stats;
struct rpc_program nlm_program = {
.name = "lockd",
.number = NLM_PROGRAM,
.nrvers = ARRAY_SIZE(nlm_versions),
.version = nlm_versions,
.stats = &nlm_rpc_stats,
};
......@@ -25,9 +25,22 @@
#define NLM_HOST_EXPIRE (300 * HZ)
#define NLM_HOST_COLLECT (120 * HZ)
static struct hlist_head nlm_hosts[NLM_HOST_NRHASH];
static struct hlist_head nlm_server_hosts[NLM_HOST_NRHASH];
static struct hlist_head nlm_client_hosts[NLM_HOST_NRHASH];
#define for_each_host(host, pos, chain, table) \
for ((chain) = (table); \
(chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
hlist_for_each_entry((host), (pos), (chain), h_hash)
#define for_each_host_safe(host, pos, next, chain, table) \
for ((chain) = (table); \
(chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
hlist_for_each_entry_safe((host), (pos), (next), \
(chain), h_hash)
static unsigned long next_gc;
static int nrhosts;
static unsigned long nrhosts;
static DEFINE_MUTEX(nlm_host_mutex);
static void nlm_gc_hosts(void);
......@@ -40,8 +53,6 @@ struct nlm_lookup_host_info {
const u32 version; /* NLM version to search for */
const char *hostname; /* remote's hostname */
const size_t hostname_len; /* it's length */
const struct sockaddr *src_sap; /* our address (optional) */
const size_t src_len; /* it's length */
const int noresvport; /* use non-priv port */
};
......@@ -88,127 +99,83 @@ static unsigned int nlm_hash_address(const struct sockaddr *sap)
}
/*
* Common host lookup routine for server & client
* Allocate and initialize an nlm_host. Common to both client and server.
*/
static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
struct nsm_handle *nsm)
{
struct hlist_head *chain;
struct hlist_node *pos;
struct nlm_host *host;
struct nsm_handle *nsm = NULL;
mutex_lock(&nlm_host_mutex);
struct nlm_host *host = NULL;
unsigned long now = jiffies;
if (time_after_eq(jiffies, next_gc))
nlm_gc_hosts();
/* We may keep several nlm_host objects for a peer, because each
* nlm_host is identified by
* (address, protocol, version, server/client)
* We could probably simplify this a little by putting all those
* different NLM rpc_clients into one single nlm_host object.
* This would allow us to have one nlm_host per address.
*/
chain = &nlm_hosts[nlm_hash_address(ni->sap)];
hlist_for_each_entry(host, pos, chain, h_hash) {
if (!rpc_cmp_addr(nlm_addr(host), ni->sap))
continue;
/* See if we have an NSM handle for this client */
if (!nsm)
nsm = host->h_nsmhandle;
if (host->h_proto != ni->protocol)
continue;
if (host->h_version != ni->version)
continue;
if (host->h_server != ni->server)
continue;
if (ni->server && ni->src_len != 0 &&
!rpc_cmp_addr(nlm_srcaddr(host), ni->src_sap))
continue;
/* Move to head of hash chain. */
hlist_del(&host->h_hash);
hlist_add_head(&host->h_hash, chain);
nlm_get_host(host);
dprintk("lockd: nlm_lookup_host found host %s (%s)\n",
host->h_name, host->h_addrbuf);
goto out;
}
/*
* The host wasn't in our hash table. If we don't
* have an NSM handle for it yet, create one.
*/
if (nsm)
if (nsm != NULL)
atomic_inc(&nsm->sm_count);
else {
host = NULL;
nsm = nsm_get_handle(ni->sap, ni->salen,
ni->hostname, ni->hostname_len);
if (!nsm) {
dprintk("lockd: nlm_lookup_host failed; "
"no nsm handle\n");
if (unlikely(nsm == NULL)) {
dprintk("lockd: %s failed; no nsm handle\n",
__func__);
goto out;
}
}
host = kzalloc(sizeof(*host), GFP_KERNEL);
if (!host) {
host = kmalloc(sizeof(*host), GFP_KERNEL);
if (unlikely(host == NULL)) {
dprintk("lockd: %s failed; no memory\n", __func__);
nsm_release(nsm);
dprintk("lockd: nlm_lookup_host failed; no memory\n");
goto out;
}
host->h_name = nsm->sm_name;
host->h_addrbuf = nsm->sm_addrbuf;
memcpy(nlm_addr(host), ni->sap, ni->salen);
host->h_addrlen = ni->salen;
host->h_addrlen = ni->salen;
rpc_set_port(nlm_addr(host), 0);
memcpy(nlm_srcaddr(host), ni->src_sap, ni->src_len);
host->h_srcaddrlen = ni->src_len;
host->h_srcaddrlen = 0;
host->h_rpcclnt = NULL;
host->h_name = nsm->sm_name;
host->h_version = ni->version;
host->h_proto = ni->protocol;
host->h_rpcclnt = NULL;
mutex_init(&host->h_mutex);
host->h_nextrebind = jiffies + NLM_HOST_REBIND;
host->h_expires = jiffies + NLM_HOST_EXPIRE;
atomic_set(&host->h_count, 1);
host->h_reclaiming = 0;
host->h_server = ni->server;
host->h_noresvport = ni->noresvport;
host->h_inuse = 0;
init_waitqueue_head(&host->h_gracewait);
init_rwsem(&host->h_rwsem);
host->h_state = 0; /* pseudo NSM state */
host->h_nsmstate = 0; /* real NSM state */
host->h_nsmhandle = nsm;
host->h_server = ni->server;
host->h_noresvport = ni->noresvport;
hlist_add_head(&host->h_hash, chain);
host->h_state = 0;
host->h_nsmstate = 0;
host->h_pidcount = 0;
atomic_set(&host->h_count, 1);
mutex_init(&host->h_mutex);
host->h_nextrebind = now + NLM_HOST_REBIND;
host->h_expires = now + NLM_HOST_EXPIRE;
INIT_LIST_HEAD(&host->h_lockowners);
spin_lock_init(&host->h_lock);
INIT_LIST_HEAD(&host->h_granted);
INIT_LIST_HEAD(&host->h_reclaim);
nrhosts++;
dprintk("lockd: nlm_lookup_host created host %s\n",
host->h_name);
host->h_nsmhandle = nsm;
host->h_addrbuf = nsm->sm_addrbuf;
out:
mutex_unlock(&nlm_host_mutex);
return host;
}
/*
* Destroy a host
* Destroy an nlm_host and free associated resources
*
* Caller must hold nlm_host_mutex.
*/
static void
nlm_destroy_host(struct nlm_host *host)
static void nlm_destroy_host_locked(struct nlm_host *host)
{
struct rpc_clnt *clnt;
dprintk("lockd: destroy host %s\n", host->h_name);
BUG_ON(!list_empty(&host->h_lockowners));
BUG_ON(atomic_read(&host->h_count));
hlist_del_init(&host->h_hash);
nsm_unmonitor(host);
nsm_release(host->h_nsmhandle);
......@@ -216,6 +183,8 @@ nlm_destroy_host(struct nlm_host *host)
if (clnt != NULL)
rpc_shutdown_client(clnt);
kfree(host);
nrhosts--;
}
/**
......@@ -249,12 +218,76 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
.hostname_len = strlen(hostname),
.noresvport = noresvport,
};
struct hlist_head *chain;
struct hlist_node *pos;
struct nlm_host *host;
struct nsm_handle *nsm = NULL;
dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
(hostname ? hostname : "<none>"), version,
(protocol == IPPROTO_UDP ? "udp" : "tcp"));
return nlm_lookup_host(&ni);
mutex_lock(&nlm_host_mutex);
chain = &nlm_client_hosts[nlm_hash_address(sap)];
hlist_for_each_entry(host, pos, chain, h_hash) {
if (!rpc_cmp_addr(nlm_addr(host), sap))
continue;
/* Same address. Share an NSM handle if we already have one */
if (nsm == NULL)
nsm = host->h_nsmhandle;
if (host->h_proto != protocol)
continue;
if (host->h_version != version)
continue;
nlm_get_host(host);
dprintk("lockd: %s found host %s (%s)\n", __func__,
host->h_name, host->h_addrbuf);
goto out;
}
host = nlm_alloc_host(&ni, nsm);
if (unlikely(host == NULL))
goto out;
hlist_add_head(&host->h_hash, chain);
nrhosts++;
dprintk("lockd: %s created host %s (%s)\n", __func__,
host->h_name, host->h_addrbuf);
out:
mutex_unlock(&nlm_host_mutex);
return host;
}
/**
* nlmclnt_release_host - release client nlm_host
* @host: nlm_host to release
*
*/
void nlmclnt_release_host(struct nlm_host *host)
{
if (host == NULL)
return;
dprintk("lockd: release client host %s\n", host->h_name);
BUG_ON(atomic_read(&host->h_count) < 0);
BUG_ON(host->h_server);
if (atomic_dec_and_test(&host->h_count)) {
BUG_ON(!list_empty(&host->h_lockowners));
BUG_ON(!list_empty(&host->h_granted));
BUG_ON(!list_empty(&host->h_reclaim));
mutex_lock(&nlm_host_mutex);
nlm_destroy_host_locked(host);
mutex_unlock(&nlm_host_mutex);
}
}
/**
......@@ -279,12 +312,18 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
const char *hostname,
const size_t hostname_len)
{
struct hlist_head *chain;
struct hlist_node *pos;
struct nlm_host *host = NULL;
struct nsm_handle *nsm = NULL;
struct sockaddr_in sin = {
.sin_family = AF_INET,
};
struct sockaddr_in6 sin6 = {
.sin6_family = AF_INET6,
};
struct sockaddr *src_sap;
size_t src_len = rqstp->rq_addrlen;
struct nlm_lookup_host_info ni = {
.server = 1,
.sap = svc_addr(rqstp),
......@@ -293,27 +332,91 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
.version = rqstp->rq_vers,
.hostname = hostname,
.hostname_len = hostname_len,
.src_len = rqstp->rq_addrlen,
};
dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__,
(int)hostname_len, hostname, rqstp->rq_vers,
(rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
mutex_lock(&nlm_host_mutex);
switch (ni.sap->sa_family) {
case AF_INET:
sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr;
ni.src_sap = (struct sockaddr *)&sin;
src_sap = (struct sockaddr *)&sin;
break;
case AF_INET6:
ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6);
ni.src_sap = (struct sockaddr *)&sin6;
src_sap = (struct sockaddr *)&sin6;
break;
default:
return NULL;
dprintk("lockd: %s failed; unrecognized address family\n",
__func__);
goto out;
}
if (time_after_eq(jiffies, next_gc))
nlm_gc_hosts();
chain = &nlm_server_hosts[nlm_hash_address(ni.sap)];
hlist_for_each_entry(host, pos, chain, h_hash) {
if (!rpc_cmp_addr(nlm_addr(host), ni.sap))
continue;
/* Same address. Share an NSM handle if we already have one */
if (nsm == NULL)
nsm = host->h_nsmhandle;
if (host->h_proto != ni.protocol)
continue;
if (host->h_version != ni.version)
continue;
if (!rpc_cmp_addr(nlm_srcaddr(host), src_sap))
continue;
/* Move to head of hash chain. */
hlist_del(&host->h_hash);
hlist_add_head(&host->h_hash, chain);
nlm_get_host(host);
dprintk("lockd: %s found host %s (%s)\n",
__func__, host->h_name, host->h_addrbuf);
goto out;
}
return nlm_lookup_host(&ni);
host = nlm_alloc_host(&ni, nsm);
if (unlikely(host == NULL))
goto out;
memcpy(nlm_srcaddr(host), src_sap, src_len);
host->h_srcaddrlen = src_len;
hlist_add_head(&host->h_hash, chain);
nrhosts++;
dprintk("lockd: %s created host %s (%s)\n",
__func__, host->h_name, host->h_addrbuf);
out:
mutex_unlock(&nlm_host_mutex);
return host;
}
/**
* nlmsvc_release_host - release server nlm_host
* @host: nlm_host to release
*
* Host is destroyed later in nlm_gc_host().
*/
void nlmsvc_release_host(struct nlm_host *host)
{
if (host == NULL)
return;
dprintk("lockd: release server host %s\n", host->h_name);
BUG_ON(atomic_read(&host->h_count) < 0);
BUG_ON(!host->h_server);
atomic_dec(&host->h_count);
}
/*
......@@ -413,20 +516,28 @@ struct nlm_host * nlm_get_host(struct nlm_host *host)
return host;
}
/*
* Release NLM host after use
*/
void nlm_release_host(struct nlm_host *host)
static struct nlm_host *next_host_state(struct hlist_head *cache,
struct nsm_handle *nsm,
const struct nlm_reboot *info)
{
if (host != NULL) {
dprintk("lockd: release host %s\n", host->h_name);
BUG_ON(atomic_read(&host->h_count) < 0);
if (atomic_dec_and_test(&host->h_count)) {
BUG_ON(!list_empty(&host->h_lockowners));
BUG_ON(!list_empty(&host->h_granted));
BUG_ON(!list_empty(&host->h_reclaim));
struct nlm_host *host = NULL;
struct hlist_head *chain;
struct hlist_node *pos;
mutex_lock(&nlm_host_mutex);
for_each_host(host, pos, chain, cache) {
if (host->h_nsmhandle == nsm
&& host->h_nsmstate != info->state) {
host->h_nsmstate = info->state;
host->h_state++;
nlm_get_host(host);
goto out;
}
}
out:
mutex_unlock(&nlm_host_mutex);
return host;
}
/**
......@@ -438,8 +549,6 @@ void nlm_release_host(struct nlm_host *host)
*/
void nlm_host_rebooted(const struct nlm_reboot *info)
{
struct hlist_head *chain;
struct hlist_node *pos;
struct nsm_handle *nsm;
struct nlm_host *host;
......@@ -452,32 +561,15 @@ void nlm_host_rebooted(const struct nlm_reboot *info)
* lock for this.
* To avoid processing a host several times, we match the nsmstate.
*/
again: mutex_lock(&nlm_host_mutex);
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
hlist_for_each_entry(host, pos, chain, h_hash) {
if (host->h_nsmhandle == nsm
&& host->h_nsmstate != info->state) {
host->h_nsmstate = info->state;
host->h_state++;
nlm_get_host(host);
mutex_unlock(&nlm_host_mutex);
if (host->h_server) {
/* We're server for this guy, just ditch
* all the locks he held. */
nlmsvc_free_host_resources(host);
} else {
/* He's the server, initiate lock recovery. */
nlmclnt_recovery(host);
}
nlm_release_host(host);
goto again;
}
}
while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) {
nlmsvc_free_host_resources(host);
nlmsvc_release_host(host);
}
mutex_unlock(&nlm_host_mutex);
while ((host = next_host_state(nlm_client_hosts, nsm, info)) != NULL) {
nlmclnt_recovery(host);
nlmclnt_release_host(host);
}
nsm_release(nsm);
}
......@@ -497,13 +589,11 @@ nlm_shutdown_hosts(void)
/* First, make all hosts eligible for gc */
dprintk("lockd: nuking all hosts...\n");
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
hlist_for_each_entry(host, pos, chain, h_hash) {
host->h_expires = jiffies - 1;
if (host->h_rpcclnt) {
rpc_shutdown_client(host->h_rpcclnt);
host->h_rpcclnt = NULL;
}
for_each_host(host, pos, chain, nlm_server_hosts) {
host->h_expires = jiffies - 1;
if (host->h_rpcclnt) {
rpc_shutdown_client(host->h_rpcclnt);
host->h_rpcclnt = NULL;
}
}
......@@ -512,15 +602,13 @@ nlm_shutdown_hosts(void)
mutex_unlock(&nlm_host_mutex);
/* complain if any hosts are left */
if (nrhosts) {
if (nrhosts != 0) {
printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
dprintk("lockd: %d hosts left:\n", nrhosts);
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
hlist_for_each_entry(host, pos, chain, h_hash) {
dprintk(" %s (cnt %d use %d exp %ld)\n",
host->h_name, atomic_read(&host->h_count),
host->h_inuse, host->h_expires);
}
dprintk("lockd: %lu hosts left:\n", nrhosts);
for_each_host(host, pos, chain, nlm_server_hosts) {
dprintk(" %s (cnt %d use %d exp %ld)\n",
host->h_name, atomic_read(&host->h_count),
host->h_inuse, host->h_expires);
}
}
}
......@@ -538,29 +626,22 @@ nlm_gc_hosts(void)
struct nlm_host *host;
dprintk("lockd: host garbage collection\n");
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
hlist_for_each_entry(host, pos, chain, h_hash)
host->h_inuse = 0;
}
for_each_host(host, pos, chain, nlm_server_hosts)
host->h_inuse = 0;
/* Mark all hosts that hold locks, blocks or shares */
nlmsvc_mark_resources();
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
hlist_for_each_entry_safe(host, pos, next, chain, h_hash) {
if (atomic_read(&host->h_count) || host->h_inuse
|| time_before(jiffies, host->h_expires)) {
dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n",
host->h_name, atomic_read(&host->h_count),
host->h_inuse, host->h_expires);
continue;
}
dprintk("lockd: delete host %s\n", host->h_name);
hlist_del_init(&host->h_hash);
nlm_destroy_host(host);
nrhosts--;
for_each_host_safe(host, pos, next, chain, nlm_server_hosts) {
if (atomic_read(&host->h_count) || host->h_inuse
|| time_before(jiffies, host->h_expires)) {
dprintk("nlm_gc_hosts skipping %s "
"(cnt %d use %d exp %ld)\n",
host->h_name, atomic_read(&host->h_count),
host->h_inuse, host->h_expires);
continue;
}
nlm_destroy_host_locked(host);
}
next_gc = jiffies + NLM_HOST_COLLECT;
......
......@@ -401,26 +401,22 @@ void nsm_release(struct nsm_handle *nsm)
* Status Monitor wire protocol.
*/
static int encode_nsm_string(struct xdr_stream *xdr, const char *string)
static void encode_nsm_string(struct xdr_stream *xdr, const char *string)
{
const u32 len = strlen(string);
__be32 *p;
if (unlikely(len > SM_MAXSTRLEN))
return -EIO;
p = xdr_reserve_space(xdr, sizeof(u32) + len);
if (unlikely(p == NULL))
return -EIO;
BUG_ON(len > SM_MAXSTRLEN);
p = xdr_reserve_space(xdr, 4 + len);
xdr_encode_opaque(p, string, len);
return 0;
}
/*
* "mon_name" specifies the host to be monitored.
*/
static int encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp)
static void encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp)
{
return encode_nsm_string(xdr, argp->mon_name);
encode_nsm_string(xdr, argp->mon_name);
}
/*
......@@ -429,35 +425,25 @@ static int encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp)
* (via the NLMPROC_SM_NOTIFY call) that the state of host "mon_name"
* has changed.
*/
static int encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp)
static void encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp)
{
int status;
__be32 *p;
status = encode_nsm_string(xdr, utsname()->nodename);
if (unlikely(status != 0))
return status;
p = xdr_reserve_space(xdr, 3 * sizeof(u32));
if (unlikely(p == NULL))
return -EIO;
*p++ = htonl(argp->prog);
*p++ = htonl(argp->vers);
*p++ = htonl(argp->proc);
return 0;
encode_nsm_string(xdr, utsname()->nodename);
p = xdr_reserve_space(xdr, 4 + 4 + 4);
*p++ = cpu_to_be32(argp->prog);
*p++ = cpu_to_be32(argp->vers);
*p = cpu_to_be32(argp->proc);
}
/*
* The "mon_id" argument specifies the non-private arguments
* of an NSMPROC_MON or NSMPROC_UNMON call.
*/
static int encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp)
static void encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp)
{
int status;
status = encode_mon_name(xdr, argp);
if (unlikely(status != 0))
return status;
return encode_my_id(xdr, argp);
encode_mon_name(xdr, argp);
encode_my_id(xdr, argp);
}
/*
......@@ -465,68 +451,56 @@ static int encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp)
* by the NSMPROC_MON call. This information will be supplied in the
* NLMPROC_SM_NOTIFY call.
*/
static int encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp)
static void encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp)
{
__be32 *p;
p = xdr_reserve_space(xdr, SM_PRIV_SIZE);
if (unlikely(p == NULL))
return -EIO;
xdr_encode_opaque_fixed(p, argp->priv->data, SM_PRIV_SIZE);
return 0;
}
static int xdr_enc_mon(struct rpc_rqst *req, __be32 *p,
const struct nsm_args *argp)
static void nsm_xdr_enc_mon(struct rpc_rqst *req, struct xdr_stream *xdr,
const struct nsm_args *argp)
{
struct xdr_stream xdr;
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
status = encode_mon_id(&xdr, argp);
if (unlikely(status))
return status;
return encode_priv(&xdr, argp);
encode_mon_id(xdr, argp);
encode_priv(xdr, argp);
}
static int xdr_enc_unmon(struct rpc_rqst *req, __be32 *p,
const struct nsm_args *argp)
static void nsm_xdr_enc_unmon(struct rpc_rqst *req, struct xdr_stream *xdr,
const struct nsm_args *argp)
{
struct xdr_stream xdr;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
return encode_mon_id(&xdr, argp);
encode_mon_id(xdr, argp);
}
static int xdr_dec_stat_res(struct rpc_rqst *rqstp, __be32 *p,
struct nsm_res *resp)
static int nsm_xdr_dec_stat_res(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
struct nsm_res *resp)
{
struct xdr_stream xdr;
__be32 *p;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
p = xdr_inline_decode(&xdr, 2 * sizeof(u32));
p = xdr_inline_decode(xdr, 4 + 4);
if (unlikely(p == NULL))
return -EIO;
resp->status = ntohl(*p++);
resp->state = ntohl(*p);
resp->status = be32_to_cpup(p++);
resp->state = be32_to_cpup(p);
dprintk("lockd: xdr_dec_stat_res status %d state %d\n",
resp->status, resp->state);
dprintk("lockd: %s status %d state %d\n",
__func__, resp->status, resp->state);
return 0;
}
static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p,
struct nsm_res *resp)
static int nsm_xdr_dec_stat(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
struct nsm_res *resp)
{
struct xdr_stream xdr;
__be32 *p;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
p = xdr_inline_decode(&xdr, sizeof(u32));
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
return -EIO;
resp->state = ntohl(*p);
resp->state = be32_to_cpup(p);
dprintk("lockd: xdr_dec_stat state %d\n", resp->state);
dprintk("lockd: %s state %d\n", __func__, resp->state);
return 0;
}
......@@ -542,8 +516,8 @@ static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p,
static struct rpc_procinfo nsm_procedures[] = {
[NSMPROC_MON] = {
.p_proc = NSMPROC_MON,
.p_encode = (kxdrproc_t)xdr_enc_mon,
.p_decode = (kxdrproc_t)xdr_dec_stat_res,
.p_encode = (kxdreproc_t)nsm_xdr_enc_mon,
.p_decode = (kxdrdproc_t)nsm_xdr_dec_stat_res,
.p_arglen = SM_mon_sz,
.p_replen = SM_monres_sz,
.p_statidx = NSMPROC_MON,
......@@ -551,8 +525,8 @@ static struct rpc_procinfo nsm_procedures[] = {
},
[NSMPROC_UNMON] = {
.p_proc = NSMPROC_UNMON,
.p_encode = (kxdrproc_t)xdr_enc_unmon,
.p_decode = (kxdrproc_t)xdr_dec_stat,
.p_encode = (kxdreproc_t)nsm_xdr_enc_unmon,
.p_decode = (kxdrdproc_t)nsm_xdr_dec_stat,
.p_arglen = SM_mon_id_sz,
.p_replen = SM_unmonres_sz,
.p_statidx = NSMPROC_UNMON,
......
......@@ -51,7 +51,7 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
return 0;
no_locks:
nlm_release_host(host);
nlmsvc_release_host(host);
if (error)
return error;
return nlm_lck_denied_nolocks;
......@@ -92,7 +92,7 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
else
dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
}
......@@ -134,7 +134,7 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
else
dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
}
......@@ -164,7 +164,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
dprintk("lockd: CANCEL status %d\n", ntohl(resp->status));
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
......@@ -197,7 +197,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = nlmsvc_unlock(file, &argp->lock);
dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status));
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
......@@ -229,7 +229,7 @@ static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
static void nlm4svc_callback_release(void *data)
{
nlm_release_call(data);
nlmsvc_release_call(data);
}
static const struct rpc_call_ops nlm4svc_callback_ops = {
......@@ -261,7 +261,7 @@ static __be32 nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
stat = func(rqstp, argp, &call->a_res);
if (stat != 0) {
nlm_release_call(call);
nlmsvc_release_call(call);
return stat;
}
......@@ -334,7 +334,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = nlmsvc_share_file(host, file, argp);
dprintk("lockd: SHARE status %d\n", ntohl(resp->status));
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
......@@ -367,7 +367,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = nlmsvc_unshare_file(host, file, argp);
dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status));
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
......@@ -399,7 +399,7 @@ nlm4svc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
return rpc_success;
nlmsvc_free_host_resources(host);
nlm_release_host(host);
nlmsvc_release_host(host);
return rpc_success;
}
......
......@@ -46,6 +46,7 @@ static void nlmsvc_remove_block(struct nlm_block *block);
static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock);
static void nlmsvc_freegrantargs(struct nlm_rqst *call);
static const struct rpc_call_ops nlmsvc_grant_ops;
static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie);
/*
* The list of blocked locks to retry
......@@ -233,7 +234,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
failed_free:
kfree(block);
failed:
nlm_release_call(call);
nlmsvc_release_call(call);
return NULL;
}
......@@ -266,7 +267,7 @@ static void nlmsvc_free_block(struct kref *kref)
mutex_unlock(&file->f_mutex);
nlmsvc_freegrantargs(block->b_call);
nlm_release_call(block->b_call);
nlmsvc_release_call(block->b_call);
nlm_release_file(block->b_file);
kfree(block->b_fl);
kfree(block);
......@@ -934,3 +935,32 @@ nlmsvc_retry_blocked(void)
return timeout;
}
#ifdef RPC_DEBUG
static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
{
/*
* We can get away with a static buffer because we're only
* called with BKL held.
*/
static char buf[2*NLM_MAXCOOKIELEN+1];
unsigned int i, len = sizeof(buf);
char *p = buf;
len--; /* allow for trailing \0 */
if (len < 3)
return "???";
for (i = 0 ; i < cookie->len ; i++) {
if (len < 2) {
strcpy(p-3, "...");
break;
}
sprintf(p, "%02x", cookie->data[i]);
p += 2;
len -= 2;
}
*p = '\0';
return buf;
}
#endif
......@@ -80,7 +80,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
return 0;
no_locks:
nlm_release_host(host);
nlmsvc_release_host(host);
if (error)
return error;
return nlm_lck_denied_nolocks;
......@@ -122,7 +122,7 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
dprintk("lockd: TEST status %d vers %d\n",
ntohl(resp->status), rqstp->rq_vers);
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
}
......@@ -164,7 +164,7 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
else
dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
}
......@@ -194,7 +194,7 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = cast_status(nlmsvc_cancel_blocked(file, &argp->lock));
dprintk("lockd: CANCEL status %d\n", ntohl(resp->status));
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
......@@ -227,7 +227,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = cast_status(nlmsvc_unlock(file, &argp->lock));
dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status));
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
......@@ -257,9 +257,17 @@ static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
-task->tk_status);
}
void nlmsvc_release_call(struct nlm_rqst *call)
{
if (!atomic_dec_and_test(&call->a_count))
return;
nlmsvc_release_host(call->a_host);
kfree(call);
}
static void nlmsvc_callback_release(void *data)
{
nlm_release_call(data);
nlmsvc_release_call(data);
}
static const struct rpc_call_ops nlmsvc_callback_ops = {
......@@ -291,7 +299,7 @@ static __be32 nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
stat = func(rqstp, argp, &call->a_res);
if (stat != 0) {
nlm_release_call(call);
nlmsvc_release_call(call);
return stat;
}
......@@ -366,7 +374,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = cast_status(nlmsvc_share_file(host, file, argp));
dprintk("lockd: SHARE status %d\n", ntohl(resp->status));
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
......@@ -399,7 +407,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = cast_status(nlmsvc_unshare_file(host, file, argp));
dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status));
nlm_release_host(host);
nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
......@@ -431,7 +439,7 @@ nlmsvc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
return rpc_success;
nlmsvc_free_host_resources(host);
nlm_release_host(host);
nlmsvc_release_host(host);
return rpc_success;
}
......
......@@ -148,37 +148,6 @@ nlm_decode_lock(__be32 *p, struct nlm_lock *lock)
return p;
}
/*
* Encode a lock as part of an NLM call
*/
static __be32 *
nlm_encode_lock(__be32 *p, struct nlm_lock *lock)
{
struct file_lock *fl = &lock->fl;
__s32 start, len;
if (!(p = xdr_encode_string(p, lock->caller))
|| !(p = nlm_encode_fh(p, &lock->fh))
|| !(p = nlm_encode_oh(p, &lock->oh)))
return NULL;
if (fl->fl_start > NLM_OFFSET_MAX
|| (fl->fl_end > NLM_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
return NULL;
start = loff_t_to_s32(fl->fl_start);
if (fl->fl_end == OFFSET_MAX)
len = 0;
else
len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
*p++ = htonl(lock->svid);
*p++ = htonl(start);
*p++ = htonl(len);
return p;
}
/*
* Encode result of a TEST/TEST_MSG call
*/
......@@ -372,259 +341,3 @@ nlmsvc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
{
return xdr_ressize_check(rqstp, p);
}
/*
* Now, the client side XDR functions
*/
#ifdef NLMCLNT_SUPPORT_SHARES
static int
nlmclt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr)
{
return 0;
}
#endif
static int
nlmclt_encode_testargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
{
struct nlm_lock *lock = &argp->lock;
if (!(p = nlm_encode_cookie(p, &argp->cookie)))
return -EIO;
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
if (!(p = nlm_encode_lock(p, lock)))
return -EIO;
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlmclt_decode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
{
if (!(p = nlm_decode_cookie(p, &resp->cookie)))
return -EIO;
resp->status = *p++;
if (resp->status == nlm_lck_denied) {
struct file_lock *fl = &resp->lock.fl;
u32 excl;
s32 start, len, end;
memset(&resp->lock, 0, sizeof(resp->lock));
locks_init_lock(fl);
excl = ntohl(*p++);
resp->lock.svid = ntohl(*p++);
fl->fl_pid = (pid_t)resp->lock.svid;
if (!(p = nlm_decode_oh(p, &resp->lock.oh)))
return -EIO;
fl->fl_flags = FL_POSIX;
fl->fl_type = excl? F_WRLCK : F_RDLCK;
start = ntohl(*p++);
len = ntohl(*p++);
end = start + len - 1;
fl->fl_start = s32_to_loff_t(start);
if (len == 0 || end < 0)
fl->fl_end = OFFSET_MAX;
else
fl->fl_end = s32_to_loff_t(end);
}
return 0;
}
static int
nlmclt_encode_lockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
{
struct nlm_lock *lock = &argp->lock;
if (!(p = nlm_encode_cookie(p, &argp->cookie)))
return -EIO;
*p++ = argp->block? xdr_one : xdr_zero;
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
if (!(p = nlm_encode_lock(p, lock)))
return -EIO;
*p++ = argp->reclaim? xdr_one : xdr_zero;
*p++ = htonl(argp->state);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlmclt_encode_cancargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
{
struct nlm_lock *lock = &argp->lock;
if (!(p = nlm_encode_cookie(p, &argp->cookie)))
return -EIO;
*p++ = argp->block? xdr_one : xdr_zero;
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
if (!(p = nlm_encode_lock(p, lock)))
return -EIO;
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlmclt_encode_unlockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
{
struct nlm_lock *lock = &argp->lock;
if (!(p = nlm_encode_cookie(p, &argp->cookie)))
return -EIO;
if (!(p = nlm_encode_lock(p, lock)))
return -EIO;
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlmclt_encode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
{
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
return -EIO;
*p++ = resp->status;
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlmclt_encode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
{
if (!(p = nlm_encode_testres(p, resp)))
return -EIO;
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlmclt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
{
if (!(p = nlm_decode_cookie(p, &resp->cookie)))
return -EIO;
resp->status = *p++;
return 0;
}
#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
#endif
/*
* Buffer requirements for NLM
*/
#define NLM_void_sz 0
#define NLM_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN)
#define NLM_caller_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
#define NLM_owner_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
#define NLM_fhandle_sz 1+XDR_QUADLEN(NFS2_FHSIZE)
#define NLM_lock_sz 3+NLM_caller_sz+NLM_owner_sz+NLM_fhandle_sz
#define NLM_holder_sz 4+NLM_owner_sz
#define NLM_testargs_sz NLM_cookie_sz+1+NLM_lock_sz
#define NLM_lockargs_sz NLM_cookie_sz+4+NLM_lock_sz
#define NLM_cancargs_sz NLM_cookie_sz+2+NLM_lock_sz
#define NLM_unlockargs_sz NLM_cookie_sz+NLM_lock_sz
#define NLM_testres_sz NLM_cookie_sz+1+NLM_holder_sz
#define NLM_res_sz NLM_cookie_sz+1
#define NLM_norep_sz 0
/*
* For NLM, a void procedure really returns nothing
*/
#define nlmclt_decode_norep NULL
#define PROC(proc, argtype, restype) \
[NLMPROC_##proc] = { \
.p_proc = NLMPROC_##proc, \
.p_encode = (kxdrproc_t) nlmclt_encode_##argtype, \
.p_decode = (kxdrproc_t) nlmclt_decode_##restype, \
.p_arglen = NLM_##argtype##_sz, \
.p_replen = NLM_##restype##_sz, \
.p_statidx = NLMPROC_##proc, \
.p_name = #proc, \
}
static struct rpc_procinfo nlm_procedures[] = {
PROC(TEST, testargs, testres),
PROC(LOCK, lockargs, res),
PROC(CANCEL, cancargs, res),
PROC(UNLOCK, unlockargs, res),
PROC(GRANTED, testargs, res),
PROC(TEST_MSG, testargs, norep),
PROC(LOCK_MSG, lockargs, norep),
PROC(CANCEL_MSG, cancargs, norep),
PROC(UNLOCK_MSG, unlockargs, norep),
PROC(GRANTED_MSG, testargs, norep),
PROC(TEST_RES, testres, norep),
PROC(LOCK_RES, res, norep),
PROC(CANCEL_RES, res, norep),
PROC(UNLOCK_RES, res, norep),
PROC(GRANTED_RES, res, norep),
#ifdef NLMCLNT_SUPPORT_SHARES
PROC(SHARE, shareargs, shareres),
PROC(UNSHARE, shareargs, shareres),
PROC(NM_LOCK, lockargs, res),
PROC(FREE_ALL, notify, void),
#endif
};
static struct rpc_version nlm_version1 = {
.number = 1,
.nrprocs = 16,
.procs = nlm_procedures,
};
static struct rpc_version nlm_version3 = {
.number = 3,
.nrprocs = 24,
.procs = nlm_procedures,
};
static struct rpc_version * nlm_versions[] = {
[1] = &nlm_version1,
[3] = &nlm_version3,
#ifdef CONFIG_LOCKD_V4
[4] = &nlm_version4,
#endif
};
static struct rpc_stat nlm_stats;
struct rpc_program nlm_program = {
.name = "lockd",
.number = NLM_PROGRAM,
.nrvers = ARRAY_SIZE(nlm_versions),
.version = nlm_versions,
.stats = &nlm_stats,
};
#ifdef RPC_DEBUG
const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
{
/*
* We can get away with a static buffer because we're only
* called with BKL held.
*/
static char buf[2*NLM_MAXCOOKIELEN+1];
unsigned int i, len = sizeof(buf);
char *p = buf;
len--; /* allow for trailing \0 */
if (len < 3)
return "???";
for (i = 0 ; i < cookie->len ; i++) {
if (len < 2) {
strcpy(p-3, "...");
break;
}
sprintf(p, "%02x", cookie->data[i]);
p += 2;
len -= 2;
}
*p = '\0';
return buf;
}
#endif
......@@ -93,15 +93,6 @@ nlm4_decode_fh(__be32 *p, struct nfs_fh *f)
return p + XDR_QUADLEN(f->size);
}
static __be32 *
nlm4_encode_fh(__be32 *p, struct nfs_fh *f)
{
*p++ = htonl(f->size);
if (f->size) p[XDR_QUADLEN(f->size)-1] = 0; /* don't leak anything */
memcpy(p, f->data, f->size);
return p + XDR_QUADLEN(f->size);
}
/*
* Encode and decode owner handle
*/
......@@ -111,12 +102,6 @@ nlm4_decode_oh(__be32 *p, struct xdr_netobj *oh)
return xdr_decode_netobj(p, oh);
}
static __be32 *
nlm4_encode_oh(__be32 *p, struct xdr_netobj *oh)
{
return xdr_encode_netobj(p, oh);
}
static __be32 *
nlm4_decode_lock(__be32 *p, struct nlm_lock *lock)
{
......@@ -149,38 +134,6 @@ nlm4_decode_lock(__be32 *p, struct nlm_lock *lock)
return p;
}
/*
* Encode a lock as part of an NLM call
*/
static __be32 *
nlm4_encode_lock(__be32 *p, struct nlm_lock *lock)
{
struct file_lock *fl = &lock->fl;
__s64 start, len;
if (!(p = xdr_encode_string(p, lock->caller))
|| !(p = nlm4_encode_fh(p, &lock->fh))
|| !(p = nlm4_encode_oh(p, &lock->oh)))
return NULL;
if (fl->fl_start > NLM4_OFFSET_MAX
|| (fl->fl_end > NLM4_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
return NULL;
*p++ = htonl(lock->svid);
start = loff_t_to_s64(fl->fl_start);
if (fl->fl_end == OFFSET_MAX)
len = 0;
else
len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
p = xdr_encode_hyper(p, start);
p = xdr_encode_hyper(p, len);
return p;
}
/*
* Encode result of a TEST/TEST_MSG call
*/
......@@ -379,211 +332,3 @@ nlm4svc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
{
return xdr_ressize_check(rqstp, p);
}
/*
* Now, the client side XDR functions
*/
#ifdef NLMCLNT_SUPPORT_SHARES
static int
nlm4clt_decode_void(struct rpc_rqst *req, __be32 *p, void *ptr)
{
return 0;
}
#endif
static int
nlm4clt_encode_testargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
{
struct nlm_lock *lock = &argp->lock;
if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
return -EIO;
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
if (!(p = nlm4_encode_lock(p, lock)))
return -EIO;
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlm4clt_decode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
{
if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
return -EIO;
resp->status = *p++;
if (resp->status == nlm_lck_denied) {
struct file_lock *fl = &resp->lock.fl;
u32 excl;
__u64 start, len;
__s64 end;
memset(&resp->lock, 0, sizeof(resp->lock));
locks_init_lock(fl);
excl = ntohl(*p++);
resp->lock.svid = ntohl(*p++);
fl->fl_pid = (pid_t)resp->lock.svid;
if (!(p = nlm4_decode_oh(p, &resp->lock.oh)))
return -EIO;
fl->fl_flags = FL_POSIX;
fl->fl_type = excl? F_WRLCK : F_RDLCK;
p = xdr_decode_hyper(p, &start);
p = xdr_decode_hyper(p, &len);
end = start + len - 1;
fl->fl_start = s64_to_loff_t(start);
if (len == 0 || end < 0)
fl->fl_end = OFFSET_MAX;
else
fl->fl_end = s64_to_loff_t(end);
}
return 0;
}
static int
nlm4clt_encode_lockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
{
struct nlm_lock *lock = &argp->lock;
if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
return -EIO;
*p++ = argp->block? xdr_one : xdr_zero;
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
if (!(p = nlm4_encode_lock(p, lock)))
return -EIO;
*p++ = argp->reclaim? xdr_one : xdr_zero;
*p++ = htonl(argp->state);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlm4clt_encode_cancargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
{
struct nlm_lock *lock = &argp->lock;
if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
return -EIO;
*p++ = argp->block? xdr_one : xdr_zero;
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
if (!(p = nlm4_encode_lock(p, lock)))
return -EIO;
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlm4clt_encode_unlockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
{
struct nlm_lock *lock = &argp->lock;
if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
return -EIO;
if (!(p = nlm4_encode_lock(p, lock)))
return -EIO;
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlm4clt_encode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
{
if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
return -EIO;
*p++ = resp->status;
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlm4clt_encode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
{
if (!(p = nlm4_encode_testres(p, resp)))
return -EIO;
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
nlm4clt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
{
if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
return -EIO;
resp->status = *p++;
return 0;
}
#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
#endif
#if (NLMCLNT_OHSIZE > NLM_MAXSTRLEN)
# error "NLM host name cannot be larger than NLM's maximum string length!"
#endif
/*
* Buffer requirements for NLM
*/
#define NLM4_void_sz 0
#define NLM4_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN)
#define NLM4_caller_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
#define NLM4_owner_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
#define NLM4_fhandle_sz 1+XDR_QUADLEN(NFS3_FHSIZE)
#define NLM4_lock_sz 5+NLM4_caller_sz+NLM4_owner_sz+NLM4_fhandle_sz
#define NLM4_holder_sz 6+NLM4_owner_sz
#define NLM4_testargs_sz NLM4_cookie_sz+1+NLM4_lock_sz
#define NLM4_lockargs_sz NLM4_cookie_sz+4+NLM4_lock_sz
#define NLM4_cancargs_sz NLM4_cookie_sz+2+NLM4_lock_sz
#define NLM4_unlockargs_sz NLM4_cookie_sz+NLM4_lock_sz
#define NLM4_testres_sz NLM4_cookie_sz+1+NLM4_holder_sz
#define NLM4_res_sz NLM4_cookie_sz+1
#define NLM4_norep_sz 0
/*
* For NLM, a void procedure really returns nothing
*/
#define nlm4clt_decode_norep NULL
#define PROC(proc, argtype, restype) \
[NLMPROC_##proc] = { \
.p_proc = NLMPROC_##proc, \
.p_encode = (kxdrproc_t) nlm4clt_encode_##argtype, \
.p_decode = (kxdrproc_t) nlm4clt_decode_##restype, \
.p_arglen = NLM4_##argtype##_sz, \
.p_replen = NLM4_##restype##_sz, \
.p_statidx = NLMPROC_##proc, \
.p_name = #proc, \
}
static struct rpc_procinfo nlm4_procedures[] = {
PROC(TEST, testargs, testres),
PROC(LOCK, lockargs, res),
PROC(CANCEL, cancargs, res),
PROC(UNLOCK, unlockargs, res),
PROC(GRANTED, testargs, res),
PROC(TEST_MSG, testargs, norep),
PROC(LOCK_MSG, lockargs, norep),
PROC(CANCEL_MSG, cancargs, norep),
PROC(UNLOCK_MSG, unlockargs, norep),
PROC(GRANTED_MSG, testargs, norep),
PROC(TEST_RES, testres, norep),
PROC(LOCK_RES, res, norep),
PROC(CANCEL_RES, res, norep),
PROC(UNLOCK_RES, res, norep),
PROC(GRANTED_RES, res, norep),
#ifdef NLMCLNT_SUPPORT_SHARES
PROC(SHARE, shareargs, shareres),
PROC(UNSHARE, shareargs, shareres),
PROC(NM_LOCK, lockargs, res),
PROC(FREE_ALL, notify, void),
#endif
};
struct rpc_version nlm_version4 = {
.number = 4,
.nrprocs = 24,
.procs = nlm4_procedures,
};
......@@ -16,9 +16,7 @@
#include <linux/freezer.h>
#include <linux/kthread.h>
#include <linux/sunrpc/svcauth_gss.h>
#if defined(CONFIG_NFS_V4_1)
#include <linux/sunrpc/bc_xprt.h>
#endif
#include <net/inet_sock.h>
......@@ -136,6 +134,33 @@ nfs4_callback_up(struct svc_serv *serv)
}
#if defined(CONFIG_NFS_V4_1)
/*
* * CB_SEQUENCE operations will fail until the callback sessionid is set.
* */
int nfs4_set_callback_sessionid(struct nfs_client *clp)
{
struct svc_serv *serv = clp->cl_rpcclient->cl_xprt->bc_serv;
struct nfs4_sessionid *bc_sid;
if (!serv->sv_bc_xprt)
return -EINVAL;
/* on success freed in xprt_free */
bc_sid = kmalloc(sizeof(struct nfs4_sessionid), GFP_KERNEL);
if (!bc_sid)
return -ENOMEM;
memcpy(bc_sid->data, &clp->cl_session->sess_id.data,
NFS4_MAX_SESSIONID_LEN);
spin_lock_bh(&serv->sv_cb_lock);
serv->sv_bc_xprt->xpt_bc_sid = bc_sid;
spin_unlock_bh(&serv->sv_cb_lock);
dprintk("%s set xpt_bc_sid=%u:%u:%u:%u for sv_bc_xprt %p\n", __func__,
((u32 *)bc_sid->data)[0], ((u32 *)bc_sid->data)[1],
((u32 *)bc_sid->data)[2], ((u32 *)bc_sid->data)[3],
serv->sv_bc_xprt);
return 0;
}
/*
* The callback service for NFSv4.1 callbacks
*/
......@@ -177,30 +202,38 @@ nfs41_callback_svc(void *vrqstp)
struct svc_rqst *
nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
{
struct svc_xprt *bc_xprt;
struct svc_rqst *rqstp = ERR_PTR(-ENOMEM);
struct svc_rqst *rqstp;
int ret;
dprintk("--> %s\n", __func__);
/* Create a svc_sock for the service */
bc_xprt = svc_sock_create(serv, xprt->prot);
if (!bc_xprt)
/*
* Create an svc_sock for the back channel service that shares the
* fore channel connection.
* Returns the input port (0) and sets the svc_serv bc_xprt on success
*/
ret = svc_create_xprt(serv, "tcp-bc", &init_net, PF_INET, 0,
SVC_SOCK_ANONYMOUS);
if (ret < 0) {
rqstp = ERR_PTR(ret);
goto out;
}
/*
* Save the svc_serv in the transport so that it can
* be referenced when the session backchannel is initialized
*/
serv->bc_xprt = bc_xprt;
xprt->bc_serv = serv;
INIT_LIST_HEAD(&serv->sv_cb_list);
spin_lock_init(&serv->sv_cb_lock);
init_waitqueue_head(&serv->sv_cb_waitq);
rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
if (IS_ERR(rqstp))
svc_sock_destroy(bc_xprt);
if (IS_ERR(rqstp)) {
svc_xprt_put(serv->sv_bc_xprt);
serv->sv_bc_xprt = NULL;
}
out:
dprintk("--> %s return %p\n", __func__, rqstp);
dprintk("--> %s return %ld\n", __func__,
IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0);
return rqstp;
}
......@@ -233,6 +266,10 @@ static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
struct nfs_callback_data *cb_info)
{
}
int nfs4_set_callback_sessionid(struct nfs_client *clp)
{
return 0;
}
#endif /* CONFIG_NFS_V4_1 */
/*
......@@ -328,6 +365,9 @@ static int check_gss_callback_principal(struct nfs_client *clp,
struct rpc_clnt *r = clp->cl_rpcclient;
char *p = svc_gss_principal(rqstp);
/* No RPC_AUTH_GSS on NFSv4.1 back channel yet */
if (clp->cl_minorversion != 0)
return SVC_DROP;
/*
* It might just be a normal user principal, in which case
* userspace won't bother to tell us the name at all.
......@@ -345,6 +385,23 @@ static int check_gss_callback_principal(struct nfs_client *clp,
return SVC_OK;
}
/* pg_authenticate method helper */
static struct nfs_client *nfs_cb_find_client(struct svc_rqst *rqstp)
{
struct nfs4_sessionid *sessionid = bc_xprt_sid(rqstp);
int is_cb_compound = rqstp->rq_proc == CB_COMPOUND ? 1 : 0;
dprintk("--> %s rq_proc %d\n", __func__, rqstp->rq_proc);
if (svc_is_backchannel(rqstp))
/* Sessionid (usually) set after CB_NULL ping */
return nfs4_find_client_sessionid(svc_addr(rqstp), sessionid,
is_cb_compound);
else
/* No callback identifier in pg_authenticate */
return nfs4_find_client_no_ident(svc_addr(rqstp));
}
/* pg_authenticate method for nfsv4 callback threads. */
static int nfs_callback_authenticate(struct svc_rqst *rqstp)
{
struct nfs_client *clp;
......@@ -352,7 +409,7 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp)
int ret = SVC_OK;
/* Don't talk to strangers */
clp = nfs_find_client(svc_addr(rqstp), 4);
clp = nfs_cb_find_client(rqstp);
if (clp == NULL)
return SVC_DROP;
......
......@@ -34,10 +34,17 @@ enum nfs4_callback_opnum {
OP_CB_ILLEGAL = 10044,
};
struct cb_process_state {
__be32 drc_status;
struct nfs_client *clp;
struct nfs4_sessionid *svc_sid; /* v4.1 callback service sessionid */
};
struct cb_compound_hdr_arg {
unsigned int taglen;
const char *tag;
unsigned int minorversion;
unsigned int cb_ident; /* v4.0 callback identifier */
unsigned nops;
};
......@@ -103,14 +110,23 @@ struct cb_sequenceres {
uint32_t csr_target_highestslotid;
};
extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
struct cb_sequenceres *res);
extern __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
struct cb_sequenceres *res,
struct cb_process_state *cps);
extern int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation,
const nfs4_stateid *stateid);
#define RCA4_TYPE_MASK_RDATA_DLG 0
#define RCA4_TYPE_MASK_WDATA_DLG 1
#define RCA4_TYPE_MASK_DIR_DLG 2
#define RCA4_TYPE_MASK_FILE_LAYOUT 3
#define RCA4_TYPE_MASK_BLK_LAYOUT 4
#define RCA4_TYPE_MASK_OBJ_LAYOUT_MIN 8
#define RCA4_TYPE_MASK_OBJ_LAYOUT_MAX 9
#define RCA4_TYPE_MASK_OTHER_LAYOUT_MIN 12
#define RCA4_TYPE_MASK_OTHER_LAYOUT_MAX 15
#define RCA4_TYPE_MASK_ALL 0xf31f
struct cb_recallanyargs {
struct sockaddr *craa_addr;
......@@ -118,25 +134,52 @@ struct cb_recallanyargs {
uint32_t craa_type_mask;
};
extern unsigned nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy);
extern __be32 nfs4_callback_recallany(struct cb_recallanyargs *args,
void *dummy,
struct cb_process_state *cps);
struct cb_recallslotargs {
struct sockaddr *crsa_addr;
uint32_t crsa_target_max_slots;
};
extern unsigned nfs4_callback_recallslot(struct cb_recallslotargs *args,
void *dummy);
extern __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args,
void *dummy,
struct cb_process_state *cps);
struct cb_layoutrecallargs {
struct sockaddr *cbl_addr;
uint32_t cbl_recall_type;
uint32_t cbl_layout_type;
uint32_t cbl_layoutchanged;
union {
struct {
struct nfs_fh cbl_fh;
struct pnfs_layout_range cbl_range;
nfs4_stateid cbl_stateid;
};
struct nfs_fsid cbl_fsid;
};
};
#endif /* CONFIG_NFS_V4_1 */
extern unsigned nfs4_callback_layoutrecall(
struct cb_layoutrecallargs *args,
void *dummy, struct cb_process_state *cps);
extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res);
extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses);
extern void nfs4_cb_take_slot(struct nfs_client *clp);
#endif /* CONFIG_NFS_V4_1 */
extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args,
struct cb_getattrres *res,
struct cb_process_state *cps);
extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
struct cb_process_state *cps);
#ifdef CONFIG_NFS_V4
extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt);
extern void nfs_callback_down(int minorversion);
extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation,
const nfs4_stateid *stateid);
extern int nfs4_set_callback_sessionid(struct nfs_client *clp);
#endif /* CONFIG_NFS_V4 */
/*
* nfs41: Callbacks are expected to not cause substantial latency,
......
......@@ -12,30 +12,33 @@
#include "callback.h"
#include "delegation.h"
#include "internal.h"
#include "pnfs.h"
#ifdef NFS_DEBUG
#define NFSDBG_FACILITY NFSDBG_CALLBACK
#endif
__be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res)
__be32 nfs4_callback_getattr(struct cb_getattrargs *args,
struct cb_getattrres *res,
struct cb_process_state *cps)
{
struct nfs_client *clp;
struct nfs_delegation *delegation;
struct nfs_inode *nfsi;
struct inode *inode;
res->status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
goto out;
res->bitmap[0] = res->bitmap[1] = 0;
res->status = htonl(NFS4ERR_BADHANDLE);
clp = nfs_find_client(args->addr, 4);
if (clp == NULL)
goto out;
dprintk("NFS: GETATTR callback request from %s\n",
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
inode = nfs_delegation_find_inode(clp, &args->fh);
inode = nfs_delegation_find_inode(cps->clp, &args->fh);
if (inode == NULL)
goto out_putclient;
goto out;
nfsi = NFS_I(inode);
rcu_read_lock();
delegation = rcu_dereference(nfsi->delegation);
......@@ -55,49 +58,41 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *
out_iput:
rcu_read_unlock();
iput(inode);
out_putclient:
nfs_put_client(clp);
out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status));
return res->status;
}
__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
struct cb_process_state *cps)
{
struct nfs_client *clp;
struct inode *inode;
__be32 res;
res = htonl(NFS4ERR_BADHANDLE);
clp = nfs_find_client(args->addr, 4);
if (clp == NULL)
res = htonl(NFS4ERR_OP_NOT_IN_SESSION);
if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
goto out;
dprintk("NFS: RECALL callback request from %s\n",
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
do {
struct nfs_client *prev = clp;
inode = nfs_delegation_find_inode(clp, &args->fh);
if (inode != NULL) {
/* Set up a helper thread to actually return the delegation */
switch (nfs_async_inode_return_delegation(inode, &args->stateid)) {
case 0:
res = 0;
break;
case -ENOENT:
if (res != 0)
res = htonl(NFS4ERR_BAD_STATEID);
break;
default:
res = htonl(NFS4ERR_RESOURCE);
}
iput(inode);
}
clp = nfs_find_client_next(prev);
nfs_put_client(prev);
} while (clp != NULL);
rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
res = htonl(NFS4ERR_BADHANDLE);
inode = nfs_delegation_find_inode(cps->clp, &args->fh);
if (inode == NULL)
goto out;
/* Set up a helper thread to actually return the delegation */
switch (nfs_async_inode_return_delegation(inode, &args->stateid)) {
case 0:
res = 0;
break;
case -ENOENT:
if (res != 0)
res = htonl(NFS4ERR_BAD_STATEID);
break;
default:
res = htonl(NFS4ERR_RESOURCE);
}
iput(inode);
out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
return res;
......@@ -113,6 +108,139 @@ int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation, const nf
#if defined(CONFIG_NFS_V4_1)
static u32 initiate_file_draining(struct nfs_client *clp,
struct cb_layoutrecallargs *args)
{
struct pnfs_layout_hdr *lo;
struct inode *ino;
bool found = false;
u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
LIST_HEAD(free_me_list);
spin_lock(&clp->cl_lock);
list_for_each_entry(lo, &clp->cl_layouts, plh_layouts) {
if (nfs_compare_fh(&args->cbl_fh,
&NFS_I(lo->plh_inode)->fh))
continue;
ino = igrab(lo->plh_inode);
if (!ino)
continue;
found = true;
/* Without this, layout can be freed as soon
* as we release cl_lock.
*/
get_layout_hdr(lo);
break;
}
spin_unlock(&clp->cl_lock);
if (!found)
return NFS4ERR_NOMATCHING_LAYOUT;
spin_lock(&ino->i_lock);
if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags) ||
mark_matching_lsegs_invalid(lo, &free_me_list,
args->cbl_range.iomode))
rv = NFS4ERR_DELAY;
else
rv = NFS4ERR_NOMATCHING_LAYOUT;
pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
spin_unlock(&ino->i_lock);
pnfs_free_lseg_list(&free_me_list);
put_layout_hdr(lo);
iput(ino);
return rv;
}
static u32 initiate_bulk_draining(struct nfs_client *clp,
struct cb_layoutrecallargs *args)
{
struct pnfs_layout_hdr *lo;
struct inode *ino;
u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
struct pnfs_layout_hdr *tmp;
LIST_HEAD(recall_list);
LIST_HEAD(free_me_list);
struct pnfs_layout_range range = {
.iomode = IOMODE_ANY,
.offset = 0,
.length = NFS4_MAX_UINT64,
};
spin_lock(&clp->cl_lock);
list_for_each_entry(lo, &clp->cl_layouts, plh_layouts) {
if ((args->cbl_recall_type == RETURN_FSID) &&
memcmp(&NFS_SERVER(lo->plh_inode)->fsid,
&args->cbl_fsid, sizeof(struct nfs_fsid)))
continue;
if (!igrab(lo->plh_inode))
continue;
get_layout_hdr(lo);
BUG_ON(!list_empty(&lo->plh_bulk_recall));
list_add(&lo->plh_bulk_recall, &recall_list);
}
spin_unlock(&clp->cl_lock);
list_for_each_entry_safe(lo, tmp,
&recall_list, plh_bulk_recall) {
ino = lo->plh_inode;
spin_lock(&ino->i_lock);
set_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
if (mark_matching_lsegs_invalid(lo, &free_me_list, range.iomode))
rv = NFS4ERR_DELAY;
list_del_init(&lo->plh_bulk_recall);
spin_unlock(&ino->i_lock);
put_layout_hdr(lo);
iput(ino);
}
pnfs_free_lseg_list(&free_me_list);
return rv;
}
static u32 do_callback_layoutrecall(struct nfs_client *clp,
struct cb_layoutrecallargs *args)
{
u32 res = NFS4ERR_DELAY;
dprintk("%s enter, type=%i\n", __func__, args->cbl_recall_type);
if (test_and_set_bit(NFS4CLNT_LAYOUTRECALL, &clp->cl_state))
goto out;
if (args->cbl_recall_type == RETURN_FILE)
res = initiate_file_draining(clp, args);
else
res = initiate_bulk_draining(clp, args);
clear_bit(NFS4CLNT_LAYOUTRECALL, &clp->cl_state);
out:
dprintk("%s returning %i\n", __func__, res);
return res;
}
__be32 nfs4_callback_layoutrecall(struct cb_layoutrecallargs *args,
void *dummy, struct cb_process_state *cps)
{
u32 res;
dprintk("%s: -->\n", __func__);
if (cps->clp)
res = do_callback_layoutrecall(cps->clp, args);
else
res = NFS4ERR_OP_NOT_IN_SESSION;
dprintk("%s: exit with status = %d\n", __func__, res);
return cpu_to_be32(res);
}
static void pnfs_recall_all_layouts(struct nfs_client *clp)
{
struct cb_layoutrecallargs args;
/* Pretend we got a CB_LAYOUTRECALL(ALL) */
memset(&args, 0, sizeof(args));
args.cbl_recall_type = RETURN_ALL;
/* FIXME we ignore errors, what should we do? */
do_callback_layoutrecall(clp, &args);
}
int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, const nfs4_stateid *stateid)
{
if (delegation == NULL)
......@@ -184,42 +312,6 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args)
return htonl(NFS4ERR_SEQ_MISORDERED);
}
/*
* Returns a pointer to a held 'struct nfs_client' that matches the server's
* address, major version number, and session ID. It is the caller's
* responsibility to release the returned reference.
*
* Returns NULL if there are no connections with sessions, or if no session
* matches the one of interest.
*/
static struct nfs_client *find_client_with_session(
const struct sockaddr *addr, u32 nfsversion,
struct nfs4_sessionid *sessionid)
{
struct nfs_client *clp;
clp = nfs_find_client(addr, 4);
if (clp == NULL)
return NULL;
do {
struct nfs_client *prev = clp;
if (clp->cl_session != NULL) {
if (memcmp(clp->cl_session->sess_id.data,
sessionid->data,
NFS4_MAX_SESSIONID_LEN) == 0) {
/* Returns a held reference to clp */
return clp;
}
}
clp = nfs_find_client_next(prev);
nfs_put_client(prev);
} while (clp != NULL);
return NULL;
}
/*
* For each referring call triple, check the session's slot table for
* a match. If the slot is in use and the sequence numbers match, the
......@@ -276,20 +368,34 @@ static bool referring_call_exists(struct nfs_client *clp,
}
__be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
struct cb_sequenceres *res)
struct cb_sequenceres *res,
struct cb_process_state *cps)
{
struct nfs_client *clp;
int i;
__be32 status;
cps->clp = NULL;
status = htonl(NFS4ERR_BADSESSION);
clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid);
/* Incoming session must match the callback session */
if (memcmp(&args->csa_sessionid, cps->svc_sid, NFS4_MAX_SESSIONID_LEN))
goto out;
clp = nfs4_find_client_sessionid(args->csa_addr,
&args->csa_sessionid, 1);
if (clp == NULL)
goto out;
/* state manager is resetting the session */
if (test_bit(NFS4_SESSION_DRAINING, &clp->cl_session->session_state)) {
status = NFS4ERR_DELAY;
goto out;
}
status = validate_seqid(&clp->cl_session->bc_slot_table, args);
if (status)
goto out_putclient;
goto out;
/*
* Check for pending referring calls. If a match is found, a
......@@ -298,7 +404,7 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
*/
if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) {
status = htonl(NFS4ERR_DELAY);
goto out_putclient;
goto out;
}
memcpy(&res->csr_sessionid, &args->csa_sessionid,
......@@ -307,83 +413,93 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
res->csr_slotid = args->csa_slotid;
res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
nfs4_cb_take_slot(clp);
cps->clp = clp; /* put in nfs4_callback_compound */
out_putclient:
nfs_put_client(clp);
out:
for (i = 0; i < args->csa_nrclists; i++)
kfree(args->csa_rclists[i].rcl_refcalls);
kfree(args->csa_rclists);
if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP))
res->csr_status = 0;
else
if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) {
cps->drc_status = status;
status = 0;
} else
res->csr_status = status;
dprintk("%s: exit with status = %d res->csr_status %d\n", __func__,
ntohl(status), ntohl(res->csr_status));
return status;
}
__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy)
static bool
validate_bitmap_values(unsigned long mask)
{
return (mask & ~RCA4_TYPE_MASK_ALL) == 0;
}
__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy,
struct cb_process_state *cps)
{
struct nfs_client *clp;
__be32 status;
fmode_t flags = 0;
status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
clp = nfs_find_client(args->craa_addr, 4);
if (clp == NULL)
status = cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION);
if (!cps->clp) /* set in cb_sequence */
goto out;
dprintk("NFS: RECALL_ANY callback request from %s\n",
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
status = cpu_to_be32(NFS4ERR_INVAL);
if (!validate_bitmap_values(args->craa_type_mask))
goto out;
status = cpu_to_be32(NFS4_OK);
if (test_bit(RCA4_TYPE_MASK_RDATA_DLG, (const unsigned long *)
&args->craa_type_mask))
flags = FMODE_READ;
if (test_bit(RCA4_TYPE_MASK_WDATA_DLG, (const unsigned long *)
&args->craa_type_mask))
flags |= FMODE_WRITE;
if (test_bit(RCA4_TYPE_MASK_FILE_LAYOUT, (const unsigned long *)
&args->craa_type_mask))
pnfs_recall_all_layouts(cps->clp);
if (flags)
nfs_expire_all_delegation_types(clp, flags);
status = htonl(NFS4_OK);
nfs_expire_all_delegation_types(cps->clp, flags);
out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
return status;
}
/* Reduce the fore channel's max_slots to the target value */
__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy)
__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy,
struct cb_process_state *cps)
{
struct nfs_client *clp;
struct nfs4_slot_table *fc_tbl;
__be32 status;
status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
clp = nfs_find_client(args->crsa_addr, 4);
if (clp == NULL)
if (!cps->clp) /* set in cb_sequence */
goto out;
dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n",
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR),
rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR),
args->crsa_target_max_slots);
fc_tbl = &clp->cl_session->fc_slot_table;
fc_tbl = &cps->clp->cl_session->fc_slot_table;
status = htonl(NFS4ERR_BAD_HIGH_SLOT);
if (args->crsa_target_max_slots > fc_tbl->max_slots ||
args->crsa_target_max_slots < 1)
goto out_putclient;
goto out;
status = htonl(NFS4_OK);
if (args->crsa_target_max_slots == fc_tbl->max_slots)
goto out_putclient;
goto out;
fc_tbl->target_max_slots = args->crsa_target_max_slots;
nfs41_handle_recall_slot(clp);
out_putclient:
nfs_put_client(clp); /* balance nfs_find_client */
nfs41_handle_recall_slot(cps->clp);
out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
return status;
......
......@@ -10,8 +10,10 @@
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/slab.h>
#include <linux/sunrpc/bc_xprt.h>
#include "nfs4_fs.h"
#include "callback.h"
#include "internal.h"
#define CB_OP_TAGLEN_MAXSZ (512)
#define CB_OP_HDR_RES_MAXSZ (2 + CB_OP_TAGLEN_MAXSZ)
......@@ -22,6 +24,7 @@
#define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#if defined(CONFIG_NFS_V4_1)
#define CB_OP_LAYOUTRECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \
4 + 1 + 3)
#define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
......@@ -33,7 +36,8 @@
/* Internal error code */
#define NFS4ERR_RESOURCE_HDR 11050
typedef __be32 (*callback_process_op_t)(void *, void *);
typedef __be32 (*callback_process_op_t)(void *, void *,
struct cb_process_state *);
typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *);
typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *);
......@@ -160,7 +164,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
hdr->minorversion = ntohl(*p++);
/* Check minor version is zero or one. */
if (hdr->minorversion <= 1) {
p++; /* skip callback_ident */
hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */
} else {
printk(KERN_WARNING "%s: NFSv4 server callback with "
"illegal minor version %u!\n",
......@@ -220,6 +224,66 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr,
#if defined(CONFIG_NFS_V4_1)
static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp,
struct xdr_stream *xdr,
struct cb_layoutrecallargs *args)
{
__be32 *p;
__be32 status = 0;
uint32_t iomode;
args->cbl_addr = svc_addr(rqstp);
p = read_buf(xdr, 4 * sizeof(uint32_t));
if (unlikely(p == NULL)) {
status = htonl(NFS4ERR_BADXDR);
goto out;
}
args->cbl_layout_type = ntohl(*p++);
/* Depite the spec's xdr, iomode really belongs in the FILE switch,
* as it is unuseable and ignored with the other types.
*/
iomode = ntohl(*p++);
args->cbl_layoutchanged = ntohl(*p++);
args->cbl_recall_type = ntohl(*p++);
if (args->cbl_recall_type == RETURN_FILE) {
args->cbl_range.iomode = iomode;
status = decode_fh(xdr, &args->cbl_fh);
if (unlikely(status != 0))
goto out;
p = read_buf(xdr, 2 * sizeof(uint64_t));
if (unlikely(p == NULL)) {
status = htonl(NFS4ERR_BADXDR);
goto out;
}
p = xdr_decode_hyper(p, &args->cbl_range.offset);
p = xdr_decode_hyper(p, &args->cbl_range.length);
status = decode_stateid(xdr, &args->cbl_stateid);
if (unlikely(status != 0))
goto out;
} else if (args->cbl_recall_type == RETURN_FSID) {
p = read_buf(xdr, 2 * sizeof(uint64_t));
if (unlikely(p == NULL)) {
status = htonl(NFS4ERR_BADXDR);
goto out;
}
p = xdr_decode_hyper(p, &args->cbl_fsid.major);
p = xdr_decode_hyper(p, &args->cbl_fsid.minor);
} else if (args->cbl_recall_type != RETURN_ALL) {
status = htonl(NFS4ERR_BADXDR);
goto out;
}
dprintk("%s: ltype 0x%x iomode %d changed %d recall_type %d\n",
__func__,
args->cbl_layout_type, iomode,
args->cbl_layoutchanged, args->cbl_recall_type);
out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
return status;
}
static __be32 decode_sessionid(struct xdr_stream *xdr,
struct nfs4_sessionid *sid)
{
......@@ -574,10 +638,10 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
case OP_CB_SEQUENCE:
case OP_CB_RECALL_ANY:
case OP_CB_RECALL_SLOT:
case OP_CB_LAYOUTRECALL:
*op = &callback_ops[op_nr];
break;
case OP_CB_LAYOUTRECALL:
case OP_CB_NOTIFY_DEVICEID:
case OP_CB_NOTIFY:
case OP_CB_PUSH_DELEG:
......@@ -593,6 +657,37 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
return htonl(NFS_OK);
}
static void nfs4_callback_free_slot(struct nfs4_session *session)
{
struct nfs4_slot_table *tbl = &session->bc_slot_table;
spin_lock(&tbl->slot_tbl_lock);
/*
* Let the state manager know callback processing done.
* A single slot, so highest used slotid is either 0 or -1
*/
tbl->highest_used_slotid--;
nfs4_check_drain_bc_complete(session);
spin_unlock(&tbl->slot_tbl_lock);
}
static void nfs4_cb_free_slot(struct nfs_client *clp)
{
if (clp && clp->cl_session)
nfs4_callback_free_slot(clp->cl_session);
}
/* A single slot, so highest used slotid is either 0 or -1 */
void nfs4_cb_take_slot(struct nfs_client *clp)
{
struct nfs4_slot_table *tbl = &clp->cl_session->bc_slot_table;
spin_lock(&tbl->slot_tbl_lock);
tbl->highest_used_slotid++;
BUG_ON(tbl->highest_used_slotid != 0);
spin_unlock(&tbl->slot_tbl_lock);
}
#else /* CONFIG_NFS_V4_1 */
static __be32
......@@ -601,6 +696,9 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
}
static void nfs4_cb_free_slot(struct nfs_client *clp)
{
}
#endif /* CONFIG_NFS_V4_1 */
static __be32
......@@ -621,7 +719,8 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
static __be32 process_op(uint32_t minorversion, int nop,
struct svc_rqst *rqstp,
struct xdr_stream *xdr_in, void *argp,
struct xdr_stream *xdr_out, void *resp, int* drc_status)
struct xdr_stream *xdr_out, void *resp,
struct cb_process_state *cps)
{
struct callback_op *op = &callback_ops[0];
unsigned int op_nr;
......@@ -644,8 +743,8 @@ static __be32 process_op(uint32_t minorversion, int nop,
if (status)
goto encode_hdr;
if (*drc_status) {
status = *drc_status;
if (cps->drc_status) {
status = cps->drc_status;
goto encode_hdr;
}
......@@ -653,16 +752,10 @@ static __be32 process_op(uint32_t minorversion, int nop,
if (maxlen > 0 && maxlen < PAGE_SIZE) {
status = op->decode_args(rqstp, xdr_in, argp);
if (likely(status == 0))
status = op->process_op(argp, resp);
status = op->process_op(argp, resp, cps);
} else
status = htonl(NFS4ERR_RESOURCE);
/* Only set by OP_CB_SEQUENCE processing */
if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) {
*drc_status = status;
status = 0;
}
encode_hdr:
res = encode_op_hdr(xdr_out, op_nr, status);
if (unlikely(res))
......@@ -681,8 +774,11 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
struct cb_compound_hdr_arg hdr_arg = { 0 };
struct cb_compound_hdr_res hdr_res = { NULL };
struct xdr_stream xdr_in, xdr_out;
__be32 *p;
__be32 status, drc_status = 0;
__be32 *p, status;
struct cb_process_state cps = {
.drc_status = 0,
.clp = NULL,
};
unsigned int nops = 0;
dprintk("%s: start\n", __func__);
......@@ -696,6 +792,13 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
if (status == __constant_htonl(NFS4ERR_RESOURCE))
return rpc_garbage_args;
if (hdr_arg.minorversion == 0) {
cps.clp = nfs4_find_client_ident(hdr_arg.cb_ident);
if (!cps.clp)
return rpc_drop_reply;
} else
cps.svc_sid = bc_xprt_sid(rqstp);
hdr_res.taglen = hdr_arg.taglen;
hdr_res.tag = hdr_arg.tag;
if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0)
......@@ -703,7 +806,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
while (status == 0 && nops != hdr_arg.nops) {
status = process_op(hdr_arg.minorversion, nops, rqstp,
&xdr_in, argp, &xdr_out, resp, &drc_status);
&xdr_in, argp, &xdr_out, resp, &cps);
nops++;
}
......@@ -716,6 +819,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
*hdr_res.status = status;
*hdr_res.nops = htonl(nops);
nfs4_cb_free_slot(cps.clp);
nfs_put_client(cps.clp);
dprintk("%s: done, status = %u\n", __func__, ntohl(status));
return rpc_success;
}
......@@ -739,6 +844,12 @@ static struct callback_op callback_ops[] = {
.res_maxsize = CB_OP_RECALL_RES_MAXSZ,
},
#if defined(CONFIG_NFS_V4_1)
[OP_CB_LAYOUTRECALL] = {
.process_op = (callback_process_op_t)nfs4_callback_layoutrecall,
.decode_args =
(callback_decode_arg_t)decode_layoutrecall_args,
.res_maxsize = CB_OP_LAYOUTRECALL_RES_MAXSZ,
},
[OP_CB_SEQUENCE] = {
.process_op = (callback_process_op_t)nfs4_callback_sequence,
.decode_args = (callback_decode_arg_t)decode_cb_sequence_args,
......
......@@ -56,6 +56,30 @@ static DEFINE_SPINLOCK(nfs_client_lock);
static LIST_HEAD(nfs_client_list);
static LIST_HEAD(nfs_volume_list);
static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
#ifdef CONFIG_NFS_V4
static DEFINE_IDR(cb_ident_idr); /* Protected by nfs_client_lock */
/*
* Get a unique NFSv4.0 callback identifier which will be used
* by the V4.0 callback service to lookup the nfs_client struct
*/
static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
{
int ret = 0;
if (clp->rpc_ops->version != 4 || minorversion != 0)
return ret;
retry:
if (!idr_pre_get(&cb_ident_idr, GFP_KERNEL))
return -ENOMEM;
spin_lock(&nfs_client_lock);
ret = idr_get_new(&cb_ident_idr, clp, &clp->cl_cb_ident);
spin_unlock(&nfs_client_lock);
if (ret == -EAGAIN)
goto retry;
return ret;
}
#endif /* CONFIG_NFS_V4 */
/*
* RPC cruft for NFS
......@@ -144,7 +168,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
clp->cl_proto = cl_init->proto;
#ifdef CONFIG_NFS_V4
INIT_LIST_HEAD(&clp->cl_delegations);
err = nfs_get_cb_ident_idr(clp, cl_init->minorversion);
if (err)
goto error_cleanup;
spin_lock_init(&clp->cl_lock);
INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
......@@ -170,21 +197,17 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
}
#ifdef CONFIG_NFS_V4
/*
* Clears/puts all minor version specific parts from an nfs_client struct
* reverting it to minorversion 0.
*/
static void nfs4_clear_client_minor_version(struct nfs_client *clp)
{
#ifdef CONFIG_NFS_V4_1
if (nfs4_has_session(clp)) {
static void nfs4_shutdown_session(struct nfs_client *clp)
{
if (nfs4_has_session(clp))
nfs4_destroy_session(clp->cl_session);
clp->cl_session = NULL;
}
clp->cl_mvops = nfs_v4_minor_ops[0];
#endif /* CONFIG_NFS_V4_1 */
}
#else /* CONFIG_NFS_V4_1 */
static void nfs4_shutdown_session(struct nfs_client *clp)
{
}
#endif /* CONFIG_NFS_V4_1 */
/*
* Destroy the NFS4 callback service
......@@ -199,17 +222,49 @@ static void nfs4_shutdown_client(struct nfs_client *clp)
{
if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
nfs4_kill_renewd(clp);
nfs4_clear_client_minor_version(clp);
nfs4_shutdown_session(clp);
nfs4_destroy_callback(clp);
if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
nfs_idmap_delete(clp);
rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
}
/* idr_remove_all is not needed as all id's are removed by nfs_put_client */
void nfs_cleanup_cb_ident_idr(void)
{
idr_destroy(&cb_ident_idr);
}
/* nfs_client_lock held */
static void nfs_cb_idr_remove_locked(struct nfs_client *clp)
{
if (clp->cl_cb_ident)
idr_remove(&cb_ident_idr, clp->cl_cb_ident);
}
static void pnfs_init_server(struct nfs_server *server)
{
rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC");
}
#else
static void nfs4_shutdown_client(struct nfs_client *clp)
{
}
void nfs_cleanup_cb_ident_idr(void)
{
}
static void nfs_cb_idr_remove_locked(struct nfs_client *clp)
{
}
static void pnfs_init_server(struct nfs_server *server)
{
}
#endif /* CONFIG_NFS_V4 */
/*
......@@ -248,6 +303,7 @@ void nfs_put_client(struct nfs_client *clp)
if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) {
list_del(&clp->cl_share_link);
nfs_cb_idr_remove_locked(clp);
spin_unlock(&nfs_client_lock);
BUG_ON(!list_empty(&clp->cl_superblocks));
......@@ -363,70 +419,28 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1,
return 0;
}
/*
* Find a client by IP address and protocol version
* - returns NULL if no such client
*/
struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
{
struct nfs_client *clp;
spin_lock(&nfs_client_lock);
list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
/* Don't match clients that failed to initialise properly */
if (!(clp->cl_cons_state == NFS_CS_READY ||
clp->cl_cons_state == NFS_CS_SESSION_INITING))
continue;
/* Different NFS versions cannot share the same nfs_client */
if (clp->rpc_ops->version != nfsversion)
continue;
/* Match only the IP address, not the port number */
if (!nfs_sockaddr_match_ipaddr(addr, clap))
continue;
atomic_inc(&clp->cl_count);
spin_unlock(&nfs_client_lock);
return clp;
}
spin_unlock(&nfs_client_lock);
return NULL;
}
/*
* Find a client by IP address and protocol version
* - returns NULL if no such client
*/
struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
/* Common match routine for v4.0 and v4.1 callback services */
bool
nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp,
u32 minorversion)
{
struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr;
u32 nfsvers = clp->rpc_ops->version;
struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
spin_lock(&nfs_client_lock);
list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) {
struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
/* Don't match clients that failed to initialise */
if (!(clp->cl_cons_state == NFS_CS_READY ||
clp->cl_cons_state == NFS_CS_SESSION_INITING))
return false;
/* Don't match clients that failed to initialise properly */
if (clp->cl_cons_state != NFS_CS_READY)
continue;
/* Match the version and minorversion */
if (clp->rpc_ops->version != 4 ||
clp->cl_minorversion != minorversion)
return false;
/* Different NFS versions cannot share the same nfs_client */
if (clp->rpc_ops->version != nfsvers)
continue;
/* Match only the IP address, not the port number */
if (!nfs_sockaddr_match_ipaddr(addr, clap))
return false;
/* Match only the IP address, not the port number */
if (!nfs_sockaddr_match_ipaddr(sap, clap))
continue;
atomic_inc(&clp->cl_count);
spin_unlock(&nfs_client_lock);
return clp;
}
spin_unlock(&nfs_client_lock);
return NULL;
return true;
}
/*
......@@ -988,6 +1002,27 @@ static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_serve
target->options = source->options;
}
static void nfs_server_insert_lists(struct nfs_server *server)
{
struct nfs_client *clp = server->nfs_client;
spin_lock(&nfs_client_lock);
list_add_tail_rcu(&server->client_link, &clp->cl_superblocks);
list_add_tail(&server->master_link, &nfs_volume_list);
spin_unlock(&nfs_client_lock);
}
static void nfs_server_remove_lists(struct nfs_server *server)
{
spin_lock(&nfs_client_lock);
list_del_rcu(&server->client_link);
list_del(&server->master_link);
spin_unlock(&nfs_client_lock);
synchronize_rcu();
}
/*
* Allocate and initialise a server record
*/
......@@ -1004,6 +1039,7 @@ static struct nfs_server *nfs_alloc_server(void)
/* Zero out the NFS state stuff */
INIT_LIST_HEAD(&server->client_link);
INIT_LIST_HEAD(&server->master_link);
INIT_LIST_HEAD(&server->delegations);
atomic_set(&server->active, 0);
......@@ -1019,6 +1055,8 @@ static struct nfs_server *nfs_alloc_server(void)
return NULL;
}
pnfs_init_server(server);
return server;
}
......@@ -1029,11 +1067,8 @@ void nfs_free_server(struct nfs_server *server)
{
dprintk("--> nfs_free_server()\n");
nfs_server_remove_lists(server);
unset_pnfs_layoutdriver(server);
spin_lock(&nfs_client_lock);
list_del(&server->client_link);
list_del(&server->master_link);
spin_unlock(&nfs_client_lock);
if (server->destroy != NULL)
server->destroy(server);
......@@ -1108,11 +1143,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
(unsigned long long) server->fsid.major,
(unsigned long long) server->fsid.minor);
spin_lock(&nfs_client_lock);
list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
list_add_tail(&server->master_link, &nfs_volume_list);
spin_unlock(&nfs_client_lock);
nfs_server_insert_lists(server);
server->mount_time = jiffies;
nfs_free_fattr(fattr);
return server;
......@@ -1124,6 +1155,101 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
}
#ifdef CONFIG_NFS_V4
/*
* NFSv4.0 callback thread helper
*
* Find a client by IP address, protocol version, and minorversion
*
* Called from the pg_authenticate method. The callback identifier
* is not used as it has not been decoded.
*
* Returns NULL if no such client
*/
struct nfs_client *
nfs4_find_client_no_ident(const struct sockaddr *addr)
{
struct nfs_client *clp;
spin_lock(&nfs_client_lock);
list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
if (nfs4_cb_match_client(addr, clp, 0) == false)
continue;
atomic_inc(&clp->cl_count);
spin_unlock(&nfs_client_lock);
return clp;
}
spin_unlock(&nfs_client_lock);
return NULL;
}
/*
* NFSv4.0 callback thread helper
*
* Find a client by callback identifier
*/
struct nfs_client *
nfs4_find_client_ident(int cb_ident)
{
struct nfs_client *clp;
spin_lock(&nfs_client_lock);
clp = idr_find(&cb_ident_idr, cb_ident);
if (clp)
atomic_inc(&clp->cl_count);
spin_unlock(&nfs_client_lock);
return clp;
}
#if defined(CONFIG_NFS_V4_1)
/*
* NFSv4.1 callback thread helper
* For CB_COMPOUND calls, find a client by IP address, protocol version,
* minorversion, and sessionID
*
* CREATE_SESSION triggers a CB_NULL ping from servers. The callback service
* sessionid can only be set after the CREATE_SESSION return, so a CB_NULL
* can arrive before the callback sessionid is set. For CB_NULL calls,
* find a client by IP address protocol version, and minorversion.
*
* Returns NULL if no such client
*/
struct nfs_client *
nfs4_find_client_sessionid(const struct sockaddr *addr,
struct nfs4_sessionid *sid, int is_cb_compound)
{
struct nfs_client *clp;
spin_lock(&nfs_client_lock);
list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
if (nfs4_cb_match_client(addr, clp, 1) == false)
continue;
if (!nfs4_has_session(clp))
continue;
/* Match sessionid unless cb_null call*/
if (is_cb_compound && (memcmp(clp->cl_session->sess_id.data,
sid->data, NFS4_MAX_SESSIONID_LEN) != 0))
continue;
atomic_inc(&clp->cl_count);
spin_unlock(&nfs_client_lock);
return clp;
}
spin_unlock(&nfs_client_lock);
return NULL;
}
#else /* CONFIG_NFS_V4_1 */
struct nfs_client *
nfs4_find_client_sessionid(const struct sockaddr *addr,
struct nfs4_sessionid *sid, int is_cb_compound)
{
return NULL;
}
#endif /* CONFIG_NFS_V4_1 */
/*
* Initialize the NFS4 callback service
*/
......@@ -1342,11 +1468,7 @@ static int nfs4_server_common_setup(struct nfs_server *server,
if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
server->namelen = NFS4_MAXNAMLEN;
spin_lock(&nfs_client_lock);
list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
list_add_tail(&server->master_link, &nfs_volume_list);
spin_unlock(&nfs_client_lock);
nfs_server_insert_lists(server);
server->mount_time = jiffies;
out:
nfs_free_fattr(fattr);
......@@ -1551,11 +1673,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
if (error < 0)
goto out_free_server;
spin_lock(&nfs_client_lock);
list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
list_add_tail(&server->master_link, &nfs_volume_list);
spin_unlock(&nfs_client_lock);
nfs_server_insert_lists(server);
server->mount_time = jiffies;
nfs_free_fattr(fattr_fsinfo);
......
......@@ -40,11 +40,23 @@ static void nfs_free_delegation(struct nfs_delegation *delegation)
call_rcu(&delegation->rcu, nfs_free_delegation_callback);
}
/**
* nfs_mark_delegation_referenced - set delegation's REFERENCED flag
* @delegation: delegation to process
*
*/
void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
{
set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
}
/**
* nfs_have_delegation - check if inode has a delegation
* @inode: inode to check
* @flags: delegation types to check for
*
* Returns one if inode has the indicated delegation, otherwise zero.
*/
int nfs_have_delegation(struct inode *inode, fmode_t flags)
{
struct nfs_delegation *delegation;
......@@ -119,10 +131,15 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s
return 0;
}
/*
* Set up a delegation on an inode
/**
* nfs_inode_reclaim_delegation - process a delegation reclaim request
* @inode: inode to process
* @cred: credential to use for request
* @res: new delegation state from server
*
*/
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
struct nfs_openres *res)
{
struct nfs_delegation *delegation;
struct rpc_cred *oldcred = NULL;
......@@ -175,38 +192,52 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation
return inode;
}
static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi,
const nfs4_stateid *stateid,
struct nfs_client *clp)
static struct nfs_delegation *
nfs_detach_delegation_locked(struct nfs_inode *nfsi,
struct nfs_server *server)
{
struct nfs_delegation *delegation =
rcu_dereference_protected(nfsi->delegation,
lockdep_is_held(&clp->cl_lock));
lockdep_is_held(&server->nfs_client->cl_lock));
if (delegation == NULL)
goto nomatch;
spin_lock(&delegation->lock);
if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
sizeof(delegation->stateid.data)) != 0)
goto nomatch_unlock;
list_del_rcu(&delegation->super_list);
delegation->inode = NULL;
nfsi->delegation_state = 0;
rcu_assign_pointer(nfsi->delegation, NULL);
spin_unlock(&delegation->lock);
return delegation;
nomatch_unlock:
spin_unlock(&delegation->lock);
nomatch:
return NULL;
}
/*
* Set up a delegation on an inode
static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
struct nfs_server *server)
{
struct nfs_client *clp = server->nfs_client;
struct nfs_delegation *delegation;
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(nfsi, server);
spin_unlock(&clp->cl_lock);
return delegation;
}
/**
* nfs_inode_set_delegation - set up a delegation on an inode
* @inode: inode to which delegation applies
* @cred: cred to use for subsequent delegation processing
* @res: new delegation state from server
*
* Returns zero on success, or a negative errno value.
*/
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
{
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_client *clp = server->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation, *old_delegation;
struct nfs_delegation *freeme = NULL;
......@@ -227,7 +258,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
spin_lock(&clp->cl_lock);
old_delegation = rcu_dereference_protected(nfsi->delegation,
lockdep_is_held(&clp->cl_lock));
lockdep_is_held(&clp->cl_lock));
if (old_delegation != NULL) {
if (memcmp(&delegation->stateid, &old_delegation->stateid,
sizeof(old_delegation->stateid)) == 0 &&
......@@ -246,9 +277,9 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
delegation = NULL;
goto out;
}
freeme = nfs_detach_delegation_locked(nfsi, NULL, clp);
freeme = nfs_detach_delegation_locked(nfsi, server);
}
list_add_rcu(&delegation->super_list, &clp->cl_delegations);
list_add_rcu(&delegation->super_list, &server->delegations);
nfsi->delegation_state = delegation->type;
rcu_assign_pointer(nfsi->delegation, delegation);
delegation = NULL;
......@@ -290,73 +321,85 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat
return err;
}
/*
* Return all delegations that have been marked for return
/**
* nfs_client_return_marked_delegations - return previously marked delegations
* @clp: nfs_client to process
*
* Returns zero on success, or a negative errno value.
*/
int nfs_client_return_marked_delegations(struct nfs_client *clp)
{
struct nfs_delegation *delegation;
struct nfs_server *server;
struct inode *inode;
int err = 0;
restart:
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
continue;
inode = nfs_delegation_grab_inode(delegation);
if (inode == NULL)
continue;
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp);
spin_unlock(&clp->cl_lock);
rcu_read_unlock();
if (delegation != NULL) {
filemap_flush(inode->i_mapping);
err = __nfs_inode_return_delegation(inode, delegation, 0);
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
list_for_each_entry_rcu(delegation, &server->delegations,
super_list) {
if (!test_and_clear_bit(NFS_DELEGATION_RETURN,
&delegation->flags))
continue;
inode = nfs_delegation_grab_inode(delegation);
if (inode == NULL)
continue;
delegation = nfs_detach_delegation(NFS_I(inode),
server);
rcu_read_unlock();
if (delegation != NULL) {
filemap_flush(inode->i_mapping);
err = __nfs_inode_return_delegation(inode,
delegation, 0);
}
iput(inode);
if (!err)
goto restart;
set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
return err;
}
iput(inode);
if (!err)
goto restart;
set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
return err;
}
rcu_read_unlock();
return 0;
}
/*
* This function returns the delegation without reclaiming opens
* or protecting against delegation reclaims.
* It is therefore really only safe to be called from
* nfs4_clear_inode()
/**
* nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens
* @inode: inode to process
*
* Does not protect against delegation reclaims, therefore really only safe
* to be called from nfs4_clear_inode().
*/
void nfs_inode_return_delegation_noreclaim(struct inode *inode)
{
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
if (rcu_access_pointer(nfsi->delegation) != NULL) {
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);
spin_unlock(&clp->cl_lock);
delegation = nfs_detach_delegation(nfsi, server);
if (delegation != NULL)
nfs_do_return_delegation(inode, delegation, 0);
}
}
/**
* nfs_inode_return_delegation - synchronously return a delegation
* @inode: inode to process
*
* Returns zero on success, or a negative errno value.
*/
int nfs_inode_return_delegation(struct inode *inode)
{
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
int err = 0;
if (rcu_access_pointer(nfsi->delegation) != NULL) {
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);
spin_unlock(&clp->cl_lock);
delegation = nfs_detach_delegation(nfsi, server);
if (delegation != NULL) {
nfs_wb_all(inode);
err = __nfs_inode_return_delegation(inode, delegation, 1);
......@@ -365,46 +408,61 @@ int nfs_inode_return_delegation(struct inode *inode)
return err;
}
static void nfs_mark_return_delegation(struct nfs_client *clp, struct nfs_delegation *delegation)
static void nfs_mark_return_delegation(struct nfs_delegation *delegation)
{
struct nfs_client *clp = NFS_SERVER(delegation->inode)->nfs_client;
set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
}
/*
* Return all delegations associated to a super block
/**
* nfs_super_return_all_delegations - return delegations for one superblock
* @sb: sb to process
*
*/
void nfs_super_return_all_delegations(struct super_block *sb)
{
struct nfs_client *clp = NFS_SB(sb)->nfs_client;
struct nfs_server *server = NFS_SB(sb);
struct nfs_client *clp = server->nfs_client;
struct nfs_delegation *delegation;
if (clp == NULL)
return;
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
spin_lock(&delegation->lock);
if (delegation->inode != NULL && delegation->inode->i_sb == sb)
set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
spin_unlock(&delegation->lock);
}
rcu_read_unlock();
if (nfs_client_return_marked_delegations(clp) != 0)
nfs4_schedule_state_manager(clp);
}
static
void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, fmode_t flags)
static void nfs_mark_return_all_delegation_types(struct nfs_server *server,
fmode_t flags)
{
struct nfs_delegation *delegation;
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
continue;
if (delegation->type & flags)
nfs_mark_return_delegation(clp, delegation);
nfs_mark_return_delegation(delegation);
}
}
static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp,
fmode_t flags)
{
struct nfs_server *server;
rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
nfs_mark_return_all_delegation_types(server, flags);
rcu_read_unlock();
}
......@@ -419,19 +477,32 @@ static void nfs_delegation_run_state_manager(struct nfs_client *clp)
nfs4_schedule_state_manager(clp);
}
/**
* nfs_expire_all_delegation_types
* @clp: client to process
* @flags: delegation types to expire
*
*/
void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags)
{
nfs_client_mark_return_all_delegation_types(clp, flags);
nfs_delegation_run_state_manager(clp);
}
/**
* nfs_expire_all_delegations
* @clp: client to process
*
*/
void nfs_expire_all_delegations(struct nfs_client *clp)
{
nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
}
/*
* Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
/**
* nfs_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN
* @clp: client to process
*
*/
void nfs_handle_cb_pathdown(struct nfs_client *clp)
{
......@@ -440,29 +511,43 @@ void nfs_handle_cb_pathdown(struct nfs_client *clp)
nfs_client_mark_return_all_delegations(clp);
}
static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *clp)
static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
{
struct nfs_delegation *delegation;
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
continue;
nfs_mark_return_delegation(clp, delegation);
nfs_mark_return_delegation(delegation);
}
rcu_read_unlock();
}
/**
* nfs_expire_unreferenced_delegations - Eliminate unused delegations
* @clp: nfs_client to process
*
*/
void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
{
nfs_client_mark_return_unreferenced_delegations(clp);
struct nfs_server *server;
rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
nfs_mark_return_unreferenced_delegations(server);
rcu_read_unlock();
nfs_delegation_run_state_manager(clp);
}
/*
* Asynchronous delegation recall!
/**
* nfs_async_inode_return_delegation - asynchronously return a delegation
* @inode: inode to process
* @stateid: state ID information from CB_RECALL arguments
*
* Returns zero on success, or a negative errno value.
*/
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
int nfs_async_inode_return_delegation(struct inode *inode,
const nfs4_stateid *stateid)
{
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_delegation *delegation;
......@@ -474,22 +559,21 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s
rcu_read_unlock();
return -ENOENT;
}
nfs_mark_return_delegation(clp, delegation);
nfs_mark_return_delegation(delegation);
rcu_read_unlock();
nfs_delegation_run_state_manager(clp);
return 0;
}
/*
* Retrieve the inode associated with a delegation
*/
struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle)
static struct inode *
nfs_delegation_find_inode_server(struct nfs_server *server,
const struct nfs_fh *fhandle)
{
struct nfs_delegation *delegation;
struct inode *res = NULL;
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
spin_lock(&delegation->lock);
if (delegation->inode != NULL &&
nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
......@@ -499,49 +583,121 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs
if (res != NULL)
break;
}
return res;
}
/**
* nfs_delegation_find_inode - retrieve the inode associated with a delegation
* @clp: client state handle
* @fhandle: filehandle from a delegation recall
*
* Returns pointer to inode matching "fhandle," or NULL if a matching inode
* cannot be found.
*/
struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
const struct nfs_fh *fhandle)
{
struct nfs_server *server;
struct inode *res = NULL;
rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
res = nfs_delegation_find_inode_server(server, fhandle);
if (res != NULL)
break;
}
rcu_read_unlock();
return res;
}
/*
* Mark all delegations as needing to be reclaimed
static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
{
struct nfs_delegation *delegation;
list_for_each_entry_rcu(delegation, &server->delegations, super_list)
set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
}
/**
* nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed
* @clp: nfs_client to process
*
*/
void nfs_delegation_mark_reclaim(struct nfs_client *clp)
{
struct nfs_delegation *delegation;
struct nfs_server *server;
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list)
set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
nfs_delegation_mark_reclaim_server(server);
rcu_read_unlock();
}
/*
* Reap all unclaimed delegations after reboot recovery is done
/**
* nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done
* @clp: nfs_client to process
*
*/
void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
{
struct nfs_delegation *delegation;
struct nfs_server *server;
struct inode *inode;
restart:
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0)
continue;
inode = nfs_delegation_grab_inode(delegation);
if (inode == NULL)
continue;
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp);
spin_unlock(&clp->cl_lock);
rcu_read_unlock();
if (delegation != NULL)
nfs_free_delegation(delegation);
iput(inode);
goto restart;
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
list_for_each_entry_rcu(delegation, &server->delegations,
super_list) {
if (test_bit(NFS_DELEGATION_NEED_RECLAIM,
&delegation->flags) == 0)
continue;
inode = nfs_delegation_grab_inode(delegation);
if (inode == NULL)
continue;
delegation = nfs_detach_delegation(NFS_I(inode),
server);
rcu_read_unlock();
if (delegation != NULL)
nfs_free_delegation(delegation);
iput(inode);
goto restart;
}
}
rcu_read_unlock();
}
/**
* nfs_delegations_present - check for existence of delegations
* @clp: client state handle
*
* Returns one if there are any nfs_delegation structures attached
* to this nfs_client.
*/
int nfs_delegations_present(struct nfs_client *clp)
{
struct nfs_server *server;
int ret = 0;
rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
if (!list_empty(&server->delegations)) {
ret = 1;
break;
}
rcu_read_unlock();
return ret;
}
/**
* nfs4_copy_delegation_stateid - Copy inode's state ID information
* @dst: stateid data structure to fill in
* @inode: inode to check
*
* Returns one and fills in "dst->data" * if inode had a delegation,
* otherwise zero is returned.
*/
int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
......
......@@ -44,6 +44,7 @@ void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags);
void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
void nfs_handle_cb_pathdown(struct nfs_client *clp);
int nfs_client_return_marked_delegations(struct nfs_client *clp);
int nfs_delegations_present(struct nfs_client *clp);
void nfs_delegation_mark_reclaim(struct nfs_client *clp);
void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
......
......@@ -33,8 +33,8 @@
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/sched.h>
#include <linux/vmalloc.h>
#include <linux/kmemleak.h>
#include <linux/xattr.h>
#include "delegation.h"
#include "iostat.h"
......@@ -125,9 +125,10 @@ const struct inode_operations nfs4_dir_inode_operations = {
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
.getxattr = nfs4_getxattr,
.setxattr = nfs4_setxattr,
.listxattr = nfs4_listxattr,
.getxattr = generic_getxattr,
.setxattr = generic_setxattr,
.listxattr = generic_listxattr,
.removexattr = generic_removexattr,
};
#endif /* CONFIG_NFS_V4 */
......@@ -172,7 +173,7 @@ struct nfs_cache_array {
struct nfs_cache_array_entry array[0];
};
typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
typedef int (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, int);
typedef struct {
struct file *file;
struct page *page;
......@@ -378,14 +379,14 @@ int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc,
return error;
}
/* Fill in an entry based on the xdr code stored in desc->page */
static
int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, struct xdr_stream *stream)
static int xdr_decode(nfs_readdir_descriptor_t *desc,
struct nfs_entry *entry, struct xdr_stream *xdr)
{
__be32 *p = desc->decode(stream, entry, NFS_SERVER(desc->file->f_path.dentry->d_inode), desc->plus);
if (IS_ERR(p))
return PTR_ERR(p);
int error;
error = desc->decode(xdr, entry, desc->plus);
if (error)
return error;
entry->fattr->time_start = desc->timestamp;
entry->fattr->gencount = desc->gencount;
return 0;
......@@ -459,25 +460,26 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
/* Perform conversion from xdr to cache array */
static
int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry,
void *xdr_page, struct page *page, unsigned int buflen)
struct page **xdr_pages, struct page *page, unsigned int buflen)
{
struct xdr_stream stream;
struct xdr_buf buf;
__be32 *ptr = xdr_page;
struct xdr_buf buf = {
.pages = xdr_pages,
.page_len = buflen,
.buflen = buflen,
.len = buflen,
};
struct page *scratch;
struct nfs_cache_array *array;
unsigned int count = 0;
int status;
buf.head->iov_base = xdr_page;
buf.head->iov_len = buflen;
buf.tail->iov_len = 0;
buf.page_base = 0;
buf.page_len = 0;
buf.buflen = buf.head->iov_len;
buf.len = buf.head->iov_len;
xdr_init_decode(&stream, &buf, ptr);
scratch = alloc_page(GFP_KERNEL);
if (scratch == NULL)
return -ENOMEM;
xdr_init_decode(&stream, &buf, NULL);
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
do {
status = xdr_decode(desc, entry, &stream);
......@@ -506,6 +508,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
} else
status = PTR_ERR(array);
}
put_page(scratch);
return status;
}
......@@ -521,7 +525,6 @@ static
void nfs_readdir_free_large_page(void *ptr, struct page **pages,
unsigned int npages)
{
vm_unmap_ram(ptr, npages);
nfs_readdir_free_pagearray(pages, npages);
}
......@@ -530,9 +533,8 @@ void nfs_readdir_free_large_page(void *ptr, struct page **pages,
* to nfs_readdir_free_large_page
*/
static
void *nfs_readdir_large_page(struct page **pages, unsigned int npages)
int nfs_readdir_large_page(struct page **pages, unsigned int npages)
{
void *ptr;
unsigned int i;
for (i = 0; i < npages; i++) {
......@@ -541,13 +543,11 @@ void *nfs_readdir_large_page(struct page **pages, unsigned int npages)
goto out_freepages;
pages[i] = page;
}
return 0;
ptr = vm_map_ram(pages, npages, 0, PAGE_KERNEL);
if (!IS_ERR_OR_NULL(ptr))
return ptr;
out_freepages:
nfs_readdir_free_pagearray(pages, i);
return NULL;
return -ENOMEM;
}
static
......@@ -566,6 +566,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
entry.eof = 0;
entry.fh = nfs_alloc_fhandle();
entry.fattr = nfs_alloc_fattr();
entry.server = NFS_SERVER(inode);
if (entry.fh == NULL || entry.fattr == NULL)
goto out;
......@@ -577,8 +578,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
memset(array, 0, sizeof(struct nfs_cache_array));
array->eof_index = -1;
pages_ptr = nfs_readdir_large_page(pages, array_size);
if (!pages_ptr)
status = nfs_readdir_large_page(pages, array_size);
if (status < 0)
goto out_release_array;
do {
unsigned int pglen;
......@@ -587,7 +588,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
if (status < 0)
break;
pglen = status;
status = nfs_readdir_page_filler(desc, &entry, pages_ptr, page, pglen);
status = nfs_readdir_page_filler(desc, &entry, pages, page, pglen);
if (status < 0) {
if (status == -ENOSPC)
status = 0;
......@@ -1221,7 +1222,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
goto out_unblock_sillyrename;
}
inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
res = (struct dentry *)inode;
res = ERR_CAST(inode);
if (IS_ERR(res))
goto out_unblock_sillyrename;
......@@ -1355,8 +1356,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
if (nd->flags & LOOKUP_CREATE) {
attr.ia_mode = nd->intent.open.create_mode;
attr.ia_valid = ATTR_MODE;
if (!IS_POSIXACL(dir))
attr.ia_mode &= ~current_umask();
attr.ia_mode &= ~current_umask();
} else {
open_flags &= ~(O_EXCL | O_CREAT);
attr.ia_valid = 0;
......
......@@ -238,7 +238,7 @@ int nfs_map_gid_to_group(struct nfs_client *clp, __u32 gid, char *buf, size_t bu
return nfs_idmap_lookup_name(gid, "group", buf, buflen);
}
#else /* CONFIG_NFS_USE_IDMAPPER not defined */
#else /* CONFIG_NFS_USE_NEW_IDMAPPER not defined */
#include <linux/module.h>
#include <linux/mutex.h>
......
......@@ -1410,9 +1410,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
*/
void nfs4_evict_inode(struct inode *inode)
{
pnfs_destroy_layout(NFS_I(inode));
truncate_inode_pages(&inode->i_data, 0);
end_writeback(inode);
pnfs_destroy_layout(NFS_I(inode));
/* If we are holding a delegation, return it! */
nfs_inode_return_delegation_noreclaim(inode);
/* First call standard NFS clear_inode() code */
......@@ -1619,6 +1619,7 @@ static void __exit exit_nfs_fs(void)
#ifdef CONFIG_PROC_FS
rpc_proc_unregister("nfs");
#endif
nfs_cleanup_cb_ident_idr();
unregister_nfs_fs();
nfs_fs_proc_exit();
nfsiod_stop();
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册