/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Copyright 2011, 2012 Nexenta Systems, Inc. All rights reserved.
* Copyright 2013 Joyent, Inc. All rights reserved.
*/
/*
* This is the loadable module wrapper.
*/
#include <sys/systm.h>
#include <sys/modctl.h>
#include <sys/syscall.h>
#include <sys/ddi.h>
#include <sys/cmn_err.h>
#include <nfs/nfs.h>
#include <nfs/nfs_clnt.h>
#include <nfs/nfs4.h>
#include <nfs/rnode4.h>
/*
* The global tag list.
*/
ctag_t nfs4_ctags[] = NFS4_TAG_INITIALIZER;
/*
* The NFS Version 4 client VFS.
*/
static vfsdef_t vfw4 = {
VFSDEF_VERSION,
"nfs4",
nfs4init,
VSW_CANREMOUNT|VSW_NOTZONESAFE|VSW_STATS,
NULL
};
struct modlfs modlfs4 = {
&mod_fsops,
"network filesystem version 4",
&vfw4
};
uint_t nfs4_max_transfer_size = 32 * 1024;
uint_t nfs4_max_transfer_size_cots = 1024 * 1024;
uint_t nfs4_max_transfer_size_rdma = 1024 * 1024;
int
nfs4tsize(void)
{
/*
* For the moment, just return nfs4_max_transfer_size until we
* can query the appropriate transport.
*/
return (nfs4_max_transfer_size);
}
uint_t
nfs4_tsize(struct knetconfig *knp)
{
if (knp->knc_semantics == NC_TPI_COTS_ORD ||
knp->knc_semantics == NC_TPI_COTS)
return (nfs4_max_transfer_size_cots);
if (knp->knc_semantics == NC_TPI_RDMA)
return (nfs4_max_transfer_size_rdma);
return (nfs4_max_transfer_size);
}
uint_t
rfs4_tsize(struct svc_req *req)
{
if (req->rq_xprt->xp_type == T_COTS_ORD ||
req->rq_xprt->xp_type == T_COTS)
return (nfs4_max_transfer_size_cots);
if (req->rq_xprt->xp_type == T_RDMA)
return (nfs4_max_transfer_size_rdma);
return (nfs4_max_transfer_size);
}
int
nfs4_setopts(vnode_t *vp, model_t model, struct nfs_args *buf)
{
mntinfo4_t *mi; /* mount info, pointed at by vfs */
STRUCT_HANDLE(nfs_args, args);
int flags;
#ifdef lint
model = model;
#endif
STRUCT_SET_HANDLE(args, model, buf);
flags = STRUCT_FGET(args, flags);
/*
* Set option fields in mount info record
*/
mi = VTOMI4(vp);
if (flags & NFSMNT_NOAC) {
mutex_enter(&mi->mi_lock);
mi->mi_flags |= MI4_NOAC;
mutex_exit(&mi->mi_lock);
PURGE_ATTRCACHE4(vp);
}
mutex_enter(&mi->mi_lock);
if (flags & NFSMNT_NOCTO)
mi->mi_flags |= MI4_NOCTO;
if (flags & NFSMNT_LLOCK)
mi->mi_flags |= MI4_LLOCK;
if (flags & NFSMNT_GRPID)
mi->mi_flags |= MI4_GRPID;
mutex_exit(&mi->mi_lock);
if (flags & NFSMNT_RETRANS) {
if (STRUCT_FGET(args, retrans) < 0)
return (EINVAL);
mi->mi_retrans = STRUCT_FGET(args, retrans);
}
if (flags & NFSMNT_TIMEO) {
if (STRUCT_FGET(args, timeo) <= 0)
return (EINVAL);
mi->mi_timeo = STRUCT_FGET(args, timeo);
}
if (flags & NFSMNT_RSIZE) {
if (STRUCT_FGET(args, rsize) <= 0)
return (EINVAL);
mi->mi_tsize = MIN(mi->mi_tsize, STRUCT_FGET(args, rsize));
mi->mi_curread = MIN(mi->mi_curread, mi->mi_tsize);
}
if (flags & NFSMNT_WSIZE) {
if (STRUCT_FGET(args, wsize) <= 0)
return (EINVAL);
mi->mi_stsize = MIN(mi->mi_stsize, STRUCT_FGET(args, wsize));
mi->mi_curwrite = MIN(mi->mi_curwrite, mi->mi_stsize);
}
if (flags & NFSMNT_ACREGMIN) {
if (STRUCT_FGET(args, acregmin) < 0)
mi->mi_acregmin = SEC2HR(ACMINMAX);
else
mi->mi_acregmin = SEC2HR(MIN(STRUCT_FGET(args,
acregmin), ACMINMAX));
}
if (flags & NFSMNT_ACREGMAX) {
if (STRUCT_FGET(args, acregmax) < 0)
mi->mi_acregmax = SEC2HR(ACMAXMAX);
else
mi->mi_acregmax = SEC2HR(MIN(STRUCT_FGET(args,
acregmax), ACMAXMAX));
}
if (flags & NFSMNT_ACDIRMIN) {
if (STRUCT_FGET(args, acdirmin) < 0)
mi->mi_acdirmin = SEC2HR(ACMINMAX);
else
mi->mi_acdirmin = SEC2HR(MIN(STRUCT_FGET(args,
acdirmin), ACMINMAX));
}
if (flags & NFSMNT_ACDIRMAX) {
if (STRUCT_FGET(args, acdirmax) < 0)
mi->mi_acdirmax = SEC2HR(ACMAXMAX);
else
mi->mi_acdirmax = SEC2HR(MIN(STRUCT_FGET(args,
acdirmax), ACMAXMAX));
}
return (0);
}
/*
* This returns 1 if the seqid should be bumped upon receiving this
* 'res->status' for a seqid dependent operation; otherwise return 0.
*/
int
nfs4_need_to_bump_seqid(COMPOUND4res_clnt *res)
{
int i, seqid_dep_op = 0;
nfs_resop4 *resop;
resop = res->array;
for (i = 0; i < res->array_len; i++) {
switch (resop[i].resop) {
case OP_CLOSE:
case OP_OPEN:
case OP_OPEN_CONFIRM:
case OP_OPEN_DOWNGRADE:
case OP_LOCK:
case OP_LOCKU:
seqid_dep_op = 1;
break;
default:
continue;
}
}
if (!seqid_dep_op)
return (0);
switch (res->status) {
case NFS4ERR_STALE_CLIENTID:
case NFS4ERR_STALE_STATEID:
case NFS4ERR_BAD_STATEID:
case NFS4ERR_BAD_SEQID:
case NFS4ERR_BADXDR:
case NFS4ERR_OLD_STATEID:
case NFS4ERR_RESOURCE:
case NFS4ERR_NOFILEHANDLE:
return (0);
default:
return (1);
}
}
/*
* Returns 1 if the error is a RPC error that we should retry.
*/
int
nfs4_rpc_retry_error(int error)
{
switch (error) {
case ETIMEDOUT:
case ECONNREFUSED:
case ENETDOWN:
case ENETUNREACH:
case ENETRESET:
case ECONNABORTED:
case EHOSTUNREACH:
case ECONNRESET:
return (1);
default:
return (0);
}
}
char *
nfs4_stat_to_str(nfsstat4 error)
{
static char buf[40];
switch (error) {
case NFS4_OK:
return ("NFS4_OK");
case NFS4ERR_PERM:
return ("NFS4ERR_PERM");
case NFS4ERR_NOENT:
return ("NFS4ERR_NOENT");
case NFS4ERR_IO:
return ("NFS4ERR_IO");
case NFS4ERR_NXIO:
return ("NFS4ERR_NXIO");
case NFS4ERR_ACCESS:
return ("NFS4ERR_ACCESS");
case NFS4ERR_EXIST:
return ("NFS4ERR_EXIST");
case NFS4ERR_XDEV:
return ("NFS4ERR_XDEV");
case NFS4ERR_NOTDIR:
return ("NFS4ERR_NOTDIR");
case NFS4ERR_ISDIR:
return ("NFS4ERR_ISDIR");
case NFS4ERR_INVAL:
return ("NFS4ERR_INVAL");
case NFS4ERR_FBIG:
return ("NFS4ERR_FBIG");
case NFS4ERR_NOSPC:
return ("NFS4ERR_NOSPC");
case NFS4ERR_ROFS:
return ("NFS4ERR_ROFS");
case NFS4ERR_MLINK:
return ("NFS4ERR_MLINK");
case NFS4ERR_NAMETOOLONG:
return ("NFS4ERR_NAMETOOLONG");
case NFS4ERR_NOTEMPTY:
return ("NFSS4ERR_NOTEMPTY");
case NFS4ERR_DQUOT:
return ("NFS4ERR_DQUOT");
case NFS4ERR_STALE:
return ("NFS4ERR_STALE");
case NFS4ERR_BADHANDLE:
return ("NFS4ERR_BADHANDLE");
case NFS4ERR_BAD_COOKIE:
return ("NFS4ERR_BAD_COOKIE");
case NFS4ERR_NOTSUPP:
return ("NFS4ERR_NOTSUPP");
case NFS4ERR_TOOSMALL:
return ("NFS4ERR_TOOSMALL");
case NFS4ERR_SERVERFAULT:
return ("NFS4ERR_SERVERFAULT");
case NFS4ERR_BADTYPE:
return ("NFS4ERR_BADTYPE");
case NFS4ERR_DELAY:
return ("NFS4ERR_DELAY");
case NFS4ERR_SAME:
return ("NFS4ERR_SAME");
case NFS4ERR_DENIED:
return ("NFS4ERR_DENIED");
case NFS4ERR_EXPIRED:
return ("NFS4ERR_EXPIRED");
case NFS4ERR_LOCKED:
return ("NFS4ERR_LOCKED");
case NFS4ERR_GRACE:
return ("NFS4ERR_GRACE");
case NFS4ERR_FHEXPIRED:
return ("NFS4ERR_FHEXPIRED");
case NFS4ERR_SHARE_DENIED:
return ("NFS4ERR_SHARE_DENIED");
case NFS4ERR_WRONGSEC:
return ("NFS4ERR_WRONGSEC");
case NFS4ERR_CLID_INUSE:
return ("NFS4ERR_CLID_INUSE");
case NFS4ERR_RESOURCE:
return ("NFS4ERR_RESOURCE");
case NFS4ERR_MOVED:
return ("NFS4ERR_MOVED");
case NFS4ERR_NOFILEHANDLE:
return ("NFS4ERR_NOFILEHANDLE");
case NFS4ERR_MINOR_VERS_MISMATCH:
return ("NFS4ERR_MINOR_VERS_MISMATCH");
case NFS4ERR_STALE_CLIENTID:
return ("NFS4ERR_STALE_CLIENTID");
case NFS4ERR_STALE_STATEID:
return ("NFS4ERR_STALE_STATEID");
case NFS4ERR_OLD_STATEID:
return ("NFS4ERR_OLD_STATEID");
case NFS4ERR_BAD_STATEID:
return ("NFS4ERR_BAD_STATEID");
case NFS4ERR_BAD_SEQID:
return ("NFS4ERR_BAD_SEQID");
case NFS4ERR_NOT_SAME:
return ("NFS4ERR_NOT_SAME");
case NFS4ERR_LOCK_RANGE:
return ("NFS4ERR_LOCK_RANGE");
case NFS4ERR_SYMLINK:
return ("NFS4ERR_SYMLINK");
case NFS4ERR_RESTOREFH:
return ("NFS4ERR_RESTOREFH");
case NFS4ERR_LEASE_MOVED:
return ("NFS4ERR_LEASE_MOVED");
case NFS4ERR_ATTRNOTSUPP:
return ("NFS4ERR_ATTRNOTSUPP");
case NFS4ERR_NO_GRACE:
return ("NFS4ERR_NO_GRACE");
case NFS4ERR_RECLAIM_BAD:
return ("NFS4ERR_RECLAIM_BAD");
case NFS4ERR_RECLAIM_CONFLICT:
return ("NFS4ERR_RECLAIM_CONFLICT");
case NFS4ERR_BADXDR:
return ("NFS4ERR_BADXDR");
case NFS4ERR_LOCKS_HELD:
return ("NFS4ERR_LOCKS_HELD");
case NFS4ERR_OPENMODE:
return ("NFS4ERR_OPENMODE");
case NFS4ERR_BADOWNER:
return ("NFS4ERR_BADOWNER");
case NFS4ERR_BADCHAR:
return ("NFS4ERR_BADCHAR");
case NFS4ERR_BADNAME:
return ("NFS4ERR_BADNAME");
case NFS4ERR_BAD_RANGE:
return ("NFS4ERR_BAD_RANGE");
case NFS4ERR_LOCK_NOTSUPP:
return ("NFS4ERR_LOCK_NOTSUPP");
case NFS4ERR_OP_ILLEGAL:
return ("NFS4ERR_OP_ILLEGAL");
case NFS4ERR_DEADLOCK:
return ("NFS4ERR_DEADLOCK");
case NFS4ERR_FILE_OPEN:
return ("NFS4ERR_FILE_OPEN");
case NFS4ERR_ADMIN_REVOKED:
return ("NFS4ERR_ADMIN_REVOKED");
case NFS4ERR_CB_PATH_DOWN:
return ("NFS4ERR_CB_PATH_DOWN");
default:
(void) snprintf(buf, 40, "Unknown error %d", (int)error);
return (buf);
}
}
char *
nfs4_recov_action_to_str(nfs4_recov_t what)
{
static char buf[40];
switch (what) {
case NR_STALE:
return ("NR_STALE");
case NR_FAILOVER:
return ("NR_FAILOVER");
case NR_CLIENTID:
return ("NR_CLIENTID");
case NR_OPENFILES:
return ("NR_OPENFILES");
case NR_WRONGSEC:
return ("NR_WRONGSEC");
case NR_EXPIRED:
return ("NR_EXPIRED");
case NR_BAD_STATEID:
return ("NR_BAD_STATEID");
case NR_FHEXPIRED:
return ("NR_FHEXPIRED");
case NR_BADHANDLE:
return ("NR_BADHANDLE");
case NR_BAD_SEQID:
return ("NR_BAD_SEQID");
case NR_OLDSTATEID:
return ("NR_OLDSTATEID");
case NR_GRACE:
return ("NR_GRACE");
case NR_DELAY:
return ("NR_DELAY");
case NR_LOST_LOCK:
return ("NR_LOST_LOCK");
case NR_LOST_STATE_RQST:
return ("NR_LOST_STATE_RQST");
case NR_MOVED:
return ("NR_MOVED");
default:
(void) snprintf(buf, 40, "Unknown, code %d", (int)what);
return (buf);
}
}
char *
nfs4_op_to_str(nfs_opnum4 op)
{
static char buf[40];
switch (REAL_OP4(op)) {
case OP_ACCESS:
return ("OP_ACCESS");
case OP_CLOSE:
return ("OP_CLOSE");
case OP_COMMIT:
return ("OP_COMMIT");
case OP_CREATE:
return ("OP_CREATE");
case OP_DELEGPURGE:
return ("OP_DELEGPURGE");
case OP_DELEGRETURN:
return ("OP_DELEGRETURN");
case OP_GETATTR:
return ("OP_GETATTR");
case OP_GETFH:
return ("OP_GETFH");
case OP_LINK:
return ("OP_LINK");
case OP_LOCK:
return ("OP_LOCK");
case OP_LOCKT:
return ("OP_LOCKT");
case OP_LOCKU:
return ("OP_LOCKU");
case OP_LOOKUP:
return ("OP_LOOKUP");
case OP_LOOKUPP:
return ("OP_LOOKUPP");
case OP_NVERIFY:
return ("OP_NVERIFY");
case OP_OPEN:
return ("OP_OPEN");
case OP_OPENATTR:
return ("OP_OPENATTR");
case OP_OPEN_CONFIRM:
return ("OP_OPEN_CONFIRM");
case OP_OPEN_DOWNGRADE:
return ("OP_OPEN_DOWNGRADE");
case OP_PUTFH:
return ("OP_PUTFH");
case OP_PUTPUBFH:
return ("OP_PUTPUBFH");
case OP_PUTROOTFH:
return ("OP_PUTROOTFH");
case OP_READ:
return ("OP_READ");
case OP_READDIR:
return ("OP_READDIR");
case OP_READLINK:
return ("OP_READLINK");
case OP_REMOVE:
return ("OP_REMOVE");
case OP_RENAME:
return ("OP_RENAME");
case OP_RENEW:
return ("OP_RENEW");
case OP_RESTOREFH:
return ("OP_RESTOREFH");
case OP_SAVEFH:
return ("OP_SAVEFH");
case OP_SECINFO:
return ("OP_SECINFO");
case OP_SETATTR:
return ("OP_SETATTR");
case OP_SETCLIENTID:
return ("OP_SETCLIENTID");
case OP_SETCLIENTID_CONFIRM:
return ("OP_SETCLIENTID_CONFIRM");
case OP_VERIFY:
return ("OP_VERIFY");
case OP_WRITE:
return ("OP_WRITE");
case OP_RELEASE_LOCKOWNER:
return ("OP_RELEASE_LOCKOWNER");
case OP_ILLEGAL:
return ("OP_ILLEGAL");
default:
(void) snprintf(buf, 40, "Unknown op %d", (int)op);
return (buf);
}
}