提交 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 @@ ...@@ -4,7 +4,7 @@
obj-$(CONFIG_LOCKD) += lockd.o obj-$(CONFIG_LOCKD) += lockd.o
lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \ lockd-objs-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \
svcproc.o svcsubs.o mon.o xdr.o grace.o svcshare.o svcproc.o svcsubs.o mon.o xdr.o grace.o
lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o lockd-objs-$(CONFIG_LOCKD_V4) += clnt4xdr.o xdr4.o svc4proc.o
lockd-objs := $(lockd-objs-y) 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); ...@@ -79,7 +79,7 @@ EXPORT_SYMBOL_GPL(nlmclnt_init);
*/ */
void nlmclnt_done(struct nlm_host *host) void nlmclnt_done(struct nlm_host *host)
{ {
nlm_release_host(host); nlmclnt_release_host(host);
lockd_down(); lockd_down();
} }
EXPORT_SYMBOL_GPL(nlmclnt_done); EXPORT_SYMBOL_GPL(nlmclnt_done);
...@@ -273,7 +273,7 @@ reclaimer(void *ptr) ...@@ -273,7 +273,7 @@ reclaimer(void *ptr)
spin_unlock(&nlm_blocked_lock); spin_unlock(&nlm_blocked_lock);
/* Release host handle after use */ /* Release host handle after use */
nlm_release_host(host); nlmclnt_release_host(host);
lockd_down(); lockd_down();
return 0; return 0;
} }
...@@ -58,7 +58,7 @@ static void nlm_put_lockowner(struct nlm_lockowner *lockowner) ...@@ -58,7 +58,7 @@ static void nlm_put_lockowner(struct nlm_lockowner *lockowner)
return; return;
list_del(&lockowner->list); list_del(&lockowner->list);
spin_unlock(&lockowner->host->h_lock); spin_unlock(&lockowner->host->h_lock);
nlm_release_host(lockowner->host); nlmclnt_release_host(lockowner->host);
kfree(lockowner); kfree(lockowner);
} }
...@@ -207,22 +207,22 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host) ...@@ -207,22 +207,22 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
printk("nlm_alloc_call: failed, waiting for memory\n"); printk("nlm_alloc_call: failed, waiting for memory\n");
schedule_timeout_interruptible(5*HZ); schedule_timeout_interruptible(5*HZ);
} }
nlm_release_host(host); nlmclnt_release_host(host);
return NULL; 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)) if (!atomic_dec_and_test(&call->a_count))
return; return;
nlm_release_host(call->a_host); nlmclnt_release_host(call->a_host);
nlmclnt_release_lockargs(call); nlmclnt_release_lockargs(call);
kfree(call); kfree(call);
} }
static void nlmclnt_rpc_release(void *data) 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) 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) ...@@ -436,7 +436,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
status = nlm_stat_to_errno(req->a_res.status); status = nlm_stat_to_errno(req->a_res.status);
} }
out: out:
nlm_release_call(req); nlmclnt_release_call(req);
return status; return status;
} }
...@@ -593,7 +593,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) ...@@ -593,7 +593,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
out_unblock: out_unblock:
nlmclnt_finish_block(block); nlmclnt_finish_block(block);
out: out:
nlm_release_call(req); nlmclnt_release_call(req);
return status; return status;
out_unlock: out_unlock:
/* Fatal error: ensure that we remove the lock altogether */ /* Fatal error: ensure that we remove the lock altogether */
...@@ -694,7 +694,7 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) ...@@ -694,7 +694,7 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
/* What to do now? I'm out of my depth... */ /* What to do now? I'm out of my depth... */
status = -ENOLCK; status = -ENOLCK;
out: out:
nlm_release_call(req); nlmclnt_release_call(req);
return status; return status;
} }
...@@ -755,7 +755,7 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl ...@@ -755,7 +755,7 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl
NLMPROC_CANCEL, &nlmclnt_cancel_ops); NLMPROC_CANCEL, &nlmclnt_cancel_ops);
if (status == 0 && req->a_res.status == nlm_lck_denied) if (status == 0 && req->a_res.status == nlm_lck_denied)
status = -ENOLCK; status = -ENOLCK;
nlm_release_call(req); nlmclnt_release_call(req);
return status; 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 @@ ...@@ -25,9 +25,22 @@
#define NLM_HOST_EXPIRE (300 * HZ) #define NLM_HOST_EXPIRE (300 * HZ)
#define NLM_HOST_COLLECT (120 * 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 unsigned long next_gc;
static int nrhosts; static unsigned long nrhosts;
static DEFINE_MUTEX(nlm_host_mutex); static DEFINE_MUTEX(nlm_host_mutex);
static void nlm_gc_hosts(void); static void nlm_gc_hosts(void);
...@@ -40,8 +53,6 @@ struct nlm_lookup_host_info { ...@@ -40,8 +53,6 @@ struct nlm_lookup_host_info {
const u32 version; /* NLM version to search for */ const u32 version; /* NLM version to search for */
const char *hostname; /* remote's hostname */ const char *hostname; /* remote's hostname */
const size_t hostname_len; /* it's length */ 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 */ const int noresvport; /* use non-priv port */
}; };
...@@ -88,127 +99,83 @@ static unsigned int nlm_hash_address(const struct sockaddr *sap) ...@@ -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 nlm_host *host = NULL;
struct hlist_node *pos; unsigned long now = jiffies;
struct nlm_host *host;
struct nsm_handle *nsm = NULL;
mutex_lock(&nlm_host_mutex);
if (time_after_eq(jiffies, next_gc)) if (nsm != NULL)
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)
atomic_inc(&nsm->sm_count); atomic_inc(&nsm->sm_count);
else { else {
host = NULL; host = NULL;
nsm = nsm_get_handle(ni->sap, ni->salen, nsm = nsm_get_handle(ni->sap, ni->salen,
ni->hostname, ni->hostname_len); ni->hostname, ni->hostname_len);
if (!nsm) { if (unlikely(nsm == NULL)) {
dprintk("lockd: nlm_lookup_host failed; " dprintk("lockd: %s failed; no nsm handle\n",
"no nsm handle\n"); __func__);
goto out; goto out;
} }
} }
host = kzalloc(sizeof(*host), GFP_KERNEL); host = kmalloc(sizeof(*host), GFP_KERNEL);
if (!host) { if (unlikely(host == NULL)) {
dprintk("lockd: %s failed; no memory\n", __func__);
nsm_release(nsm); nsm_release(nsm);
dprintk("lockd: nlm_lookup_host failed; no memory\n");
goto out; goto out;
} }
host->h_name = nsm->sm_name;
host->h_addrbuf = nsm->sm_addrbuf;
memcpy(nlm_addr(host), ni->sap, ni->salen); 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); rpc_set_port(nlm_addr(host), 0);
memcpy(nlm_srcaddr(host), ni->src_sap, ni->src_len); host->h_srcaddrlen = 0;
host->h_srcaddrlen = ni->src_len;
host->h_rpcclnt = NULL;
host->h_name = nsm->sm_name;
host->h_version = ni->version; host->h_version = ni->version;
host->h_proto = ni->protocol; host->h_proto = ni->protocol;
host->h_rpcclnt = NULL; host->h_reclaiming = 0;
mutex_init(&host->h_mutex); host->h_server = ni->server;
host->h_nextrebind = jiffies + NLM_HOST_REBIND; host->h_noresvport = ni->noresvport;
host->h_expires = jiffies + NLM_HOST_EXPIRE; host->h_inuse = 0;
atomic_set(&host->h_count, 1);
init_waitqueue_head(&host->h_gracewait); init_waitqueue_head(&host->h_gracewait);
init_rwsem(&host->h_rwsem); init_rwsem(&host->h_rwsem);
host->h_state = 0; /* pseudo NSM state */ host->h_state = 0;
host->h_nsmstate = 0; /* real NSM state */ host->h_nsmstate = 0;
host->h_nsmhandle = nsm; host->h_pidcount = 0;
host->h_server = ni->server; atomic_set(&host->h_count, 1);
host->h_noresvport = ni->noresvport; mutex_init(&host->h_mutex);
hlist_add_head(&host->h_hash, chain); host->h_nextrebind = now + NLM_HOST_REBIND;
host->h_expires = now + NLM_HOST_EXPIRE;
INIT_LIST_HEAD(&host->h_lockowners); INIT_LIST_HEAD(&host->h_lockowners);
spin_lock_init(&host->h_lock); spin_lock_init(&host->h_lock);
INIT_LIST_HEAD(&host->h_granted); INIT_LIST_HEAD(&host->h_granted);
INIT_LIST_HEAD(&host->h_reclaim); INIT_LIST_HEAD(&host->h_reclaim);
host->h_nsmhandle = nsm;
nrhosts++; host->h_addrbuf = nsm->sm_addrbuf;
dprintk("lockd: nlm_lookup_host created host %s\n",
host->h_name);
out: out:
mutex_unlock(&nlm_host_mutex);
return host; return host;
} }
/* /*
* Destroy a host * Destroy an nlm_host and free associated resources
*
* Caller must hold nlm_host_mutex.
*/ */
static void static void nlm_destroy_host_locked(struct nlm_host *host)
nlm_destroy_host(struct nlm_host *host)
{ {
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
dprintk("lockd: destroy host %s\n", host->h_name);
BUG_ON(!list_empty(&host->h_lockowners)); BUG_ON(!list_empty(&host->h_lockowners));
BUG_ON(atomic_read(&host->h_count)); BUG_ON(atomic_read(&host->h_count));
hlist_del_init(&host->h_hash);
nsm_unmonitor(host); nsm_unmonitor(host);
nsm_release(host->h_nsmhandle); nsm_release(host->h_nsmhandle);
...@@ -216,6 +183,8 @@ nlm_destroy_host(struct nlm_host *host) ...@@ -216,6 +183,8 @@ nlm_destroy_host(struct nlm_host *host)
if (clnt != NULL) if (clnt != NULL)
rpc_shutdown_client(clnt); rpc_shutdown_client(clnt);
kfree(host); kfree(host);
nrhosts--;
} }
/** /**
...@@ -249,12 +218,76 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, ...@@ -249,12 +218,76 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
.hostname_len = strlen(hostname), .hostname_len = strlen(hostname),
.noresvport = noresvport, .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__, dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
(hostname ? hostname : "<none>"), version, (hostname ? hostname : "<none>"), version,
(protocol == IPPROTO_UDP ? "udp" : "tcp")); (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, ...@@ -279,12 +312,18 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
const char *hostname, const char *hostname,
const size_t hostname_len) 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 = { struct sockaddr_in sin = {
.sin_family = AF_INET, .sin_family = AF_INET,
}; };
struct sockaddr_in6 sin6 = { struct sockaddr_in6 sin6 = {
.sin6_family = AF_INET6, .sin6_family = AF_INET6,
}; };
struct sockaddr *src_sap;
size_t src_len = rqstp->rq_addrlen;
struct nlm_lookup_host_info ni = { struct nlm_lookup_host_info ni = {
.server = 1, .server = 1,
.sap = svc_addr(rqstp), .sap = svc_addr(rqstp),
...@@ -293,27 +332,91 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, ...@@ -293,27 +332,91 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
.version = rqstp->rq_vers, .version = rqstp->rq_vers,
.hostname = hostname, .hostname = hostname,
.hostname_len = hostname_len, .hostname_len = hostname_len,
.src_len = rqstp->rq_addrlen,
}; };
dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__, dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__,
(int)hostname_len, hostname, rqstp->rq_vers, (int)hostname_len, hostname, rqstp->rq_vers,
(rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp")); (rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
mutex_lock(&nlm_host_mutex);
switch (ni.sap->sa_family) { switch (ni.sap->sa_family) {
case AF_INET: case AF_INET:
sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr; sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr;
ni.src_sap = (struct sockaddr *)&sin; src_sap = (struct sockaddr *)&sin;
break; break;
case AF_INET6: case AF_INET6:
ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6); ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6);
ni.src_sap = (struct sockaddr *)&sin6; src_sap = (struct sockaddr *)&sin6;
break; break;
default: 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) ...@@ -413,20 +516,28 @@ struct nlm_host * nlm_get_host(struct nlm_host *host)
return host; return host;
} }
/* static struct nlm_host *next_host_state(struct hlist_head *cache,
* Release NLM host after use struct nsm_handle *nsm,
*/ const struct nlm_reboot *info)
void nlm_release_host(struct nlm_host *host)
{ {
if (host != NULL) { struct nlm_host *host = NULL;
dprintk("lockd: release host %s\n", host->h_name); struct hlist_head *chain;
BUG_ON(atomic_read(&host->h_count) < 0); struct hlist_node *pos;
if (atomic_dec_and_test(&host->h_count)) {
BUG_ON(!list_empty(&host->h_lockowners)); mutex_lock(&nlm_host_mutex);
BUG_ON(!list_empty(&host->h_granted)); for_each_host(host, pos, chain, cache) {
BUG_ON(!list_empty(&host->h_reclaim)); 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) ...@@ -438,8 +549,6 @@ void nlm_release_host(struct nlm_host *host)
*/ */
void nlm_host_rebooted(const struct nlm_reboot *info) void nlm_host_rebooted(const struct nlm_reboot *info)
{ {
struct hlist_head *chain;
struct hlist_node *pos;
struct nsm_handle *nsm; struct nsm_handle *nsm;
struct nlm_host *host; struct nlm_host *host;
...@@ -452,32 +561,15 @@ void nlm_host_rebooted(const struct nlm_reboot *info) ...@@ -452,32 +561,15 @@ void nlm_host_rebooted(const struct nlm_reboot *info)
* lock for this. * lock for this.
* To avoid processing a host several times, we match the nsmstate. * To avoid processing a host several times, we match the nsmstate.
*/ */
again: mutex_lock(&nlm_host_mutex); while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) {
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { nlmsvc_free_host_resources(host);
hlist_for_each_entry(host, pos, chain, h_hash) { nlmsvc_release_host(host);
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;
}
}
} }
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); nsm_release(nsm);
} }
...@@ -497,13 +589,11 @@ nlm_shutdown_hosts(void) ...@@ -497,13 +589,11 @@ nlm_shutdown_hosts(void)
/* First, make all hosts eligible for gc */ /* First, make all hosts eligible for gc */
dprintk("lockd: nuking all hosts...\n"); dprintk("lockd: nuking all hosts...\n");
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { for_each_host(host, pos, chain, nlm_server_hosts) {
hlist_for_each_entry(host, pos, chain, h_hash) { host->h_expires = jiffies - 1;
host->h_expires = jiffies - 1; if (host->h_rpcclnt) {
if (host->h_rpcclnt) { rpc_shutdown_client(host->h_rpcclnt);
rpc_shutdown_client(host->h_rpcclnt); host->h_rpcclnt = NULL;
host->h_rpcclnt = NULL;
}
} }
} }
...@@ -512,15 +602,13 @@ nlm_shutdown_hosts(void) ...@@ -512,15 +602,13 @@ nlm_shutdown_hosts(void)
mutex_unlock(&nlm_host_mutex); mutex_unlock(&nlm_host_mutex);
/* complain if any hosts are left */ /* complain if any hosts are left */
if (nrhosts) { if (nrhosts != 0) {
printk(KERN_WARNING "lockd: couldn't shutdown host module!\n"); printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
dprintk("lockd: %d hosts left:\n", nrhosts); dprintk("lockd: %lu hosts left:\n", nrhosts);
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { for_each_host(host, pos, chain, nlm_server_hosts) {
hlist_for_each_entry(host, pos, chain, h_hash) { dprintk(" %s (cnt %d use %d exp %ld)\n",
dprintk(" %s (cnt %d use %d exp %ld)\n", host->h_name, atomic_read(&host->h_count),
host->h_name, atomic_read(&host->h_count), host->h_inuse, host->h_expires);
host->h_inuse, host->h_expires);
}
} }
} }
} }
...@@ -538,29 +626,22 @@ nlm_gc_hosts(void) ...@@ -538,29 +626,22 @@ nlm_gc_hosts(void)
struct nlm_host *host; struct nlm_host *host;
dprintk("lockd: host garbage collection\n"); dprintk("lockd: host garbage collection\n");
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { for_each_host(host, pos, chain, nlm_server_hosts)
hlist_for_each_entry(host, pos, chain, h_hash) host->h_inuse = 0;
host->h_inuse = 0;
}
/* Mark all hosts that hold locks, blocks or shares */ /* Mark all hosts that hold locks, blocks or shares */
nlmsvc_mark_resources(); nlmsvc_mark_resources();
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { for_each_host_safe(host, pos, next, chain, nlm_server_hosts) {
hlist_for_each_entry_safe(host, pos, next, chain, h_hash) { if (atomic_read(&host->h_count) || host->h_inuse
if (atomic_read(&host->h_count) || host->h_inuse || time_before(jiffies, host->h_expires)) {
|| time_before(jiffies, host->h_expires)) { dprintk("nlm_gc_hosts skipping %s "
dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n", "(cnt %d use %d exp %ld)\n",
host->h_name, atomic_read(&host->h_count), host->h_name, atomic_read(&host->h_count),
host->h_inuse, host->h_expires); host->h_inuse, host->h_expires);
continue; continue;
}
dprintk("lockd: delete host %s\n", host->h_name);
hlist_del_init(&host->h_hash);
nlm_destroy_host(host);
nrhosts--;
} }
nlm_destroy_host_locked(host);
} }
next_gc = jiffies + NLM_HOST_COLLECT; next_gc = jiffies + NLM_HOST_COLLECT;
......
...@@ -401,26 +401,22 @@ void nsm_release(struct nsm_handle *nsm) ...@@ -401,26 +401,22 @@ void nsm_release(struct nsm_handle *nsm)
* Status Monitor wire protocol. * 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); const u32 len = strlen(string);
__be32 *p; __be32 *p;
if (unlikely(len > SM_MAXSTRLEN)) BUG_ON(len > SM_MAXSTRLEN);
return -EIO; p = xdr_reserve_space(xdr, 4 + len);
p = xdr_reserve_space(xdr, sizeof(u32) + len);
if (unlikely(p == NULL))
return -EIO;
xdr_encode_opaque(p, string, len); xdr_encode_opaque(p, string, len);
return 0;
} }
/* /*
* "mon_name" specifies the host to be monitored. * "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) ...@@ -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" * (via the NLMPROC_SM_NOTIFY call) that the state of host "mon_name"
* has changed. * 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; __be32 *p;
status = encode_nsm_string(xdr, utsname()->nodename); encode_nsm_string(xdr, utsname()->nodename);
if (unlikely(status != 0)) p = xdr_reserve_space(xdr, 4 + 4 + 4);
return status; *p++ = cpu_to_be32(argp->prog);
p = xdr_reserve_space(xdr, 3 * sizeof(u32)); *p++ = cpu_to_be32(argp->vers);
if (unlikely(p == NULL)) *p = cpu_to_be32(argp->proc);
return -EIO;
*p++ = htonl(argp->prog);
*p++ = htonl(argp->vers);
*p++ = htonl(argp->proc);
return 0;
} }
/* /*
* The "mon_id" argument specifies the non-private arguments * The "mon_id" argument specifies the non-private arguments
* of an NSMPROC_MON or NSMPROC_UNMON call. * 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; encode_mon_name(xdr, argp);
encode_my_id(xdr, argp);
status = encode_mon_name(xdr, argp);
if (unlikely(status != 0))
return status;
return encode_my_id(xdr, argp);
} }
/* /*
...@@ -465,68 +451,56 @@ static int encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *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 * by the NSMPROC_MON call. This information will be supplied in the
* NLMPROC_SM_NOTIFY call. * 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; __be32 *p;
p = xdr_reserve_space(xdr, SM_PRIV_SIZE); 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); xdr_encode_opaque_fixed(p, argp->priv->data, SM_PRIV_SIZE);
return 0;
} }
static int xdr_enc_mon(struct rpc_rqst *req, __be32 *p, static void nsm_xdr_enc_mon(struct rpc_rqst *req, struct xdr_stream *xdr,
const struct nsm_args *argp) const struct nsm_args *argp)
{ {
struct xdr_stream xdr; encode_mon_id(xdr, argp);
int status; encode_priv(xdr, argp);
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);
} }
static int xdr_enc_unmon(struct rpc_rqst *req, __be32 *p, static void nsm_xdr_enc_unmon(struct rpc_rqst *req, struct xdr_stream *xdr,
const struct nsm_args *argp) const struct nsm_args *argp)
{ {
struct xdr_stream xdr; encode_mon_id(xdr, argp);
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
return encode_mon_id(&xdr, argp);
} }
static int xdr_dec_stat_res(struct rpc_rqst *rqstp, __be32 *p, static int nsm_xdr_dec_stat_res(struct rpc_rqst *rqstp,
struct nsm_res *resp) 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, 4 + 4);
p = xdr_inline_decode(&xdr, 2 * sizeof(u32));
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return -EIO; return -EIO;
resp->status = ntohl(*p++); resp->status = be32_to_cpup(p++);
resp->state = ntohl(*p); resp->state = be32_to_cpup(p);
dprintk("lockd: xdr_dec_stat_res status %d state %d\n", dprintk("lockd: %s status %d state %d\n",
resp->status, resp->state); __func__, resp->status, resp->state);
return 0; return 0;
} }
static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p, static int nsm_xdr_dec_stat(struct rpc_rqst *rqstp,
struct nsm_res *resp) 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, 4);
p = xdr_inline_decode(&xdr, sizeof(u32));
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return -EIO; 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; return 0;
} }
...@@ -542,8 +516,8 @@ static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p, ...@@ -542,8 +516,8 @@ static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p,
static struct rpc_procinfo nsm_procedures[] = { static struct rpc_procinfo nsm_procedures[] = {
[NSMPROC_MON] = { [NSMPROC_MON] = {
.p_proc = NSMPROC_MON, .p_proc = NSMPROC_MON,
.p_encode = (kxdrproc_t)xdr_enc_mon, .p_encode = (kxdreproc_t)nsm_xdr_enc_mon,
.p_decode = (kxdrproc_t)xdr_dec_stat_res, .p_decode = (kxdrdproc_t)nsm_xdr_dec_stat_res,
.p_arglen = SM_mon_sz, .p_arglen = SM_mon_sz,
.p_replen = SM_monres_sz, .p_replen = SM_monres_sz,
.p_statidx = NSMPROC_MON, .p_statidx = NSMPROC_MON,
...@@ -551,8 +525,8 @@ static struct rpc_procinfo nsm_procedures[] = { ...@@ -551,8 +525,8 @@ static struct rpc_procinfo nsm_procedures[] = {
}, },
[NSMPROC_UNMON] = { [NSMPROC_UNMON] = {
.p_proc = NSMPROC_UNMON, .p_proc = NSMPROC_UNMON,
.p_encode = (kxdrproc_t)xdr_enc_unmon, .p_encode = (kxdreproc_t)nsm_xdr_enc_unmon,
.p_decode = (kxdrproc_t)xdr_dec_stat, .p_decode = (kxdrdproc_t)nsm_xdr_dec_stat,
.p_arglen = SM_mon_id_sz, .p_arglen = SM_mon_id_sz,
.p_replen = SM_unmonres_sz, .p_replen = SM_unmonres_sz,
.p_statidx = NSMPROC_UNMON, .p_statidx = NSMPROC_UNMON,
......
...@@ -51,7 +51,7 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -51,7 +51,7 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
return 0; return 0;
no_locks: no_locks:
nlm_release_host(host); nlmsvc_release_host(host);
if (error) if (error)
return error; return error;
return nlm_lck_denied_nolocks; return nlm_lck_denied_nolocks;
...@@ -92,7 +92,7 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -92,7 +92,7 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
else else
dprintk("lockd: TEST4 status %d\n", ntohl(resp->status)); dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rc; return rc;
} }
...@@ -134,7 +134,7 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -134,7 +134,7 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
else else
dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rc; return rc;
} }
...@@ -164,7 +164,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -164,7 +164,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = nlmsvc_cancel_blocked(file, &argp->lock); resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
dprintk("lockd: CANCEL status %d\n", ntohl(resp->status)); dprintk("lockd: CANCEL status %d\n", ntohl(resp->status));
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rpc_success; return rpc_success;
} }
...@@ -197,7 +197,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -197,7 +197,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = nlmsvc_unlock(file, &argp->lock); resp->status = nlmsvc_unlock(file, &argp->lock);
dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status)); dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status));
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rpc_success; return rpc_success;
} }
...@@ -229,7 +229,7 @@ static void nlm4svc_callback_exit(struct rpc_task *task, void *data) ...@@ -229,7 +229,7 @@ static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
static void nlm4svc_callback_release(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 = { 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 ...@@ -261,7 +261,7 @@ static __be32 nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
stat = func(rqstp, argp, &call->a_res); stat = func(rqstp, argp, &call->a_res);
if (stat != 0) { if (stat != 0) {
nlm_release_call(call); nlmsvc_release_call(call);
return stat; return stat;
} }
...@@ -334,7 +334,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -334,7 +334,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = nlmsvc_share_file(host, file, argp); resp->status = nlmsvc_share_file(host, file, argp);
dprintk("lockd: SHARE status %d\n", ntohl(resp->status)); dprintk("lockd: SHARE status %d\n", ntohl(resp->status));
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rpc_success; return rpc_success;
} }
...@@ -367,7 +367,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -367,7 +367,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = nlmsvc_unshare_file(host, file, argp); resp->status = nlmsvc_unshare_file(host, file, argp);
dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status)); dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status));
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rpc_success; return rpc_success;
} }
...@@ -399,7 +399,7 @@ nlm4svc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -399,7 +399,7 @@ nlm4svc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
return rpc_success; return rpc_success;
nlmsvc_free_host_resources(host); nlmsvc_free_host_resources(host);
nlm_release_host(host); nlmsvc_release_host(host);
return rpc_success; return rpc_success;
} }
......
...@@ -46,6 +46,7 @@ static void nlmsvc_remove_block(struct nlm_block *block); ...@@ -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 int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock);
static void nlmsvc_freegrantargs(struct nlm_rqst *call); static void nlmsvc_freegrantargs(struct nlm_rqst *call);
static const struct rpc_call_ops nlmsvc_grant_ops; 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 * The list of blocked locks to retry
...@@ -233,7 +234,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host, ...@@ -233,7 +234,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
failed_free: failed_free:
kfree(block); kfree(block);
failed: failed:
nlm_release_call(call); nlmsvc_release_call(call);
return NULL; return NULL;
} }
...@@ -266,7 +267,7 @@ static void nlmsvc_free_block(struct kref *kref) ...@@ -266,7 +267,7 @@ static void nlmsvc_free_block(struct kref *kref)
mutex_unlock(&file->f_mutex); mutex_unlock(&file->f_mutex);
nlmsvc_freegrantargs(block->b_call); nlmsvc_freegrantargs(block->b_call);
nlm_release_call(block->b_call); nlmsvc_release_call(block->b_call);
nlm_release_file(block->b_file); nlm_release_file(block->b_file);
kfree(block->b_fl); kfree(block->b_fl);
kfree(block); kfree(block);
...@@ -934,3 +935,32 @@ nlmsvc_retry_blocked(void) ...@@ -934,3 +935,32 @@ nlmsvc_retry_blocked(void)
return timeout; 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, ...@@ -80,7 +80,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
return 0; return 0;
no_locks: no_locks:
nlm_release_host(host); nlmsvc_release_host(host);
if (error) if (error)
return error; return error;
return nlm_lck_denied_nolocks; return nlm_lck_denied_nolocks;
...@@ -122,7 +122,7 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -122,7 +122,7 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
dprintk("lockd: TEST status %d vers %d\n", dprintk("lockd: TEST status %d vers %d\n",
ntohl(resp->status), rqstp->rq_vers); ntohl(resp->status), rqstp->rq_vers);
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rc; return rc;
} }
...@@ -164,7 +164,7 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -164,7 +164,7 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
else else
dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rc; return rc;
} }
...@@ -194,7 +194,7 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -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)); resp->status = cast_status(nlmsvc_cancel_blocked(file, &argp->lock));
dprintk("lockd: CANCEL status %d\n", ntohl(resp->status)); dprintk("lockd: CANCEL status %d\n", ntohl(resp->status));
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rpc_success; return rpc_success;
} }
...@@ -227,7 +227,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -227,7 +227,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = cast_status(nlmsvc_unlock(file, &argp->lock)); resp->status = cast_status(nlmsvc_unlock(file, &argp->lock));
dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status)); dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status));
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rpc_success; return rpc_success;
} }
...@@ -257,9 +257,17 @@ static void nlmsvc_callback_exit(struct rpc_task *task, void *data) ...@@ -257,9 +257,17 @@ static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
-task->tk_status); -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) static void nlmsvc_callback_release(void *data)
{ {
nlm_release_call(data); nlmsvc_release_call(data);
} }
static const struct rpc_call_ops nlmsvc_callback_ops = { 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 ...@@ -291,7 +299,7 @@ static __be32 nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
stat = func(rqstp, argp, &call->a_res); stat = func(rqstp, argp, &call->a_res);
if (stat != 0) { if (stat != 0) {
nlm_release_call(call); nlmsvc_release_call(call);
return stat; return stat;
} }
...@@ -366,7 +374,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -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)); resp->status = cast_status(nlmsvc_share_file(host, file, argp));
dprintk("lockd: SHARE status %d\n", ntohl(resp->status)); dprintk("lockd: SHARE status %d\n", ntohl(resp->status));
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rpc_success; return rpc_success;
} }
...@@ -399,7 +407,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -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)); resp->status = cast_status(nlmsvc_unshare_file(host, file, argp));
dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status)); dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status));
nlm_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rpc_success; return rpc_success;
} }
...@@ -431,7 +439,7 @@ nlmsvc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -431,7 +439,7 @@ nlmsvc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
return rpc_success; return rpc_success;
nlmsvc_free_host_resources(host); nlmsvc_free_host_resources(host);
nlm_release_host(host); nlmsvc_release_host(host);
return rpc_success; return rpc_success;
} }
......
...@@ -148,37 +148,6 @@ nlm_decode_lock(__be32 *p, struct nlm_lock *lock) ...@@ -148,37 +148,6 @@ nlm_decode_lock(__be32 *p, struct nlm_lock *lock)
return p; 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 * Encode result of a TEST/TEST_MSG call
*/ */
...@@ -372,259 +341,3 @@ nlmsvc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy) ...@@ -372,259 +341,3 @@ nlmsvc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
{ {
return xdr_ressize_check(rqstp, p); 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) ...@@ -93,15 +93,6 @@ nlm4_decode_fh(__be32 *p, struct nfs_fh *f)
return p + XDR_QUADLEN(f->size); 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 * Encode and decode owner handle
*/ */
...@@ -111,12 +102,6 @@ nlm4_decode_oh(__be32 *p, struct xdr_netobj *oh) ...@@ -111,12 +102,6 @@ nlm4_decode_oh(__be32 *p, struct xdr_netobj *oh)
return xdr_decode_netobj(p, 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 * static __be32 *
nlm4_decode_lock(__be32 *p, struct nlm_lock *lock) nlm4_decode_lock(__be32 *p, struct nlm_lock *lock)
{ {
...@@ -149,38 +134,6 @@ nlm4_decode_lock(__be32 *p, struct nlm_lock *lock) ...@@ -149,38 +134,6 @@ nlm4_decode_lock(__be32 *p, struct nlm_lock *lock)
return p; 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 * Encode result of a TEST/TEST_MSG call
*/ */
...@@ -379,211 +332,3 @@ nlm4svc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy) ...@@ -379,211 +332,3 @@ nlm4svc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
{ {
return xdr_ressize_check(rqstp, p); 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 @@ ...@@ -16,9 +16,7 @@
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/sunrpc/svcauth_gss.h> #include <linux/sunrpc/svcauth_gss.h>
#if defined(CONFIG_NFS_V4_1)
#include <linux/sunrpc/bc_xprt.h> #include <linux/sunrpc/bc_xprt.h>
#endif
#include <net/inet_sock.h> #include <net/inet_sock.h>
...@@ -136,6 +134,33 @@ nfs4_callback_up(struct svc_serv *serv) ...@@ -136,6 +134,33 @@ nfs4_callback_up(struct svc_serv *serv)
} }
#if defined(CONFIG_NFS_V4_1) #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 * The callback service for NFSv4.1 callbacks
*/ */
...@@ -177,30 +202,38 @@ nfs41_callback_svc(void *vrqstp) ...@@ -177,30 +202,38 @@ nfs41_callback_svc(void *vrqstp)
struct svc_rqst * struct svc_rqst *
nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
{ {
struct svc_xprt *bc_xprt; struct svc_rqst *rqstp;
struct svc_rqst *rqstp = ERR_PTR(-ENOMEM); int ret;
dprintk("--> %s\n", __func__); /*
/* Create a svc_sock for the service */ * Create an svc_sock for the back channel service that shares the
bc_xprt = svc_sock_create(serv, xprt->prot); * fore channel connection.
if (!bc_xprt) * 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; goto out;
}
/* /*
* Save the svc_serv in the transport so that it can * Save the svc_serv in the transport so that it can
* be referenced when the session backchannel is initialized * be referenced when the session backchannel is initialized
*/ */
serv->bc_xprt = bc_xprt;
xprt->bc_serv = serv; xprt->bc_serv = serv;
INIT_LIST_HEAD(&serv->sv_cb_list); INIT_LIST_HEAD(&serv->sv_cb_list);
spin_lock_init(&serv->sv_cb_lock); spin_lock_init(&serv->sv_cb_lock);
init_waitqueue_head(&serv->sv_cb_waitq); init_waitqueue_head(&serv->sv_cb_waitq);
rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
if (IS_ERR(rqstp)) if (IS_ERR(rqstp)) {
svc_sock_destroy(bc_xprt); svc_xprt_put(serv->sv_bc_xprt);
serv->sv_bc_xprt = NULL;
}
out: out:
dprintk("--> %s return %p\n", __func__, rqstp); dprintk("--> %s return %ld\n", __func__,
IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0);
return rqstp; return rqstp;
} }
...@@ -233,6 +266,10 @@ static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, ...@@ -233,6 +266,10 @@ static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
struct nfs_callback_data *cb_info) struct nfs_callback_data *cb_info)
{ {
} }
int nfs4_set_callback_sessionid(struct nfs_client *clp)
{
return 0;
}
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
/* /*
...@@ -328,6 +365,9 @@ static int check_gss_callback_principal(struct nfs_client *clp, ...@@ -328,6 +365,9 @@ static int check_gss_callback_principal(struct nfs_client *clp,
struct rpc_clnt *r = clp->cl_rpcclient; struct rpc_clnt *r = clp->cl_rpcclient;
char *p = svc_gss_principal(rqstp); 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 * It might just be a normal user principal, in which case
* userspace won't bother to tell us the name at all. * 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, ...@@ -345,6 +385,23 @@ static int check_gss_callback_principal(struct nfs_client *clp,
return SVC_OK; 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) static int nfs_callback_authenticate(struct svc_rqst *rqstp)
{ {
struct nfs_client *clp; struct nfs_client *clp;
...@@ -352,7 +409,7 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp) ...@@ -352,7 +409,7 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp)
int ret = SVC_OK; int ret = SVC_OK;
/* Don't talk to strangers */ /* Don't talk to strangers */
clp = nfs_find_client(svc_addr(rqstp), 4); clp = nfs_cb_find_client(rqstp);
if (clp == NULL) if (clp == NULL)
return SVC_DROP; return SVC_DROP;
......
...@@ -34,10 +34,17 @@ enum nfs4_callback_opnum { ...@@ -34,10 +34,17 @@ enum nfs4_callback_opnum {
OP_CB_ILLEGAL = 10044, 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 { struct cb_compound_hdr_arg {
unsigned int taglen; unsigned int taglen;
const char *tag; const char *tag;
unsigned int minorversion; unsigned int minorversion;
unsigned int cb_ident; /* v4.0 callback identifier */
unsigned nops; unsigned nops;
}; };
...@@ -103,14 +110,23 @@ struct cb_sequenceres { ...@@ -103,14 +110,23 @@ struct cb_sequenceres {
uint32_t csr_target_highestslotid; uint32_t csr_target_highestslotid;
}; };
extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, extern __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
struct cb_sequenceres *res); struct cb_sequenceres *res,
struct cb_process_state *cps);
extern int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, extern int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation,
const nfs4_stateid *stateid); const nfs4_stateid *stateid);
#define RCA4_TYPE_MASK_RDATA_DLG 0 #define RCA4_TYPE_MASK_RDATA_DLG 0
#define RCA4_TYPE_MASK_WDATA_DLG 1 #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 cb_recallanyargs {
struct sockaddr *craa_addr; struct sockaddr *craa_addr;
...@@ -118,25 +134,52 @@ struct cb_recallanyargs { ...@@ -118,25 +134,52 @@ struct cb_recallanyargs {
uint32_t craa_type_mask; 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 cb_recallslotargs {
struct sockaddr *crsa_addr; struct sockaddr *crsa_addr;
uint32_t crsa_target_max_slots; uint32_t crsa_target_max_slots;
}; };
extern unsigned nfs4_callback_recallslot(struct cb_recallslotargs *args, extern __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args,
void *dummy); 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 void nfs4_check_drain_bc_complete(struct nfs4_session *ses);
extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy); 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 #ifdef CONFIG_NFS_V4
extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt); extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt);
extern void nfs_callback_down(int minorversion); extern void nfs_callback_down(int minorversion);
extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation, extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation,
const nfs4_stateid *stateid); const nfs4_stateid *stateid);
extern int nfs4_set_callback_sessionid(struct nfs_client *clp);
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */
/* /*
* nfs41: Callbacks are expected to not cause substantial latency, * nfs41: Callbacks are expected to not cause substantial latency,
......
...@@ -12,30 +12,33 @@ ...@@ -12,30 +12,33 @@
#include "callback.h" #include "callback.h"
#include "delegation.h" #include "delegation.h"
#include "internal.h" #include "internal.h"
#include "pnfs.h"
#ifdef NFS_DEBUG #ifdef NFS_DEBUG
#define NFSDBG_FACILITY NFSDBG_CALLBACK #define NFSDBG_FACILITY NFSDBG_CALLBACK
#endif #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_delegation *delegation;
struct nfs_inode *nfsi; struct nfs_inode *nfsi;
struct inode *inode; 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->bitmap[0] = res->bitmap[1] = 0;
res->status = htonl(NFS4ERR_BADHANDLE); 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", 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) if (inode == NULL)
goto out_putclient; goto out;
nfsi = NFS_I(inode); nfsi = NFS_I(inode);
rcu_read_lock(); rcu_read_lock();
delegation = rcu_dereference(nfsi->delegation); delegation = rcu_dereference(nfsi->delegation);
...@@ -55,49 +58,41 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres * ...@@ -55,49 +58,41 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *
out_iput: out_iput:
rcu_read_unlock(); rcu_read_unlock();
iput(inode); iput(inode);
out_putclient:
nfs_put_client(clp);
out: out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status)); dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status));
return 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; struct inode *inode;
__be32 res; __be32 res;
res = htonl(NFS4ERR_BADHANDLE); res = htonl(NFS4ERR_OP_NOT_IN_SESSION);
clp = nfs_find_client(args->addr, 4); if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
if (clp == NULL)
goto out; goto out;
dprintk("NFS: RECALL callback request from %s\n", dprintk("NFS: RECALL callback request from %s\n",
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
do { res = htonl(NFS4ERR_BADHANDLE);
struct nfs_client *prev = clp; inode = nfs_delegation_find_inode(cps->clp, &args->fh);
if (inode == NULL)
inode = nfs_delegation_find_inode(clp, &args->fh); goto out;
if (inode != NULL) { /* Set up a helper thread to actually return the delegation */
/* Set up a helper thread to actually return the delegation */ switch (nfs_async_inode_return_delegation(inode, &args->stateid)) {
switch (nfs_async_inode_return_delegation(inode, &args->stateid)) { case 0:
case 0: res = 0;
res = 0; break;
break; case -ENOENT:
case -ENOENT: if (res != 0)
if (res != 0) res = htonl(NFS4ERR_BAD_STATEID);
res = htonl(NFS4ERR_BAD_STATEID); break;
break; default:
default: res = htonl(NFS4ERR_RESOURCE);
res = htonl(NFS4ERR_RESOURCE); }
} iput(inode);
iput(inode);
}
clp = nfs_find_client_next(prev);
nfs_put_client(prev);
} while (clp != NULL);
out: out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(res)); dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
return res; return res;
...@@ -113,6 +108,139 @@ int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation, const nf ...@@ -113,6 +108,139 @@ int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation, const nf
#if defined(CONFIG_NFS_V4_1) #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) int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, const nfs4_stateid *stateid)
{ {
if (delegation == NULL) if (delegation == NULL)
...@@ -184,42 +312,6 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args) ...@@ -184,42 +312,6 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args)
return htonl(NFS4ERR_SEQ_MISORDERED); 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 * 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 * 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, ...@@ -276,20 +368,34 @@ static bool referring_call_exists(struct nfs_client *clp,
} }
__be32 nfs4_callback_sequence(struct cb_sequenceargs *args, __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; struct nfs_client *clp;
int i; int i;
__be32 status; __be32 status;
cps->clp = NULL;
status = htonl(NFS4ERR_BADSESSION); 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) if (clp == NULL)
goto out; 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); status = validate_seqid(&clp->cl_session->bc_slot_table, args);
if (status) if (status)
goto out_putclient; goto out;
/* /*
* Check for pending referring calls. If a match is found, a * Check for pending referring calls. If a match is found, a
...@@ -298,7 +404,7 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args, ...@@ -298,7 +404,7 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
*/ */
if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) { if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) {
status = htonl(NFS4ERR_DELAY); status = htonl(NFS4ERR_DELAY);
goto out_putclient; goto out;
} }
memcpy(&res->csr_sessionid, &args->csa_sessionid, memcpy(&res->csr_sessionid, &args->csa_sessionid,
...@@ -307,83 +413,93 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args, ...@@ -307,83 +413,93 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
res->csr_slotid = args->csa_slotid; res->csr_slotid = args->csa_slotid;
res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
res->csr_target_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: out:
for (i = 0; i < args->csa_nrclists; i++) for (i = 0; i < args->csa_nrclists; i++)
kfree(args->csa_rclists[i].rcl_refcalls); kfree(args->csa_rclists[i].rcl_refcalls);
kfree(args->csa_rclists); kfree(args->csa_rclists);
if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) {
res->csr_status = 0; cps->drc_status = status;
else status = 0;
} else
res->csr_status = status; res->csr_status = status;
dprintk("%s: exit with status = %d res->csr_status %d\n", __func__, dprintk("%s: exit with status = %d res->csr_status %d\n", __func__,
ntohl(status), ntohl(res->csr_status)); ntohl(status), ntohl(res->csr_status));
return 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; __be32 status;
fmode_t flags = 0; fmode_t flags = 0;
status = htonl(NFS4ERR_OP_NOT_IN_SESSION); status = cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION);
clp = nfs_find_client(args->craa_addr, 4); if (!cps->clp) /* set in cb_sequence */
if (clp == NULL)
goto out; goto out;
dprintk("NFS: RECALL_ANY callback request from %s\n", 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 *) if (test_bit(RCA4_TYPE_MASK_RDATA_DLG, (const unsigned long *)
&args->craa_type_mask)) &args->craa_type_mask))
flags = FMODE_READ; flags = FMODE_READ;
if (test_bit(RCA4_TYPE_MASK_WDATA_DLG, (const unsigned long *) if (test_bit(RCA4_TYPE_MASK_WDATA_DLG, (const unsigned long *)
&args->craa_type_mask)) &args->craa_type_mask))
flags |= FMODE_WRITE; 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) if (flags)
nfs_expire_all_delegation_types(clp, flags); nfs_expire_all_delegation_types(cps->clp, flags);
status = htonl(NFS4_OK);
out: out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
return status; return status;
} }
/* Reduce the fore channel's max_slots to the target value */ /* 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; struct nfs4_slot_table *fc_tbl;
__be32 status; __be32 status;
status = htonl(NFS4ERR_OP_NOT_IN_SESSION); status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
clp = nfs_find_client(args->crsa_addr, 4); if (!cps->clp) /* set in cb_sequence */
if (clp == NULL)
goto out; goto out;
dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n", 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); 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); status = htonl(NFS4ERR_BAD_HIGH_SLOT);
if (args->crsa_target_max_slots > fc_tbl->max_slots || if (args->crsa_target_max_slots > fc_tbl->max_slots ||
args->crsa_target_max_slots < 1) args->crsa_target_max_slots < 1)
goto out_putclient; goto out;
status = htonl(NFS4_OK); status = htonl(NFS4_OK);
if (args->crsa_target_max_slots == fc_tbl->max_slots) 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; fc_tbl->target_max_slots = args->crsa_target_max_slots;
nfs41_handle_recall_slot(clp); nfs41_handle_recall_slot(cps->clp);
out_putclient:
nfs_put_client(clp); /* balance nfs_find_client */
out: out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
return status; return status;
......
...@@ -10,8 +10,10 @@ ...@@ -10,8 +10,10 @@
#include <linux/nfs4.h> #include <linux/nfs4.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sunrpc/bc_xprt.h>
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include "callback.h" #include "callback.h"
#include "internal.h"
#define CB_OP_TAGLEN_MAXSZ (512) #define CB_OP_TAGLEN_MAXSZ (512)
#define CB_OP_HDR_RES_MAXSZ (2 + CB_OP_TAGLEN_MAXSZ) #define CB_OP_HDR_RES_MAXSZ (2 + CB_OP_TAGLEN_MAXSZ)
...@@ -22,6 +24,7 @@ ...@@ -22,6 +24,7 @@
#define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) #define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#if defined(CONFIG_NFS_V4_1) #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 + \ #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \
4 + 1 + 3) 4 + 1 + 3)
#define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) #define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
...@@ -33,7 +36,8 @@ ...@@ -33,7 +36,8 @@
/* Internal error code */ /* Internal error code */
#define NFS4ERR_RESOURCE_HDR 11050 #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_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *);
typedef __be32 (*callback_encode_res_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 ...@@ -160,7 +164,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
hdr->minorversion = ntohl(*p++); hdr->minorversion = ntohl(*p++);
/* Check minor version is zero or one. */ /* Check minor version is zero or one. */
if (hdr->minorversion <= 1) { if (hdr->minorversion <= 1) {
p++; /* skip callback_ident */ hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */
} else { } else {
printk(KERN_WARNING "%s: NFSv4 server callback with " printk(KERN_WARNING "%s: NFSv4 server callback with "
"illegal minor version %u!\n", "illegal minor version %u!\n",
...@@ -220,6 +224,66 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, ...@@ -220,6 +224,66 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr,
#if defined(CONFIG_NFS_V4_1) #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, static __be32 decode_sessionid(struct xdr_stream *xdr,
struct nfs4_sessionid *sid) struct nfs4_sessionid *sid)
{ {
...@@ -574,10 +638,10 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) ...@@ -574,10 +638,10 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
case OP_CB_SEQUENCE: case OP_CB_SEQUENCE:
case OP_CB_RECALL_ANY: case OP_CB_RECALL_ANY:
case OP_CB_RECALL_SLOT: case OP_CB_RECALL_SLOT:
case OP_CB_LAYOUTRECALL:
*op = &callback_ops[op_nr]; *op = &callback_ops[op_nr];
break; break;
case OP_CB_LAYOUTRECALL:
case OP_CB_NOTIFY_DEVICEID: case OP_CB_NOTIFY_DEVICEID:
case OP_CB_NOTIFY: case OP_CB_NOTIFY:
case OP_CB_PUSH_DELEG: case OP_CB_PUSH_DELEG:
...@@ -593,6 +657,37 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) ...@@ -593,6 +657,37 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
return htonl(NFS_OK); 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 */ #else /* CONFIG_NFS_V4_1 */
static __be32 static __be32
...@@ -601,6 +696,9 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) ...@@ -601,6 +696,9 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
return htonl(NFS4ERR_MINOR_VERS_MISMATCH); return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
} }
static void nfs4_cb_free_slot(struct nfs_client *clp)
{
}
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
static __be32 static __be32
...@@ -621,7 +719,8 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op) ...@@ -621,7 +719,8 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
static __be32 process_op(uint32_t minorversion, int nop, static __be32 process_op(uint32_t minorversion, int nop,
struct svc_rqst *rqstp, struct svc_rqst *rqstp,
struct xdr_stream *xdr_in, void *argp, 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]; struct callback_op *op = &callback_ops[0];
unsigned int op_nr; unsigned int op_nr;
...@@ -644,8 +743,8 @@ static __be32 process_op(uint32_t minorversion, int nop, ...@@ -644,8 +743,8 @@ static __be32 process_op(uint32_t minorversion, int nop,
if (status) if (status)
goto encode_hdr; goto encode_hdr;
if (*drc_status) { if (cps->drc_status) {
status = *drc_status; status = cps->drc_status;
goto encode_hdr; goto encode_hdr;
} }
...@@ -653,16 +752,10 @@ static __be32 process_op(uint32_t minorversion, int nop, ...@@ -653,16 +752,10 @@ static __be32 process_op(uint32_t minorversion, int nop,
if (maxlen > 0 && maxlen < PAGE_SIZE) { if (maxlen > 0 && maxlen < PAGE_SIZE) {
status = op->decode_args(rqstp, xdr_in, argp); status = op->decode_args(rqstp, xdr_in, argp);
if (likely(status == 0)) if (likely(status == 0))
status = op->process_op(argp, resp); status = op->process_op(argp, resp, cps);
} else } else
status = htonl(NFS4ERR_RESOURCE); 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: encode_hdr:
res = encode_op_hdr(xdr_out, op_nr, status); res = encode_op_hdr(xdr_out, op_nr, status);
if (unlikely(res)) if (unlikely(res))
...@@ -681,8 +774,11 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r ...@@ -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_arg hdr_arg = { 0 };
struct cb_compound_hdr_res hdr_res = { NULL }; struct cb_compound_hdr_res hdr_res = { NULL };
struct xdr_stream xdr_in, xdr_out; struct xdr_stream xdr_in, xdr_out;
__be32 *p; __be32 *p, status;
__be32 status, drc_status = 0; struct cb_process_state cps = {
.drc_status = 0,
.clp = NULL,
};
unsigned int nops = 0; unsigned int nops = 0;
dprintk("%s: start\n", __func__); dprintk("%s: start\n", __func__);
...@@ -696,6 +792,13 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r ...@@ -696,6 +792,13 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
if (status == __constant_htonl(NFS4ERR_RESOURCE)) if (status == __constant_htonl(NFS4ERR_RESOURCE))
return rpc_garbage_args; 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.taglen = hdr_arg.taglen;
hdr_res.tag = hdr_arg.tag; hdr_res.tag = hdr_arg.tag;
if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0) 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 ...@@ -703,7 +806,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
while (status == 0 && nops != hdr_arg.nops) { while (status == 0 && nops != hdr_arg.nops) {
status = process_op(hdr_arg.minorversion, nops, rqstp, status = process_op(hdr_arg.minorversion, nops, rqstp,
&xdr_in, argp, &xdr_out, resp, &drc_status); &xdr_in, argp, &xdr_out, resp, &cps);
nops++; nops++;
} }
...@@ -716,6 +819,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r ...@@ -716,6 +819,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
*hdr_res.status = status; *hdr_res.status = status;
*hdr_res.nops = htonl(nops); *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)); dprintk("%s: done, status = %u\n", __func__, ntohl(status));
return rpc_success; return rpc_success;
} }
...@@ -739,6 +844,12 @@ static struct callback_op callback_ops[] = { ...@@ -739,6 +844,12 @@ static struct callback_op callback_ops[] = {
.res_maxsize = CB_OP_RECALL_RES_MAXSZ, .res_maxsize = CB_OP_RECALL_RES_MAXSZ,
}, },
#if defined(CONFIG_NFS_V4_1) #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] = { [OP_CB_SEQUENCE] = {
.process_op = (callback_process_op_t)nfs4_callback_sequence, .process_op = (callback_process_op_t)nfs4_callback_sequence,
.decode_args = (callback_decode_arg_t)decode_cb_sequence_args, .decode_args = (callback_decode_arg_t)decode_cb_sequence_args,
......
...@@ -56,6 +56,30 @@ static DEFINE_SPINLOCK(nfs_client_lock); ...@@ -56,6 +56,30 @@ static DEFINE_SPINLOCK(nfs_client_lock);
static LIST_HEAD(nfs_client_list); static LIST_HEAD(nfs_client_list);
static LIST_HEAD(nfs_volume_list); static LIST_HEAD(nfs_volume_list);
static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); 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 * RPC cruft for NFS
...@@ -144,7 +168,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ ...@@ -144,7 +168,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
clp->cl_proto = cl_init->proto; clp->cl_proto = cl_init->proto;
#ifdef CONFIG_NFS_V4 #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); spin_lock_init(&clp->cl_lock);
INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); 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_ ...@@ -170,21 +197,17 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
} }
#ifdef CONFIG_NFS_V4 #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 #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); 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 * Destroy the NFS4 callback service
...@@ -199,17 +222,49 @@ static void nfs4_shutdown_client(struct nfs_client *clp) ...@@ -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)) if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
nfs4_kill_renewd(clp); nfs4_kill_renewd(clp);
nfs4_clear_client_minor_version(clp); nfs4_shutdown_session(clp);
nfs4_destroy_callback(clp); nfs4_destroy_callback(clp);
if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
nfs_idmap_delete(clp); nfs_idmap_delete(clp);
rpc_destroy_wait_queue(&clp->cl_rpcwaitq); 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 #else
static void nfs4_shutdown_client(struct nfs_client *clp) 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 */ #endif /* CONFIG_NFS_V4 */
/* /*
...@@ -248,6 +303,7 @@ void nfs_put_client(struct nfs_client *clp) ...@@ -248,6 +303,7 @@ void nfs_put_client(struct nfs_client *clp)
if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) { if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) {
list_del(&clp->cl_share_link); list_del(&clp->cl_share_link);
nfs_cb_idr_remove_locked(clp);
spin_unlock(&nfs_client_lock); spin_unlock(&nfs_client_lock);
BUG_ON(!list_empty(&clp->cl_superblocks)); BUG_ON(!list_empty(&clp->cl_superblocks));
...@@ -363,70 +419,28 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1, ...@@ -363,70 +419,28 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1,
return 0; return 0;
} }
/* /* Common match routine for v4.0 and v4.1 callback services */
* Find a client by IP address and protocol version bool
* - returns NULL if no such client nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp,
*/ u32 minorversion)
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)
{ {
struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr; struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
u32 nfsvers = clp->rpc_ops->version;
spin_lock(&nfs_client_lock); /* Don't match clients that failed to initialise */
list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) { if (!(clp->cl_cons_state == NFS_CS_READY ||
struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; clp->cl_cons_state == NFS_CS_SESSION_INITING))
return false;
/* Don't match clients that failed to initialise properly */ /* Match the version and minorversion */
if (clp->cl_cons_state != NFS_CS_READY) if (clp->rpc_ops->version != 4 ||
continue; clp->cl_minorversion != minorversion)
return false;
/* Different NFS versions cannot share the same nfs_client */ /* Match only the IP address, not the port number */
if (clp->rpc_ops->version != nfsvers) if (!nfs_sockaddr_match_ipaddr(addr, clap))
continue; return false;
/* Match only the IP address, not the port number */ return true;
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;
} }
/* /*
...@@ -988,6 +1002,27 @@ static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_serve ...@@ -988,6 +1002,27 @@ static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_serve
target->options = source->options; 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 * Allocate and initialise a server record
*/ */
...@@ -1004,6 +1039,7 @@ static struct nfs_server *nfs_alloc_server(void) ...@@ -1004,6 +1039,7 @@ static struct nfs_server *nfs_alloc_server(void)
/* Zero out the NFS state stuff */ /* Zero out the NFS state stuff */
INIT_LIST_HEAD(&server->client_link); INIT_LIST_HEAD(&server->client_link);
INIT_LIST_HEAD(&server->master_link); INIT_LIST_HEAD(&server->master_link);
INIT_LIST_HEAD(&server->delegations);
atomic_set(&server->active, 0); atomic_set(&server->active, 0);
...@@ -1019,6 +1055,8 @@ static struct nfs_server *nfs_alloc_server(void) ...@@ -1019,6 +1055,8 @@ static struct nfs_server *nfs_alloc_server(void)
return NULL; return NULL;
} }
pnfs_init_server(server);
return server; return server;
} }
...@@ -1029,11 +1067,8 @@ void nfs_free_server(struct nfs_server *server) ...@@ -1029,11 +1067,8 @@ void nfs_free_server(struct nfs_server *server)
{ {
dprintk("--> nfs_free_server()\n"); dprintk("--> nfs_free_server()\n");
nfs_server_remove_lists(server);
unset_pnfs_layoutdriver(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) if (server->destroy != NULL)
server->destroy(server); server->destroy(server);
...@@ -1108,11 +1143,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, ...@@ -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.major,
(unsigned long long) server->fsid.minor); (unsigned long long) server->fsid.minor);
spin_lock(&nfs_client_lock); nfs_server_insert_lists(server);
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);
server->mount_time = jiffies; server->mount_time = jiffies;
nfs_free_fattr(fattr); nfs_free_fattr(fattr);
return server; return server;
...@@ -1124,6 +1155,101 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, ...@@ -1124,6 +1155,101 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
} }
#ifdef CONFIG_NFS_V4 #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 * Initialize the NFS4 callback service
*/ */
...@@ -1342,11 +1468,7 @@ static int nfs4_server_common_setup(struct nfs_server *server, ...@@ -1342,11 +1468,7 @@ static int nfs4_server_common_setup(struct nfs_server *server,
if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
server->namelen = NFS4_MAXNAMLEN; server->namelen = NFS4_MAXNAMLEN;
spin_lock(&nfs_client_lock); nfs_server_insert_lists(server);
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);
server->mount_time = jiffies; server->mount_time = jiffies;
out: out:
nfs_free_fattr(fattr); nfs_free_fattr(fattr);
...@@ -1551,11 +1673,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source, ...@@ -1551,11 +1673,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
if (error < 0) if (error < 0)
goto out_free_server; goto out_free_server;
spin_lock(&nfs_client_lock); nfs_server_insert_lists(server);
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);
server->mount_time = jiffies; server->mount_time = jiffies;
nfs_free_fattr(fattr_fsinfo); nfs_free_fattr(fattr_fsinfo);
......
...@@ -40,11 +40,23 @@ static void nfs_free_delegation(struct nfs_delegation *delegation) ...@@ -40,11 +40,23 @@ static void nfs_free_delegation(struct nfs_delegation *delegation)
call_rcu(&delegation->rcu, nfs_free_delegation_callback); 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) void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
{ {
set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); 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) int nfs_have_delegation(struct inode *inode, fmode_t flags)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
...@@ -119,10 +131,15 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s ...@@ -119,10 +131,15 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s
return 0; 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 nfs_delegation *delegation;
struct rpc_cred *oldcred = NULL; struct rpc_cred *oldcred = NULL;
...@@ -175,38 +192,52 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation ...@@ -175,38 +192,52 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation
return inode; return inode;
} }
static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, static struct nfs_delegation *
const nfs4_stateid *stateid, nfs_detach_delegation_locked(struct nfs_inode *nfsi,
struct nfs_client *clp) struct nfs_server *server)
{ {
struct nfs_delegation *delegation = struct nfs_delegation *delegation =
rcu_dereference_protected(nfsi->delegation, rcu_dereference_protected(nfsi->delegation,
lockdep_is_held(&clp->cl_lock)); lockdep_is_held(&server->nfs_client->cl_lock));
if (delegation == NULL) if (delegation == NULL)
goto nomatch; goto nomatch;
spin_lock(&delegation->lock); 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); list_del_rcu(&delegation->super_list);
delegation->inode = NULL; delegation->inode = NULL;
nfsi->delegation_state = 0; nfsi->delegation_state = 0;
rcu_assign_pointer(nfsi->delegation, NULL); rcu_assign_pointer(nfsi->delegation, NULL);
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
return delegation; return delegation;
nomatch_unlock:
spin_unlock(&delegation->lock);
nomatch: nomatch:
return NULL; return NULL;
} }
/* static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
* Set up a delegation on an inode 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) 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_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation, *old_delegation; struct nfs_delegation *delegation, *old_delegation;
struct nfs_delegation *freeme = NULL; struct nfs_delegation *freeme = NULL;
...@@ -227,7 +258,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct ...@@ -227,7 +258,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
old_delegation = rcu_dereference_protected(nfsi->delegation, old_delegation = rcu_dereference_protected(nfsi->delegation,
lockdep_is_held(&clp->cl_lock)); lockdep_is_held(&clp->cl_lock));
if (old_delegation != NULL) { if (old_delegation != NULL) {
if (memcmp(&delegation->stateid, &old_delegation->stateid, if (memcmp(&delegation->stateid, &old_delegation->stateid,
sizeof(old_delegation->stateid)) == 0 && sizeof(old_delegation->stateid)) == 0 &&
...@@ -246,9 +277,9 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct ...@@ -246,9 +277,9 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
delegation = NULL; delegation = NULL;
goto out; 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; nfsi->delegation_state = delegation->type;
rcu_assign_pointer(nfsi->delegation, delegation); rcu_assign_pointer(nfsi->delegation, delegation);
delegation = NULL; delegation = NULL;
...@@ -290,73 +321,85 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat ...@@ -290,73 +321,85 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat
return err; 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) int nfs_client_return_marked_delegations(struct nfs_client *clp)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct nfs_server *server;
struct inode *inode; struct inode *inode;
int err = 0; int err = 0;
restart: restart:
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) list_for_each_entry_rcu(delegation, &server->delegations,
continue; super_list) {
inode = nfs_delegation_grab_inode(delegation); if (!test_and_clear_bit(NFS_DELEGATION_RETURN,
if (inode == NULL) &delegation->flags))
continue; continue;
spin_lock(&clp->cl_lock); inode = nfs_delegation_grab_inode(delegation);
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); if (inode == NULL)
spin_unlock(&clp->cl_lock); continue;
rcu_read_unlock(); delegation = nfs_detach_delegation(NFS_I(inode),
if (delegation != NULL) { server);
filemap_flush(inode->i_mapping); rcu_read_unlock();
err = __nfs_inode_return_delegation(inode, delegation, 0);
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(); rcu_read_unlock();
return 0; return 0;
} }
/* /**
* This function returns the delegation without reclaiming opens * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens
* or protecting against delegation reclaims. * @inode: inode to process
* It is therefore really only safe to be called from *
* nfs4_clear_inode() * 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) 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_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
if (rcu_access_pointer(nfsi->delegation) != NULL) { if (rcu_access_pointer(nfsi->delegation) != NULL) {
spin_lock(&clp->cl_lock); delegation = nfs_detach_delegation(nfsi, server);
delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);
spin_unlock(&clp->cl_lock);
if (delegation != NULL) if (delegation != NULL)
nfs_do_return_delegation(inode, delegation, 0); 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) 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_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
int err = 0; int err = 0;
if (rcu_access_pointer(nfsi->delegation) != NULL) { if (rcu_access_pointer(nfsi->delegation) != NULL) {
spin_lock(&clp->cl_lock); delegation = nfs_detach_delegation(nfsi, server);
delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);
spin_unlock(&clp->cl_lock);
if (delegation != NULL) { if (delegation != NULL) {
nfs_wb_all(inode); nfs_wb_all(inode);
err = __nfs_inode_return_delegation(inode, delegation, 1); err = __nfs_inode_return_delegation(inode, delegation, 1);
...@@ -365,46 +408,61 @@ int nfs_inode_return_delegation(struct inode *inode) ...@@ -365,46 +408,61 @@ int nfs_inode_return_delegation(struct inode *inode)
return err; 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(NFS_DELEGATION_RETURN, &delegation->flags);
set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 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) 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; struct nfs_delegation *delegation;
if (clp == NULL) if (clp == NULL)
return; return;
rcu_read_lock(); 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); 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); spin_unlock(&delegation->lock);
} }
rcu_read_unlock(); rcu_read_unlock();
if (nfs_client_return_marked_delegations(clp) != 0) if (nfs_client_return_marked_delegations(clp) != 0)
nfs4_schedule_state_manager(clp); nfs4_schedule_state_manager(clp);
} }
static static void nfs_mark_return_all_delegation_types(struct nfs_server *server,
void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, fmode_t flags) fmode_t flags)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
rcu_read_lock(); list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
continue; continue;
if (delegation->type & flags) 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(); rcu_read_unlock();
} }
...@@ -419,19 +477,32 @@ static void nfs_delegation_run_state_manager(struct nfs_client *clp) ...@@ -419,19 +477,32 @@ static void nfs_delegation_run_state_manager(struct nfs_client *clp)
nfs4_schedule_state_manager(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) void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags)
{ {
nfs_client_mark_return_all_delegation_types(clp, flags); nfs_client_mark_return_all_delegation_types(clp, flags);
nfs_delegation_run_state_manager(clp); nfs_delegation_run_state_manager(clp);
} }
/**
* nfs_expire_all_delegations
* @clp: client to process
*
*/
void nfs_expire_all_delegations(struct nfs_client *clp) void nfs_expire_all_delegations(struct nfs_client *clp)
{ {
nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); 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) void nfs_handle_cb_pathdown(struct nfs_client *clp)
{ {
...@@ -440,29 +511,43 @@ 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); 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; struct nfs_delegation *delegation;
rcu_read_lock(); list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
continue; 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) 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); 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_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
...@@ -474,22 +559,21 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s ...@@ -474,22 +559,21 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s
rcu_read_unlock(); rcu_read_unlock();
return -ENOENT; return -ENOENT;
} }
nfs_mark_return_delegation(delegation);
nfs_mark_return_delegation(clp, delegation);
rcu_read_unlock(); rcu_read_unlock();
nfs_delegation_run_state_manager(clp); nfs_delegation_run_state_manager(clp);
return 0; return 0;
} }
/* static struct inode *
* Retrieve the inode associated with a delegation nfs_delegation_find_inode_server(struct nfs_server *server,
*/ const struct nfs_fh *fhandle)
struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct inode *res = NULL; 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); spin_lock(&delegation->lock);
if (delegation->inode != NULL && if (delegation->inode != NULL &&
nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 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 ...@@ -499,49 +583,121 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs
if (res != NULL) if (res != NULL)
break; 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(); rcu_read_unlock();
return res; return res;
} }
/* static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
* Mark all delegations as needing to be reclaimed {
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) void nfs_delegation_mark_reclaim(struct nfs_client *clp)
{ {
struct nfs_delegation *delegation; struct nfs_server *server;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); nfs_delegation_mark_reclaim_server(server);
rcu_read_unlock(); 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) void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct nfs_server *server;
struct inode *inode; struct inode *inode;
restart: restart:
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0) list_for_each_entry_rcu(delegation, &server->delegations,
continue; super_list) {
inode = nfs_delegation_grab_inode(delegation); if (test_bit(NFS_DELEGATION_NEED_RECLAIM,
if (inode == NULL) &delegation->flags) == 0)
continue; continue;
spin_lock(&clp->cl_lock); inode = nfs_delegation_grab_inode(delegation);
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); if (inode == NULL)
spin_unlock(&clp->cl_lock); continue;
rcu_read_unlock(); delegation = nfs_detach_delegation(NFS_I(inode),
if (delegation != NULL) server);
nfs_free_delegation(delegation); rcu_read_unlock();
iput(inode);
goto restart; if (delegation != NULL)
nfs_free_delegation(delegation);
iput(inode);
goto restart;
}
} }
rcu_read_unlock(); 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) int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
{ {
struct nfs_inode *nfsi = NFS_I(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); ...@@ -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_expire_unreferenced_delegations(struct nfs_client *clp);
void nfs_handle_cb_pathdown(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_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_mark_reclaim(struct nfs_client *clp);
void nfs_delegation_reap_unclaimed(struct nfs_client *clp); void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
......
...@@ -33,8 +33,8 @@ ...@@ -33,8 +33,8 @@
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/vmalloc.h>
#include <linux/kmemleak.h> #include <linux/kmemleak.h>
#include <linux/xattr.h>
#include "delegation.h" #include "delegation.h"
#include "iostat.h" #include "iostat.h"
...@@ -125,9 +125,10 @@ const struct inode_operations nfs4_dir_inode_operations = { ...@@ -125,9 +125,10 @@ const struct inode_operations nfs4_dir_inode_operations = {
.permission = nfs_permission, .permission = nfs_permission,
.getattr = nfs_getattr, .getattr = nfs_getattr,
.setattr = nfs_setattr, .setattr = nfs_setattr,
.getxattr = nfs4_getxattr, .getxattr = generic_getxattr,
.setxattr = nfs4_setxattr, .setxattr = generic_setxattr,
.listxattr = nfs4_listxattr, .listxattr = generic_listxattr,
.removexattr = generic_removexattr,
}; };
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */
...@@ -172,7 +173,7 @@ struct nfs_cache_array { ...@@ -172,7 +173,7 @@ struct nfs_cache_array {
struct nfs_cache_array_entry array[0]; 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 { typedef struct {
struct file *file; struct file *file;
struct page *page; struct page *page;
...@@ -378,14 +379,14 @@ int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc, ...@@ -378,14 +379,14 @@ int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc,
return error; return error;
} }
/* Fill in an entry based on the xdr code stored in desc->page */ static int xdr_decode(nfs_readdir_descriptor_t *desc,
static struct nfs_entry *entry, struct xdr_stream *xdr)
int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, struct xdr_stream *stream)
{ {
__be32 *p = desc->decode(stream, entry, NFS_SERVER(desc->file->f_path.dentry->d_inode), desc->plus); int error;
if (IS_ERR(p))
return PTR_ERR(p);
error = desc->decode(xdr, entry, desc->plus);
if (error)
return error;
entry->fattr->time_start = desc->timestamp; entry->fattr->time_start = desc->timestamp;
entry->fattr->gencount = desc->gencount; entry->fattr->gencount = desc->gencount;
return 0; return 0;
...@@ -459,25 +460,26 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) ...@@ -459,25 +460,26 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
/* Perform conversion from xdr to cache array */ /* Perform conversion from xdr to cache array */
static static
int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, 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_stream stream;
struct xdr_buf buf; struct xdr_buf buf = {
__be32 *ptr = xdr_page; .pages = xdr_pages,
.page_len = buflen,
.buflen = buflen,
.len = buflen,
};
struct page *scratch;
struct nfs_cache_array *array; struct nfs_cache_array *array;
unsigned int count = 0; unsigned int count = 0;
int status; int status;
buf.head->iov_base = xdr_page; scratch = alloc_page(GFP_KERNEL);
buf.head->iov_len = buflen; if (scratch == NULL)
buf.tail->iov_len = 0; return -ENOMEM;
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);
xdr_init_decode(&stream, &buf, NULL);
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
do { do {
status = xdr_decode(desc, entry, &stream); status = xdr_decode(desc, entry, &stream);
...@@ -506,6 +508,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en ...@@ -506,6 +508,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
} else } else
status = PTR_ERR(array); status = PTR_ERR(array);
} }
put_page(scratch);
return status; return status;
} }
...@@ -521,7 +525,6 @@ static ...@@ -521,7 +525,6 @@ static
void nfs_readdir_free_large_page(void *ptr, struct page **pages, void nfs_readdir_free_large_page(void *ptr, struct page **pages,
unsigned int npages) unsigned int npages)
{ {
vm_unmap_ram(ptr, npages);
nfs_readdir_free_pagearray(pages, npages); nfs_readdir_free_pagearray(pages, npages);
} }
...@@ -530,9 +533,8 @@ void nfs_readdir_free_large_page(void *ptr, struct page **pages, ...@@ -530,9 +533,8 @@ void nfs_readdir_free_large_page(void *ptr, struct page **pages,
* to nfs_readdir_free_large_page * to nfs_readdir_free_large_page
*/ */
static 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; unsigned int i;
for (i = 0; i < npages; i++) { for (i = 0; i < npages; i++) {
...@@ -541,13 +543,11 @@ void *nfs_readdir_large_page(struct page **pages, unsigned int npages) ...@@ -541,13 +543,11 @@ void *nfs_readdir_large_page(struct page **pages, unsigned int npages)
goto out_freepages; goto out_freepages;
pages[i] = page; pages[i] = page;
} }
return 0;
ptr = vm_map_ram(pages, npages, 0, PAGE_KERNEL);
if (!IS_ERR_OR_NULL(ptr))
return ptr;
out_freepages: out_freepages:
nfs_readdir_free_pagearray(pages, i); nfs_readdir_free_pagearray(pages, i);
return NULL; return -ENOMEM;
} }
static static
...@@ -566,6 +566,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, ...@@ -566,6 +566,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
entry.eof = 0; entry.eof = 0;
entry.fh = nfs_alloc_fhandle(); entry.fh = nfs_alloc_fhandle();
entry.fattr = nfs_alloc_fattr(); entry.fattr = nfs_alloc_fattr();
entry.server = NFS_SERVER(inode);
if (entry.fh == NULL || entry.fattr == NULL) if (entry.fh == NULL || entry.fattr == NULL)
goto out; goto out;
...@@ -577,8 +578,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, ...@@ -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)); memset(array, 0, sizeof(struct nfs_cache_array));
array->eof_index = -1; array->eof_index = -1;
pages_ptr = nfs_readdir_large_page(pages, array_size); status = nfs_readdir_large_page(pages, array_size);
if (!pages_ptr) if (status < 0)
goto out_release_array; goto out_release_array;
do { do {
unsigned int pglen; unsigned int pglen;
...@@ -587,7 +588,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, ...@@ -587,7 +588,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
if (status < 0) if (status < 0)
break; break;
pglen = status; 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 < 0) {
if (status == -ENOSPC) if (status == -ENOSPC)
status = 0; status = 0;
...@@ -1221,7 +1222,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru ...@@ -1221,7 +1222,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
goto out_unblock_sillyrename; goto out_unblock_sillyrename;
} }
inode = nfs_fhget(dentry->d_sb, fhandle, fattr); inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
res = (struct dentry *)inode; res = ERR_CAST(inode);
if (IS_ERR(res)) if (IS_ERR(res))
goto out_unblock_sillyrename; goto out_unblock_sillyrename;
...@@ -1355,8 +1356,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry ...@@ -1355,8 +1356,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
if (nd->flags & LOOKUP_CREATE) { if (nd->flags & LOOKUP_CREATE) {
attr.ia_mode = nd->intent.open.create_mode; attr.ia_mode = nd->intent.open.create_mode;
attr.ia_valid = ATTR_MODE; attr.ia_valid = ATTR_MODE;
if (!IS_POSIXACL(dir)) attr.ia_mode &= ~current_umask();
attr.ia_mode &= ~current_umask();
} else { } else {
open_flags &= ~(O_EXCL | O_CREAT); open_flags &= ~(O_EXCL | O_CREAT);
attr.ia_valid = 0; 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 ...@@ -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); 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/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
......
...@@ -1410,9 +1410,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1410,9 +1410,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
*/ */
void nfs4_evict_inode(struct inode *inode) void nfs4_evict_inode(struct inode *inode)
{ {
pnfs_destroy_layout(NFS_I(inode));
truncate_inode_pages(&inode->i_data, 0); truncate_inode_pages(&inode->i_data, 0);
end_writeback(inode); end_writeback(inode);
pnfs_destroy_layout(NFS_I(inode));
/* If we are holding a delegation, return it! */ /* If we are holding a delegation, return it! */
nfs_inode_return_delegation_noreclaim(inode); nfs_inode_return_delegation_noreclaim(inode);
/* First call standard NFS clear_inode() code */ /* First call standard NFS clear_inode() code */
...@@ -1619,6 +1619,7 @@ static void __exit exit_nfs_fs(void) ...@@ -1619,6 +1619,7 @@ static void __exit exit_nfs_fs(void)
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
rpc_proc_unregister("nfs"); rpc_proc_unregister("nfs");
#endif #endif
nfs_cleanup_cb_ident_idr();
unregister_nfs_fs(); unregister_nfs_fs();
nfs_fs_proc_exit(); nfs_fs_proc_exit();
nfsiod_stop(); nfsiod_stop();
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册