2005-04-17 02:20:36 +04:00
/*
* Copyright ( c ) 2001 The Regents of the University of Michigan .
* All rights reserved .
*
* Kendrick Smith < kmsmith @ umich . edu >
* Andy Adamson < andros @ umich . edu >
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR
* BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include <linux/sunrpc/clnt.h>
2009-12-03 21:30:56 +03:00
# include "nfsd.h"
# include "state.h"
2005-04-17 02:20:36 +04:00
# define NFSDDBG_FACILITY NFSDDBG_PROC
# define NFSPROC4_CB_NULL 0
# define NFSPROC4_CB_COMPOUND 1
2009-09-10 13:25:59 +04:00
# define NFS4_STATEID_SIZE 16
2005-04-17 02:20:36 +04:00
/* Index of predefined Linux callback client operations */
enum {
2009-09-10 13:25:46 +04:00
NFSPROC4_CLNT_CB_NULL = 0 ,
2005-04-17 02:20:36 +04:00
NFSPROC4_CLNT_CB_RECALL ,
2009-09-10 13:25:59 +04:00
NFSPROC4_CLNT_CB_SEQUENCE ,
2005-04-17 02:20:36 +04:00
} ;
enum nfs_cb_opnum4 {
OP_CB_RECALL = 4 ,
2009-09-10 13:25:59 +04:00
OP_CB_SEQUENCE = 11 ,
2005-04-17 02:20:36 +04:00
} ;
# define NFS4_MAXTAGLEN 20
# define NFS4_enc_cb_null_sz 0
# define NFS4_dec_cb_null_sz 0
# define cb_compound_enc_hdr_sz 4
# define cb_compound_dec_hdr_sz (3 + (NFS4_MAXTAGLEN >> 2))
2009-09-10 13:25:59 +04:00
# define sessionid_sz (NFS4_MAX_SESSIONID_LEN >> 2)
# define cb_sequence_enc_sz (sessionid_sz + 4 + \
1 /* no referring calls list yet */ )
# define cb_sequence_dec_sz (op_dec_sz + sessionid_sz + 4)
2005-04-17 02:20:36 +04:00
# define op_enc_sz 1
# define op_dec_sz 2
# define enc_nfs4_fh_sz (1 + (NFS4_FHSIZE >> 2))
2007-07-17 15:04:37 +04:00
# define enc_stateid_sz (NFS4_STATEID_SIZE >> 2)
2005-04-17 02:20:36 +04:00
# define NFS4_enc_cb_recall_sz (cb_compound_enc_hdr_sz + \
2009-09-10 13:25:59 +04:00
cb_sequence_enc_sz + \
2005-04-17 02:20:36 +04:00
1 + enc_stateid_sz + \
enc_nfs4_fh_sz )
# define NFS4_dec_cb_recall_sz (cb_compound_dec_hdr_sz + \
2009-09-10 13:25:59 +04:00
cb_sequence_dec_sz + \
2005-04-17 02:20:36 +04:00
op_dec_sz )
2009-09-10 13:26:12 +04:00
struct nfs4_rpc_args {
void * args_op ;
struct nfsd4_cb_sequence args_seq ;
} ;
2005-04-17 02:20:36 +04:00
/*
* Generic encode routines from fs / nfs / nfs4xdr . c
*/
2006-10-20 10:29:01 +04:00
static inline __be32 *
xdr_writemem ( __be32 * p , const void * ptr , int nbytes )
2005-04-17 02:20:36 +04:00
{
int tmp = XDR_QUADLEN ( nbytes ) ;
if ( ! tmp )
return p ;
p [ tmp - 1 ] = 0 ;
memcpy ( p , ptr , nbytes ) ;
return p + tmp ;
}
# define WRITE32(n) *p++ = htonl(n)
# define WRITEMEM(ptr,nbytes) do { \
p = xdr_writemem ( p , ptr , nbytes ) ; \
} while ( 0 )
# define RESERVE_SPACE(nbytes) do { \
p = xdr_reserve_space ( xdr , nbytes ) ; \
2008-04-30 11:55:09 +04:00
if ( ! p ) dprintk ( " NFSD: RESERVE_SPACE(%d) failed in function %s \n " , ( int ) ( nbytes ) , __func__ ) ; \
2005-04-17 02:20:36 +04:00
BUG_ON ( ! p ) ; \
} while ( 0 )
/*
* Generic decode routines from fs / nfs / nfs4xdr . c
*/
# define DECODE_TAIL \
status = 0 ; \
out : \
return status ; \
xdr_error : \
dprintk ( " NFSD: xdr error! (%s:%d) \n " , __FILE__ , __LINE__ ) ; \
status = - EIO ; \
goto out
# define READ32(x) (x) = ntohl(*p++)
# define READ64(x) do { \
( x ) = ( u64 ) ntohl ( * p + + ) < < 32 ; \
( x ) | = ntohl ( * p + + ) ; \
} while ( 0 )
# define READTIME(x) do { \
p + + ; \
( x . tv_sec ) = ntohl ( * p + + ) ; \
( x . tv_nsec ) = ntohl ( * p + + ) ; \
} while ( 0 )
# define READ_BUF(nbytes) do { \
p = xdr_inline_decode ( xdr , nbytes ) ; \
if ( ! p ) { \
2006-10-02 13:17:41 +04:00
dprintk ( " NFSD: %s: reply buffer overflowed in line %d. \n " , \
2008-04-30 11:55:09 +04:00
__func__ , __LINE__ ) ; \
2005-04-17 02:20:36 +04:00
return - EIO ; \
} \
} while ( 0 )
struct nfs4_cb_compound_hdr {
2009-09-10 13:25:59 +04:00
/* args */
u32 ident ; /* minorversion 0 only */
2005-04-17 02:20:36 +04:00
u32 nops ;
2009-06-16 05:20:50 +04:00
__be32 * nops_p ;
2009-06-16 05:20:53 +04:00
u32 minorversion ;
2009-09-10 13:25:59 +04:00
/* res */
int status ;
2005-04-17 02:20:36 +04:00
u32 taglen ;
2009-06-16 05:20:50 +04:00
char * tag ;
2005-04-17 02:20:36 +04:00
} ;
static struct {
int stat ;
int errno ;
} nfs_cb_errtbl [ ] = {
{ NFS4_OK , 0 } ,
{ NFS4ERR_PERM , EPERM } ,
{ NFS4ERR_NOENT , ENOENT } ,
{ NFS4ERR_IO , EIO } ,
{ NFS4ERR_NXIO , ENXIO } ,
{ NFS4ERR_ACCESS , EACCES } ,
{ NFS4ERR_EXIST , EEXIST } ,
{ NFS4ERR_XDEV , EXDEV } ,
{ NFS4ERR_NOTDIR , ENOTDIR } ,
{ NFS4ERR_ISDIR , EISDIR } ,
{ NFS4ERR_INVAL , EINVAL } ,
{ NFS4ERR_FBIG , EFBIG } ,
{ NFS4ERR_NOSPC , ENOSPC } ,
{ NFS4ERR_ROFS , EROFS } ,
{ NFS4ERR_MLINK , EMLINK } ,
{ NFS4ERR_NAMETOOLONG , ENAMETOOLONG } ,
{ NFS4ERR_NOTEMPTY , ENOTEMPTY } ,
{ NFS4ERR_DQUOT , EDQUOT } ,
{ NFS4ERR_STALE , ESTALE } ,
{ NFS4ERR_BADHANDLE , EBADHANDLE } ,
{ NFS4ERR_BAD_COOKIE , EBADCOOKIE } ,
{ NFS4ERR_NOTSUPP , ENOTSUPP } ,
{ NFS4ERR_TOOSMALL , ETOOSMALL } ,
{ NFS4ERR_SERVERFAULT , ESERVERFAULT } ,
{ NFS4ERR_BADTYPE , EBADTYPE } ,
{ NFS4ERR_LOCKED , EAGAIN } ,
{ NFS4ERR_RESOURCE , EREMOTEIO } ,
{ NFS4ERR_SYMLINK , ELOOP } ,
{ NFS4ERR_OP_ILLEGAL , EOPNOTSUPP } ,
{ NFS4ERR_DEADLOCK , EDEADLK } ,
{ - 1 , EIO }
} ;
static int
nfs_cb_stat_to_errno ( int stat )
{
int i ;
for ( i = 0 ; nfs_cb_errtbl [ i ] . stat ! = - 1 ; i + + ) {
if ( nfs_cb_errtbl [ i ] . stat = = stat )
return nfs_cb_errtbl [ i ] . errno ;
}
/* If we cannot translate the error, the recovery routines should
* handle it .
* Note : remaining NFSv4 error codes have values > 10000 , so should
* not conflict with native Linux error codes .
*/
return stat ;
}
/*
* XDR encode
*/
2009-06-16 05:20:50 +04:00
static void
2005-04-17 02:20:36 +04:00
encode_cb_compound_hdr ( struct xdr_stream * xdr , struct nfs4_cb_compound_hdr * hdr )
{
2006-10-20 10:29:01 +04:00
__be32 * p ;
2005-04-17 02:20:36 +04:00
RESERVE_SPACE ( 16 ) ;
WRITE32 ( 0 ) ; /* tag length is always 0 */
2009-06-16 05:20:53 +04:00
WRITE32 ( hdr - > minorversion ) ;
2005-04-17 02:20:36 +04:00
WRITE32 ( hdr - > ident ) ;
2009-06-16 05:20:50 +04:00
hdr - > nops_p = p ;
2005-04-17 02:20:36 +04:00
WRITE32 ( hdr - > nops ) ;
}
2009-06-16 05:20:50 +04:00
static void encode_cb_nops ( struct nfs4_cb_compound_hdr * hdr )
{
* hdr - > nops_p = htonl ( hdr - > nops ) ;
}
static void
encode_cb_recall ( struct xdr_stream * xdr , struct nfs4_delegation * dp ,
struct nfs4_cb_compound_hdr * hdr )
2005-04-17 02:20:36 +04:00
{
2006-10-20 10:29:01 +04:00
__be32 * p ;
2009-05-02 03:50:00 +04:00
int len = dp - > dl_fh . fh_size ;
2005-04-17 02:20:36 +04:00
2009-05-02 03:50:00 +04:00
RESERVE_SPACE ( 12 + sizeof ( dp - > dl_stateid ) + len ) ;
2005-04-17 02:20:36 +04:00
WRITE32 ( OP_CB_RECALL ) ;
2009-05-02 03:50:00 +04:00
WRITE32 ( dp - > dl_stateid . si_generation ) ;
WRITEMEM ( & dp - > dl_stateid . si_opaque , sizeof ( stateid_opaque_t ) ) ;
2009-05-02 03:57:46 +04:00
WRITE32 ( 0 ) ; /* truncate optimization not implemented */
2005-04-17 02:20:36 +04:00
WRITE32 ( len ) ;
2009-05-02 03:50:00 +04:00
WRITEMEM ( & dp - > dl_fh . fh_base , len ) ;
2009-06-16 05:20:50 +04:00
hdr - > nops + + ;
2005-04-17 02:20:36 +04:00
}
2009-09-10 13:26:51 +04:00
static void
encode_cb_sequence ( struct xdr_stream * xdr , struct nfsd4_cb_sequence * args ,
struct nfs4_cb_compound_hdr * hdr )
{
__be32 * p ;
if ( hdr - > minorversion = = 0 )
return ;
RESERVE_SPACE ( 1 + NFS4_MAX_SESSIONID_LEN + 20 ) ;
WRITE32 ( OP_CB_SEQUENCE ) ;
WRITEMEM ( args - > cbs_clp - > cl_sessionid . data , NFS4_MAX_SESSIONID_LEN ) ;
WRITE32 ( args - > cbs_clp - > cl_cb_seq_nr ) ;
WRITE32 ( 0 ) ; /* slotid, always 0 */
WRITE32 ( 0 ) ; /* highest slotid always 0 */
WRITE32 ( 0 ) ; /* cachethis always 0 */
WRITE32 ( 0 ) ; /* FIXME: support referring_call_lists */
hdr - > nops + + ;
}
2005-04-17 02:20:36 +04:00
static int
2006-10-20 10:29:01 +04:00
nfs4_xdr_enc_cb_null ( struct rpc_rqst * req , __be32 * p )
2005-04-17 02:20:36 +04:00
{
struct xdr_stream xdrs , * xdr = & xdrs ;
xdr_init_encode ( & xdrs , & req - > rq_snd_buf , p ) ;
RESERVE_SPACE ( 0 ) ;
return 0 ;
}
static int
2009-09-10 13:27:04 +04:00
nfs4_xdr_enc_cb_recall ( struct rpc_rqst * req , __be32 * p ,
struct nfs4_rpc_args * rpc_args )
2005-04-17 02:20:36 +04:00
{
struct xdr_stream xdr ;
2009-09-10 13:27:04 +04:00
struct nfs4_delegation * args = rpc_args - > args_op ;
2005-04-17 02:20:36 +04:00
struct nfs4_cb_compound_hdr hdr = {
2009-05-02 03:50:00 +04:00
. ident = args - > dl_ident ,
2009-09-10 13:27:04 +04:00
. minorversion = rpc_args - > args_seq . cbs_minorversion ,
2005-04-17 02:20:36 +04:00
} ;
xdr_init_encode ( & xdr , & req - > rq_snd_buf , p ) ;
encode_cb_compound_hdr ( & xdr , & hdr ) ;
2009-09-10 13:27:04 +04:00
encode_cb_sequence ( & xdr , & rpc_args - > args_seq , & hdr ) ;
2009-06-16 05:20:50 +04:00
encode_cb_recall ( & xdr , args , & hdr ) ;
encode_cb_nops ( & hdr ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
decode_cb_compound_hdr ( struct xdr_stream * xdr , struct nfs4_cb_compound_hdr * hdr ) {
2006-10-20 10:29:01 +04:00
__be32 * p ;
2005-04-17 02:20:36 +04:00
READ_BUF ( 8 ) ;
READ32 ( hdr - > status ) ;
READ32 ( hdr - > taglen ) ;
READ_BUF ( hdr - > taglen + 4 ) ;
hdr - > tag = ( char * ) p ;
p + = XDR_QUADLEN ( hdr - > taglen ) ;
READ32 ( hdr - > nops ) ;
return 0 ;
}
static int
decode_cb_op_hdr ( struct xdr_stream * xdr , enum nfs_opnum4 expected )
{
2006-10-20 10:29:01 +04:00
__be32 * p ;
2005-04-17 02:20:36 +04:00
u32 op ;
int32_t nfserr ;
READ_BUF ( 8 ) ;
READ32 ( op ) ;
if ( op ! = expected ) {
dprintk ( " NFSD: decode_cb_op_hdr: Callback server returned "
" operation %d but we issued a request for %d \n " ,
op , expected ) ;
return - EIO ;
}
READ32 ( nfserr ) ;
if ( nfserr ! = NFS_OK )
return - nfs_cb_stat_to_errno ( nfserr ) ;
return 0 ;
}
2009-09-10 13:26:51 +04:00
/*
* Our current back channel implmentation supports a single backchannel
* with a single slot .
*/
static int
decode_cb_sequence ( struct xdr_stream * xdr , struct nfsd4_cb_sequence * res ,
struct rpc_rqst * rqstp )
{
struct nfs4_sessionid id ;
int status ;
u32 dummy ;
__be32 * p ;
if ( res - > cbs_minorversion = = 0 )
return 0 ;
status = decode_cb_op_hdr ( xdr , OP_CB_SEQUENCE ) ;
if ( status )
return status ;
/*
* If the server returns different values for sessionID , slotID or
* sequence number , the server is looney tunes .
*/
status = - ESERVERFAULT ;
READ_BUF ( NFS4_MAX_SESSIONID_LEN + 16 ) ;
memcpy ( id . data , p , NFS4_MAX_SESSIONID_LEN ) ;
p + = XDR_QUADLEN ( NFS4_MAX_SESSIONID_LEN ) ;
if ( memcmp ( id . data , res - > cbs_clp - > cl_sessionid . data ,
NFS4_MAX_SESSIONID_LEN ) ) {
dprintk ( " %s Invalid session id \n " , __func__ ) ;
goto out ;
}
READ32 ( dummy ) ;
if ( dummy ! = res - > cbs_clp - > cl_cb_seq_nr ) {
dprintk ( " %s Invalid sequence number \n " , __func__ ) ;
goto out ;
}
READ32 ( dummy ) ; /* slotid must be 0 */
if ( dummy ! = 0 ) {
dprintk ( " %s Invalid slotid \n " , __func__ ) ;
goto out ;
}
/* FIXME: process highest slotid and target highest slotid */
status = 0 ;
out :
return status ;
}
2005-04-17 02:20:36 +04:00
static int
2006-10-20 10:29:01 +04:00
nfs4_xdr_dec_cb_null ( struct rpc_rqst * req , __be32 * p )
2005-04-17 02:20:36 +04:00
{
return 0 ;
}
static int
2009-09-10 13:27:04 +04:00
nfs4_xdr_dec_cb_recall ( struct rpc_rqst * rqstp , __be32 * p ,
struct nfsd4_cb_sequence * seq )
2005-04-17 02:20:36 +04:00
{
struct xdr_stream xdr ;
struct nfs4_cb_compound_hdr hdr ;
int status ;
xdr_init_decode ( & xdr , & rqstp - > rq_rcv_buf , p ) ;
status = decode_cb_compound_hdr ( & xdr , & hdr ) ;
if ( status )
goto out ;
2009-09-10 13:27:04 +04:00
if ( seq ) {
status = decode_cb_sequence ( & xdr , seq , rqstp ) ;
if ( status )
goto out ;
}
2005-04-17 02:20:36 +04:00
status = decode_cb_op_hdr ( & xdr , OP_CB_RECALL ) ;
out :
return status ;
}
/*
* RPC procedure tables
*/
# define PROC(proc, call, argtype, restype) \
[ NFSPROC4_CLNT_ # # proc ] = { \
. p_proc = NFSPROC4_CB_ # # call , \
. p_encode = ( kxdrproc_t ) nfs4_xdr_ # # argtype , \
. p_decode = ( kxdrproc_t ) nfs4_xdr_ # # restype , \
2007-03-30 00:47:53 +04:00
. p_arglen = NFS4_ # # argtype # # _sz , \
. p_replen = NFS4_ # # restype # # _sz , \
2006-03-20 21:44:22 +03:00
. p_statidx = NFSPROC4_CB_ # # call , \
. p_name = # proc , \
2005-04-17 02:20:36 +04:00
}
2005-06-24 09:04:03 +04:00
static struct rpc_procinfo nfs4_cb_procedures [ ] = {
2005-04-17 02:20:36 +04:00
PROC ( CB_NULL , NULL , enc_cb_null , dec_cb_null ) ,
PROC ( CB_RECALL , COMPOUND , enc_cb_recall , dec_cb_recall ) ,
} ;
2005-06-24 09:04:03 +04:00
static struct rpc_version nfs_cb_version4 = {
2005-04-17 02:20:36 +04:00
. number = 1 ,
2006-03-24 14:15:34 +03:00
. nrprocs = ARRAY_SIZE ( nfs4_cb_procedures ) ,
2005-04-17 02:20:36 +04:00
. procs = nfs4_cb_procedures
} ;
static struct rpc_version * nfs_cb_version [ ] = {
NULL ,
& nfs_cb_version4 ,
} ;
2008-03-28 23:04:56 +03:00
static struct rpc_program cb_program ;
static struct rpc_stat cb_stats = {
. program = & cb_program
} ;
# define NFS4_CALLBACK 0x40000000
static struct rpc_program cb_program = {
. name = " nfs4_cb " ,
. number = NFS4_CALLBACK ,
. nrvers = ARRAY_SIZE ( nfs_cb_version ) ,
. version = nfs_cb_version ,
. stats = & cb_stats ,
2008-12-24 00:19:00 +03:00
. pipe_dir_name = " /nfsd4_cb " ,
2008-03-28 23:04:56 +03:00
} ;
2009-03-06 01:18:10 +03:00
static int max_cb_time ( void )
{
2010-03-01 07:20:19 +03:00
return max ( nfsd4_lease / 10 , ( time_t ) 1 ) * HZ ;
2009-03-06 01:18:10 +03:00
}
2007-07-28 02:06:50 +04:00
/* Reference counting, callback cleanup, etc., all look racy as heck.
* And why is cb_set an atomic ? */
2009-02-23 21:45:27 +03:00
int setup_callback_client ( struct nfs4_client * clp )
2007-07-28 02:06:50 +04:00
{
2009-04-30 03:09:19 +04:00
struct nfs4_cb_conn * cb = & clp - > cl_cb_conn ;
2006-08-23 04:06:21 +04:00
struct rpc_timeout timeparms = {
2009-03-06 01:18:10 +03:00
. to_initval = max_cb_time ( ) ,
. to_retries = 0 ,
2006-08-23 04:06:21 +04:00
} ;
struct rpc_create_args args = {
2009-09-10 13:27:21 +04:00
. protocol = XPRT_TRANSPORT_TCP ,
2009-08-14 20:57:57 +04:00
. address = ( struct sockaddr * ) & cb - > cb_addr ,
. addrsize = cb - > cb_addrlen ,
2006-08-23 04:06:21 +04:00
. timeout = & timeparms ,
2008-03-28 23:04:56 +03:00
. program = & cb_program ,
2008-09-28 10:21:26 +04:00
. prognumber = cb - > cb_prog ,
2006-08-23 04:06:21 +04:00
. version = nfs_cb_version [ 1 ] - > number ,
2008-12-24 00:19:00 +03:00
. authflavor = clp - > cl_flavor ,
2008-06-10 00:51:31 +04:00
. flags = ( RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET ) ,
2008-12-24 00:17:40 +03:00
. client_name = clp - > cl_principal ,
2006-08-23 04:06:21 +04:00
} ;
2007-10-26 03:00:26 +04:00
struct rpc_clnt * client ;
2005-04-17 02:20:36 +04:00
2009-02-23 02:52:13 +03:00
if ( ! clp - > cl_principal & & ( clp - > cl_flavor > = RPC_AUTH_GSS_KRB5 ) )
2009-02-23 21:45:27 +03:00
return - EINVAL ;
2009-09-10 13:27:21 +04:00
if ( cb - > cb_minorversion ) {
args . bc_xprt = clp - > cl_cb_xprt ;
args . protocol = XPRT_TRANSPORT_BC_TCP ;
}
2006-08-23 04:06:21 +04:00
/* Create RPC client */
2007-10-26 03:00:26 +04:00
client = rpc_create ( & args ) ;
2009-02-23 21:45:27 +03:00
if ( IS_ERR ( client ) ) {
2009-02-23 03:43:45 +03:00
dprintk ( " NFSD: couldn't create callback client: %ld \n " ,
PTR_ERR ( client ) ) ;
2009-02-23 21:45:27 +03:00
return PTR_ERR ( client ) ;
}
cb - > cb_client = client ;
return 0 ;
2009-02-23 03:43:45 +03:00
}
2009-02-24 06:35:22 +03:00
static void warn_no_callback_path ( struct nfs4_client * clp , int reason )
{
dprintk ( " NFSD: warning: no callback path to client %.*s: error %d \n " ,
( int ) clp - > cl_name . len , clp - > cl_name . data , reason ) ;
}
2009-03-05 23:01:11 +03:00
static void nfsd4_cb_probe_done ( struct rpc_task * task , void * calldata )
{
struct nfs4_client * clp = calldata ;
if ( task - > tk_status )
warn_no_callback_path ( clp , task - > tk_status ) ;
else
2009-04-30 03:09:19 +04:00
atomic_set ( & clp - > cl_cb_conn . cb_set , 1 ) ;
2009-03-05 23:01:11 +03:00
put_nfs4_client ( clp ) ;
}
static const struct rpc_call_ops nfsd4_cb_probe_ops = {
. rpc_call_done = nfsd4_cb_probe_done ,
} ;
2009-09-16 02:07:35 +04:00
static struct rpc_cred * callback_cred ;
int set_callback_cred ( void )
2009-02-24 08:42:10 +03:00
{
2010-03-04 00:13:29 +03:00
if ( callback_cred )
return 0 ;
2009-09-16 02:07:35 +04:00
callback_cred = rpc_lookup_machine_cred ( ) ;
if ( ! callback_cred )
return - ENOMEM ;
return 0 ;
2009-02-24 08:42:10 +03:00
}
2009-09-16 02:07:35 +04:00
2009-03-05 23:01:11 +03:00
void do_probe_callback ( struct nfs4_client * clp )
2009-02-23 03:43:45 +03:00
{
2009-04-30 03:09:19 +04:00
struct nfs4_cb_conn * cb = & clp - > cl_cb_conn ;
2009-02-23 03:43:45 +03:00
struct rpc_message msg = {
. rpc_proc = & nfs4_cb_procedures [ NFSPROC4_CLNT_CB_NULL ] ,
. rpc_argp = clp ,
2009-09-16 02:07:35 +04:00
. rpc_cred = callback_cred
2009-02-23 03:43:45 +03:00
} ;
int status ;
2010-02-25 00:48:06 +03:00
status = rpc_call_async ( cb - > cb_client , & msg ,
RPC_TASK_SOFT | RPC_TASK_SOFTCONN ,
2009-03-05 23:01:11 +03:00
& nfsd4_cb_probe_ops , ( void * ) clp ) ;
if ( status ) {
2009-02-24 06:35:22 +03:00
warn_no_callback_path ( clp , status ) ;
2009-03-05 23:01:11 +03:00
put_nfs4_client ( clp ) ;
}
2007-10-26 03:00:26 +04:00
}
/*
* Set up the callback client and put a NFSPROC4_CB_NULL on the wire . . .
*/
void
nfsd4_probe_callback ( struct nfs4_client * clp )
{
2009-02-24 06:35:22 +03:00
int status ;
2007-10-26 03:00:26 +04:00
2009-04-30 03:09:19 +04:00
BUG_ON ( atomic_read ( & clp - > cl_cb_conn . cb_set ) ) ;
2007-10-26 03:00:26 +04:00
2009-02-24 06:35:22 +03:00
status = setup_callback_client ( clp ) ;
if ( status ) {
warn_no_callback_path ( clp , status ) ;
return ;
}
2006-04-11 09:55:38 +04:00
/* the task holds a reference to the nfs4_client struct */
2005-04-17 02:20:36 +04:00
atomic_inc ( & clp - > cl_count ) ;
2009-03-05 23:01:11 +03:00
do_probe_callback ( clp ) ;
2005-04-17 02:20:36 +04:00
}
2009-09-10 13:26:38 +04:00
/*
* There ' s currently a single callback channel slot .
* If the slot is available , then mark it busy . Otherwise , set the
* thread for sleeping on the callback RPC wait queue .
*/
static int nfsd41_cb_setup_sequence ( struct nfs4_client * clp ,
struct rpc_task * task )
{
struct nfs4_rpc_args * args = task - > tk_msg . rpc_argp ;
u32 * ptr = ( u32 * ) clp - > cl_sessionid . data ;
int status = 0 ;
dprintk ( " %s: %u:%u:%u:%u \n " , __func__ ,
ptr [ 0 ] , ptr [ 1 ] , ptr [ 2 ] , ptr [ 3 ] ) ;
if ( test_and_set_bit ( 0 , & clp - > cl_cb_slot_busy ) ! = 0 ) {
rpc_sleep_on ( & clp - > cl_cb_waitq , task , NULL ) ;
dprintk ( " %s slot is busy \n " , __func__ ) ;
status = - EAGAIN ;
goto out ;
}
/*
* We ' ll need the clp during XDR encoding and decoding ,
* and the sequence during decoding to verify the reply
*/
args - > args_seq . cbs_clp = clp ;
task - > tk_msg . rpc_resp = & args - > args_seq ;
out :
dprintk ( " %s status=%d \n " , __func__ , status ) ;
return status ;
}
/*
* TODO : cb_sequence should support referring call lists , cachethis , multiple
* slots , and mark callback channel down on communication errors .
*/
static void nfsd4_cb_prepare ( struct rpc_task * task , void * calldata )
{
struct nfs4_delegation * dp = calldata ;
struct nfs4_client * clp = dp - > dl_client ;
struct nfs4_rpc_args * args = task - > tk_msg . rpc_argp ;
u32 minorversion = clp - > cl_cb_conn . cb_minorversion ;
int status = 0 ;
args - > args_seq . cbs_minorversion = minorversion ;
if ( minorversion ) {
status = nfsd41_cb_setup_sequence ( clp , task ) ;
if ( status ) {
if ( status ! = - EAGAIN ) {
/* terminate rpc task */
task - > tk_status = status ;
task - > tk_action = NULL ;
}
return ;
}
}
rpc_call_start ( task ) ;
}
2009-09-10 13:27:04 +04:00
static void nfsd4_cb_done ( struct rpc_task * task , void * calldata )
{
struct nfs4_delegation * dp = calldata ;
struct nfs4_client * clp = dp - > dl_client ;
dprintk ( " %s: minorversion=%d \n " , __func__ ,
clp - > cl_cb_conn . cb_minorversion ) ;
if ( clp - > cl_cb_conn . cb_minorversion ) {
/* No need for lock, access serialized in nfsd4_cb_prepare */
+ + clp - > cl_cb_seq_nr ;
clear_bit ( 0 , & clp - > cl_cb_slot_busy ) ;
rpc_wake_up_next ( & clp - > cl_cb_waitq ) ;
dprintk ( " %s: freed slot, new seqid=%d \n " , __func__ ,
clp - > cl_cb_seq_nr ) ;
/* We're done looking into the sequence information */
task - > tk_msg . rpc_resp = NULL ;
}
}
2009-05-02 06:36:55 +04:00
static void nfsd4_cb_recall_done ( struct rpc_task * task , void * calldata )
{
struct nfs4_delegation * dp = calldata ;
struct nfs4_client * clp = dp - > dl_client ;
2009-09-10 13:27:04 +04:00
nfsd4_cb_done ( task , calldata ) ;
2009-05-02 06:36:55 +04:00
switch ( task - > tk_status ) {
case - EIO :
/* Network partition? */
atomic_set ( & clp - > cl_cb_conn . cb_set , 0 ) ;
warn_no_callback_path ( clp , task - > tk_status ) ;
case - EBADHANDLE :
case - NFS4ERR_BAD_STATEID :
/* Race: client probably got cb_recall
* before open reply granting delegation */
break ;
default :
/* success, or error we can't handle */
2009-09-10 13:27:04 +04:00
goto done ;
2009-05-02 06:36:55 +04:00
}
if ( dp - > dl_retries - - ) {
rpc_delay ( task , 2 * HZ ) ;
task - > tk_status = 0 ;
rpc_restart_call ( task ) ;
2009-09-10 13:27:04 +04:00
return ;
2009-05-02 06:36:55 +04:00
} else {
atomic_set ( & clp - > cl_cb_conn . cb_set , 0 ) ;
warn_no_callback_path ( clp , task - > tk_status ) ;
}
2009-09-10 13:27:04 +04:00
done :
kfree ( task - > tk_msg . rpc_argp ) ;
2009-05-02 06:36:55 +04:00
}
static void nfsd4_cb_recall_release ( void * calldata )
{
struct nfs4_delegation * dp = calldata ;
struct nfs4_client * clp = dp - > dl_client ;
nfs4_put_delegation ( dp ) ;
put_nfs4_client ( clp ) ;
}
static const struct rpc_call_ops nfsd4_cb_recall_ops = {
2009-09-10 13:26:38 +04:00
. rpc_call_prepare = nfsd4_cb_prepare ,
2009-05-02 06:36:55 +04:00
. rpc_call_done = nfsd4_cb_recall_done ,
. rpc_release = nfsd4_cb_recall_release ,
} ;
2005-04-17 02:20:36 +04:00
/*
* called with dp - > dl_count inc ' ed .
*/
void
nfsd4_cb_recall ( struct nfs4_delegation * dp )
{
struct nfs4_client * clp = dp - > dl_client ;
2009-04-30 03:09:19 +04:00
struct rpc_clnt * clnt = clp - > cl_cb_conn . cb_client ;
2009-09-10 13:27:04 +04:00
struct nfs4_rpc_args * args ;
2005-04-17 02:20:36 +04:00
struct rpc_message msg = {
. rpc_proc = & nfs4_cb_procedures [ NFSPROC4_CLNT_CB_RECALL ] ,
2009-09-16 02:07:35 +04:00
. rpc_cred = callback_cred
2005-04-17 02:20:36 +04:00
} ;
2009-09-10 13:27:04 +04:00
int status = - ENOMEM ;
2005-04-17 02:20:36 +04:00
2009-09-10 13:27:04 +04:00
args = kzalloc ( sizeof ( * args ) , GFP_KERNEL ) ;
if ( ! args )
goto out ;
args - > args_op = dp ;
msg . rpc_argp = args ;
2009-05-02 04:11:12 +04:00
dp - > dl_retries = 1 ;
2009-05-02 06:36:55 +04:00
status = rpc_call_async ( clnt , & msg , RPC_TASK_SOFT ,
& nfsd4_cb_recall_ops , dp ) ;
2009-09-10 13:27:04 +04:00
out :
2009-05-02 06:36:55 +04:00
if ( status ) {
2009-09-10 13:27:04 +04:00
kfree ( args ) ;
2009-05-02 06:36:55 +04:00
put_nfs4_client ( clp ) ;
nfs4_put_delegation ( dp ) ;
2005-04-17 02:20:36 +04:00
}
}