17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 77c478bd9Sstevel@tonic-gate * with the License. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 127c478bd9Sstevel@tonic-gate * and limitations under the License. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 197c478bd9Sstevel@tonic-gate * 207c478bd9Sstevel@tonic-gate * CDDL HEADER END 217c478bd9Sstevel@tonic-gate */ 227c478bd9Sstevel@tonic-gate /* 237c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate /* 287c478bd9Sstevel@tonic-gate * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T. 297c478bd9Sstevel@tonic-gate * All Rights Reserved 307c478bd9Sstevel@tonic-gate */ 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 337c478bd9Sstevel@tonic-gate 347c478bd9Sstevel@tonic-gate #include <sys/param.h> 357c478bd9Sstevel@tonic-gate #include <sys/types.h> 367c478bd9Sstevel@tonic-gate #include <sys/systm.h> 377c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 387c478bd9Sstevel@tonic-gate #include <sys/vtrace.h> 397c478bd9Sstevel@tonic-gate #include <sys/session.h> 407c478bd9Sstevel@tonic-gate #include <sys/thread.h> 417c478bd9Sstevel@tonic-gate #include <sys/dnlc.h> 427c478bd9Sstevel@tonic-gate #include <sys/cred.h> 437c478bd9Sstevel@tonic-gate #include <sys/list.h> 447c478bd9Sstevel@tonic-gate #include <sys/sdt.h> 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate #include <rpc/types.h> 477c478bd9Sstevel@tonic-gate #include <rpc/xdr.h> 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate #include <nfs/nfs.h> 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate #include <nfs/nfs_clnt.h> 527c478bd9Sstevel@tonic-gate 537c478bd9Sstevel@tonic-gate #include <nfs/nfs4.h> 547c478bd9Sstevel@tonic-gate #include <nfs/rnode4.h> 557c478bd9Sstevel@tonic-gate #include <nfs/nfs4_clnt.h> 567c478bd9Sstevel@tonic-gate 577c478bd9Sstevel@tonic-gate /* 587c478bd9Sstevel@tonic-gate * client side statistics 597c478bd9Sstevel@tonic-gate */ 607c478bd9Sstevel@tonic-gate static const struct clstat4 clstat4_tmpl = { 617c478bd9Sstevel@tonic-gate { "calls", KSTAT_DATA_UINT64 }, 627c478bd9Sstevel@tonic-gate { "badcalls", KSTAT_DATA_UINT64 }, 637c478bd9Sstevel@tonic-gate { "clgets", KSTAT_DATA_UINT64 }, 647c478bd9Sstevel@tonic-gate { "cltoomany", KSTAT_DATA_UINT64 }, 657c478bd9Sstevel@tonic-gate #ifdef DEBUG 667c478bd9Sstevel@tonic-gate { "clalloc", KSTAT_DATA_UINT64 }, 677c478bd9Sstevel@tonic-gate { "noresponse", KSTAT_DATA_UINT64 }, 687c478bd9Sstevel@tonic-gate { "failover", KSTAT_DATA_UINT64 }, 697c478bd9Sstevel@tonic-gate { "remap", KSTAT_DATA_UINT64 }, 707c478bd9Sstevel@tonic-gate #endif 717c478bd9Sstevel@tonic-gate }; 727c478bd9Sstevel@tonic-gate 737c478bd9Sstevel@tonic-gate #ifdef DEBUG 747c478bd9Sstevel@tonic-gate struct clstat4_debug clstat4_debug = { 757c478bd9Sstevel@tonic-gate { "nrnode", KSTAT_DATA_UINT64 }, 767c478bd9Sstevel@tonic-gate { "access", KSTAT_DATA_UINT64 }, 777c478bd9Sstevel@tonic-gate { "dirent", KSTAT_DATA_UINT64 }, 787c478bd9Sstevel@tonic-gate { "dirents", KSTAT_DATA_UINT64 }, 797c478bd9Sstevel@tonic-gate { "reclaim", KSTAT_DATA_UINT64 }, 807c478bd9Sstevel@tonic-gate { "clreclaim", KSTAT_DATA_UINT64 }, 817c478bd9Sstevel@tonic-gate { "f_reclaim", KSTAT_DATA_UINT64 }, 827c478bd9Sstevel@tonic-gate { "a_reclaim", KSTAT_DATA_UINT64 }, 837c478bd9Sstevel@tonic-gate { "r_reclaim", KSTAT_DATA_UINT64 }, 847c478bd9Sstevel@tonic-gate { "r_path", KSTAT_DATA_UINT64 }, 857c478bd9Sstevel@tonic-gate }; 867c478bd9Sstevel@tonic-gate #endif 877c478bd9Sstevel@tonic-gate 887c478bd9Sstevel@tonic-gate /* 897c478bd9Sstevel@tonic-gate * We keep a global list of per-zone client data, so we can clean up all zones 907c478bd9Sstevel@tonic-gate * if we get low on memory. 917c478bd9Sstevel@tonic-gate */ 927c478bd9Sstevel@tonic-gate static list_t nfs4_clnt_list; 937c478bd9Sstevel@tonic-gate static kmutex_t nfs4_clnt_list_lock; 947c478bd9Sstevel@tonic-gate static zone_key_t nfs4clnt_zone_key; 957c478bd9Sstevel@tonic-gate 967c478bd9Sstevel@tonic-gate static struct kmem_cache *chtab4_cache; 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate #ifdef DEBUG 997c478bd9Sstevel@tonic-gate static int nfs4_rfscall_debug; 1007c478bd9Sstevel@tonic-gate static int nfs4_try_failover_any; 1017c478bd9Sstevel@tonic-gate int nfs4_utf8_debug = 0; 1027c478bd9Sstevel@tonic-gate #endif 1037c478bd9Sstevel@tonic-gate 1047c478bd9Sstevel@tonic-gate /* 1057c478bd9Sstevel@tonic-gate * NFSv4 readdir cache implementation 1067c478bd9Sstevel@tonic-gate */ 1077c478bd9Sstevel@tonic-gate typedef struct rddir4_cache_impl { 1087c478bd9Sstevel@tonic-gate rddir4_cache rc; /* readdir cache element */ 1097c478bd9Sstevel@tonic-gate kmutex_t lock; /* lock protects count */ 1107c478bd9Sstevel@tonic-gate uint_t count; /* reference count */ 1117c478bd9Sstevel@tonic-gate avl_node_t tree; /* AVL tree link */ 1127c478bd9Sstevel@tonic-gate } rddir4_cache_impl; 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate static int rddir4_cache_compar(const void *, const void *); 1157c478bd9Sstevel@tonic-gate static void rddir4_cache_free(rddir4_cache_impl *); 1167c478bd9Sstevel@tonic-gate static rddir4_cache *rddir4_cache_alloc(int); 1177c478bd9Sstevel@tonic-gate static void rddir4_cache_hold(rddir4_cache *); 1187c478bd9Sstevel@tonic-gate static int try_failover(enum clnt_stat); 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate static int nfs4_readdir_cache_hits = 0; 1217c478bd9Sstevel@tonic-gate static int nfs4_readdir_cache_waits = 0; 1227c478bd9Sstevel@tonic-gate static int nfs4_readdir_cache_misses = 0; 1237c478bd9Sstevel@tonic-gate 1247c478bd9Sstevel@tonic-gate /* 1257c478bd9Sstevel@tonic-gate * Shared nfs4 functions 1267c478bd9Sstevel@tonic-gate */ 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate /* 1297c478bd9Sstevel@tonic-gate * Copy an nfs_fh4. The destination storage (to->nfs_fh4_val) must already 1307c478bd9Sstevel@tonic-gate * be allocated. 1317c478bd9Sstevel@tonic-gate */ 1327c478bd9Sstevel@tonic-gate 1337c478bd9Sstevel@tonic-gate void 1347c478bd9Sstevel@tonic-gate nfs_fh4_copy(nfs_fh4 *from, nfs_fh4 *to) 1357c478bd9Sstevel@tonic-gate { 1367c478bd9Sstevel@tonic-gate to->nfs_fh4_len = from->nfs_fh4_len; 1377c478bd9Sstevel@tonic-gate bcopy(from->nfs_fh4_val, to->nfs_fh4_val, to->nfs_fh4_len); 1387c478bd9Sstevel@tonic-gate } 1397c478bd9Sstevel@tonic-gate 1407c478bd9Sstevel@tonic-gate /* 1417c478bd9Sstevel@tonic-gate * nfs4cmpfh - compare 2 filehandles. 1427c478bd9Sstevel@tonic-gate * Returns 0 if the two nfsv4 filehandles are the same, -1 if the first is 1437c478bd9Sstevel@tonic-gate * "less" than the second, +1 if the first is "greater" than the second. 1447c478bd9Sstevel@tonic-gate */ 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate int 1477c478bd9Sstevel@tonic-gate nfs4cmpfh(const nfs_fh4 *fh4p1, const nfs_fh4 *fh4p2) 1487c478bd9Sstevel@tonic-gate { 1497c478bd9Sstevel@tonic-gate const char *c1, *c2; 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate if (fh4p1->nfs_fh4_len < fh4p2->nfs_fh4_len) 1527c478bd9Sstevel@tonic-gate return (-1); 1537c478bd9Sstevel@tonic-gate if (fh4p1->nfs_fh4_len > fh4p2->nfs_fh4_len) 1547c478bd9Sstevel@tonic-gate return (1); 1557c478bd9Sstevel@tonic-gate for (c1 = fh4p1->nfs_fh4_val, c2 = fh4p2->nfs_fh4_val; 1567c478bd9Sstevel@tonic-gate c1 < fh4p1->nfs_fh4_val + fh4p1->nfs_fh4_len; 1577c478bd9Sstevel@tonic-gate c1++, c2++) { 1587c478bd9Sstevel@tonic-gate if (*c1 < *c2) 1597c478bd9Sstevel@tonic-gate return (-1); 1607c478bd9Sstevel@tonic-gate if (*c1 > *c2) 1617c478bd9Sstevel@tonic-gate return (1); 1627c478bd9Sstevel@tonic-gate } 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate return (0); 1657c478bd9Sstevel@tonic-gate } 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate /* 1687c478bd9Sstevel@tonic-gate * Compare two v4 filehandles. Return zero if they're the same, non-zero 1697c478bd9Sstevel@tonic-gate * if they're not. Like nfs4cmpfh(), but different filehandle 1707c478bd9Sstevel@tonic-gate * representation, and doesn't provide information about greater than or 1717c478bd9Sstevel@tonic-gate * less than. 1727c478bd9Sstevel@tonic-gate */ 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate int 1757c478bd9Sstevel@tonic-gate nfs4cmpfhandle(nfs4_fhandle_t *fh1, nfs4_fhandle_t *fh2) 1767c478bd9Sstevel@tonic-gate { 1777c478bd9Sstevel@tonic-gate if (fh1->fh_len == fh2->fh_len) 1787c478bd9Sstevel@tonic-gate return (bcmp(fh1->fh_buf, fh2->fh_buf, fh1->fh_len)); 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate return (1); 1817c478bd9Sstevel@tonic-gate } 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate int 1847c478bd9Sstevel@tonic-gate stateid4_cmp(stateid4 *s1, stateid4 *s2) 1857c478bd9Sstevel@tonic-gate { 1867c478bd9Sstevel@tonic-gate if (bcmp(s1, s2, sizeof (stateid4)) == 0) 1877c478bd9Sstevel@tonic-gate return (1); 1887c478bd9Sstevel@tonic-gate else 1897c478bd9Sstevel@tonic-gate return (0); 1907c478bd9Sstevel@tonic-gate } 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate nfsstat4 1937c478bd9Sstevel@tonic-gate puterrno4(int error) 1947c478bd9Sstevel@tonic-gate { 1957c478bd9Sstevel@tonic-gate switch (error) { 1967c478bd9Sstevel@tonic-gate case 0: 1977c478bd9Sstevel@tonic-gate return (NFS4_OK); 1987c478bd9Sstevel@tonic-gate case EPERM: 1997c478bd9Sstevel@tonic-gate return (NFS4ERR_PERM); 2007c478bd9Sstevel@tonic-gate case ENOENT: 2017c478bd9Sstevel@tonic-gate return (NFS4ERR_NOENT); 2027c478bd9Sstevel@tonic-gate case EINTR: 2037c478bd9Sstevel@tonic-gate return (NFS4ERR_IO); 2047c478bd9Sstevel@tonic-gate case EIO: 2057c478bd9Sstevel@tonic-gate return (NFS4ERR_IO); 2067c478bd9Sstevel@tonic-gate case ENXIO: 2077c478bd9Sstevel@tonic-gate return (NFS4ERR_NXIO); 2087c478bd9Sstevel@tonic-gate case ENOMEM: 2097c478bd9Sstevel@tonic-gate return (NFS4ERR_RESOURCE); 2107c478bd9Sstevel@tonic-gate case EACCES: 2117c478bd9Sstevel@tonic-gate return (NFS4ERR_ACCESS); 2127c478bd9Sstevel@tonic-gate case EBUSY: 2137c478bd9Sstevel@tonic-gate return (NFS4ERR_IO); 2147c478bd9Sstevel@tonic-gate case EEXIST: 2157c478bd9Sstevel@tonic-gate return (NFS4ERR_EXIST); 2167c478bd9Sstevel@tonic-gate case EXDEV: 2177c478bd9Sstevel@tonic-gate return (NFS4ERR_XDEV); 2187c478bd9Sstevel@tonic-gate case ENODEV: 2197c478bd9Sstevel@tonic-gate return (NFS4ERR_IO); 2207c478bd9Sstevel@tonic-gate case ENOTDIR: 2217c478bd9Sstevel@tonic-gate return (NFS4ERR_NOTDIR); 2227c478bd9Sstevel@tonic-gate case EISDIR: 2237c478bd9Sstevel@tonic-gate return (NFS4ERR_ISDIR); 2247c478bd9Sstevel@tonic-gate case EINVAL: 2257c478bd9Sstevel@tonic-gate return (NFS4ERR_INVAL); 2267c478bd9Sstevel@tonic-gate case EMFILE: 2277c478bd9Sstevel@tonic-gate return (NFS4ERR_RESOURCE); 2287c478bd9Sstevel@tonic-gate case EFBIG: 2297c478bd9Sstevel@tonic-gate return (NFS4ERR_FBIG); 2307c478bd9Sstevel@tonic-gate case ENOSPC: 2317c478bd9Sstevel@tonic-gate return (NFS4ERR_NOSPC); 2327c478bd9Sstevel@tonic-gate case EROFS: 2337c478bd9Sstevel@tonic-gate return (NFS4ERR_ROFS); 2347c478bd9Sstevel@tonic-gate case EMLINK: 2357c478bd9Sstevel@tonic-gate return (NFS4ERR_MLINK); 2367c478bd9Sstevel@tonic-gate case EDEADLK: 2377c478bd9Sstevel@tonic-gate return (NFS4ERR_DEADLOCK); 2387c478bd9Sstevel@tonic-gate case ENOLCK: 2397c478bd9Sstevel@tonic-gate return (NFS4ERR_DENIED); 2407c478bd9Sstevel@tonic-gate case EREMOTE: 2417c478bd9Sstevel@tonic-gate return (NFS4ERR_SERVERFAULT); 2427c478bd9Sstevel@tonic-gate case ENOTSUP: 2437c478bd9Sstevel@tonic-gate return (NFS4ERR_NOTSUPP); 2447c478bd9Sstevel@tonic-gate case EDQUOT: 2457c478bd9Sstevel@tonic-gate return (NFS4ERR_DQUOT); 2467c478bd9Sstevel@tonic-gate case ENAMETOOLONG: 2477c478bd9Sstevel@tonic-gate return (NFS4ERR_NAMETOOLONG); 2487c478bd9Sstevel@tonic-gate case EOVERFLOW: 2497c478bd9Sstevel@tonic-gate return (NFS4ERR_INVAL); 2507c478bd9Sstevel@tonic-gate case ENOSYS: 2517c478bd9Sstevel@tonic-gate return (NFS4ERR_NOTSUPP); 2527c478bd9Sstevel@tonic-gate case ENOTEMPTY: 2537c478bd9Sstevel@tonic-gate return (NFS4ERR_NOTEMPTY); 2547c478bd9Sstevel@tonic-gate case EOPNOTSUPP: 2557c478bd9Sstevel@tonic-gate return (NFS4ERR_NOTSUPP); 2567c478bd9Sstevel@tonic-gate case ESTALE: 2577c478bd9Sstevel@tonic-gate return (NFS4ERR_STALE); 2587c478bd9Sstevel@tonic-gate case EAGAIN: 2597c478bd9Sstevel@tonic-gate if (curthread->t_flag & T_WOULDBLOCK) { 2607c478bd9Sstevel@tonic-gate curthread->t_flag &= ~T_WOULDBLOCK; 2617c478bd9Sstevel@tonic-gate return (NFS4ERR_DELAY); 2627c478bd9Sstevel@tonic-gate } 2637c478bd9Sstevel@tonic-gate return (NFS4ERR_LOCKED); 2647c478bd9Sstevel@tonic-gate default: 2657c478bd9Sstevel@tonic-gate return ((enum nfsstat4)error); 2667c478bd9Sstevel@tonic-gate } 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate int 2707c478bd9Sstevel@tonic-gate geterrno4(enum nfsstat4 status) 2717c478bd9Sstevel@tonic-gate { 2727c478bd9Sstevel@tonic-gate switch (status) { 2737c478bd9Sstevel@tonic-gate case NFS4_OK: 2747c478bd9Sstevel@tonic-gate return (0); 2757c478bd9Sstevel@tonic-gate case NFS4ERR_PERM: 2767c478bd9Sstevel@tonic-gate return (EPERM); 2777c478bd9Sstevel@tonic-gate case NFS4ERR_NOENT: 2787c478bd9Sstevel@tonic-gate return (ENOENT); 2797c478bd9Sstevel@tonic-gate case NFS4ERR_IO: 2807c478bd9Sstevel@tonic-gate return (EIO); 2817c478bd9Sstevel@tonic-gate case NFS4ERR_NXIO: 2827c478bd9Sstevel@tonic-gate return (ENXIO); 2837c478bd9Sstevel@tonic-gate case NFS4ERR_ACCESS: 2847c478bd9Sstevel@tonic-gate return (EACCES); 2857c478bd9Sstevel@tonic-gate case NFS4ERR_EXIST: 2867c478bd9Sstevel@tonic-gate return (EEXIST); 2877c478bd9Sstevel@tonic-gate case NFS4ERR_XDEV: 2887c478bd9Sstevel@tonic-gate return (EXDEV); 2897c478bd9Sstevel@tonic-gate case NFS4ERR_NOTDIR: 2907c478bd9Sstevel@tonic-gate return (ENOTDIR); 2917c478bd9Sstevel@tonic-gate case NFS4ERR_ISDIR: 2927c478bd9Sstevel@tonic-gate return (EISDIR); 2937c478bd9Sstevel@tonic-gate case NFS4ERR_INVAL: 2947c478bd9Sstevel@tonic-gate return (EINVAL); 2957c478bd9Sstevel@tonic-gate case NFS4ERR_FBIG: 2967c478bd9Sstevel@tonic-gate return (EFBIG); 2977c478bd9Sstevel@tonic-gate case NFS4ERR_NOSPC: 2987c478bd9Sstevel@tonic-gate return (ENOSPC); 2997c478bd9Sstevel@tonic-gate case NFS4ERR_ROFS: 3007c478bd9Sstevel@tonic-gate return (EROFS); 3017c478bd9Sstevel@tonic-gate case NFS4ERR_MLINK: 3027c478bd9Sstevel@tonic-gate return (EMLINK); 3037c478bd9Sstevel@tonic-gate case NFS4ERR_NAMETOOLONG: 3047c478bd9Sstevel@tonic-gate return (ENAMETOOLONG); 3057c478bd9Sstevel@tonic-gate case NFS4ERR_NOTEMPTY: 3067c478bd9Sstevel@tonic-gate return (ENOTEMPTY); 3077c478bd9Sstevel@tonic-gate case NFS4ERR_DQUOT: 3087c478bd9Sstevel@tonic-gate return (EDQUOT); 3097c478bd9Sstevel@tonic-gate case NFS4ERR_STALE: 3107c478bd9Sstevel@tonic-gate return (ESTALE); 3117c478bd9Sstevel@tonic-gate case NFS4ERR_BADHANDLE: 3127c478bd9Sstevel@tonic-gate return (ESTALE); 3137c478bd9Sstevel@tonic-gate case NFS4ERR_BAD_COOKIE: 3147c478bd9Sstevel@tonic-gate return (EINVAL); 3157c478bd9Sstevel@tonic-gate case NFS4ERR_NOTSUPP: 3167c478bd9Sstevel@tonic-gate return (EOPNOTSUPP); 3177c478bd9Sstevel@tonic-gate case NFS4ERR_TOOSMALL: 3187c478bd9Sstevel@tonic-gate return (EINVAL); 3197c478bd9Sstevel@tonic-gate case NFS4ERR_SERVERFAULT: 3207c478bd9Sstevel@tonic-gate return (EIO); 3217c478bd9Sstevel@tonic-gate case NFS4ERR_BADTYPE: 3227c478bd9Sstevel@tonic-gate return (EINVAL); 3237c478bd9Sstevel@tonic-gate case NFS4ERR_DELAY: 3247c478bd9Sstevel@tonic-gate return (ENXIO); 3257c478bd9Sstevel@tonic-gate case NFS4ERR_SAME: 3267c478bd9Sstevel@tonic-gate return (EPROTO); 3277c478bd9Sstevel@tonic-gate case NFS4ERR_DENIED: 3287c478bd9Sstevel@tonic-gate return (ENOLCK); 3297c478bd9Sstevel@tonic-gate case NFS4ERR_EXPIRED: 3307c478bd9Sstevel@tonic-gate return (EPROTO); 3317c478bd9Sstevel@tonic-gate case NFS4ERR_LOCKED: 3327c478bd9Sstevel@tonic-gate return (EACCES); 3337c478bd9Sstevel@tonic-gate case NFS4ERR_GRACE: 3347c478bd9Sstevel@tonic-gate return (EAGAIN); 3357c478bd9Sstevel@tonic-gate case NFS4ERR_FHEXPIRED: /* if got here, failed to get a new fh */ 3367c478bd9Sstevel@tonic-gate return (ESTALE); 3377c478bd9Sstevel@tonic-gate case NFS4ERR_SHARE_DENIED: 3387c478bd9Sstevel@tonic-gate return (EACCES); 3397c478bd9Sstevel@tonic-gate case NFS4ERR_WRONGSEC: 3407c478bd9Sstevel@tonic-gate return (EPERM); 3417c478bd9Sstevel@tonic-gate case NFS4ERR_CLID_INUSE: 3427c478bd9Sstevel@tonic-gate return (EAGAIN); 3437c478bd9Sstevel@tonic-gate case NFS4ERR_RESOURCE: 3447c478bd9Sstevel@tonic-gate return (EAGAIN); 3457c478bd9Sstevel@tonic-gate case NFS4ERR_MOVED: 3467c478bd9Sstevel@tonic-gate return (EPROTO); 3477c478bd9Sstevel@tonic-gate case NFS4ERR_NOFILEHANDLE: 3487c478bd9Sstevel@tonic-gate return (EIO); 3497c478bd9Sstevel@tonic-gate case NFS4ERR_MINOR_VERS_MISMATCH: 3507c478bd9Sstevel@tonic-gate return (ENOTSUP); 3517c478bd9Sstevel@tonic-gate case NFS4ERR_STALE_CLIENTID: 3527c478bd9Sstevel@tonic-gate return (EIO); 3537c478bd9Sstevel@tonic-gate case NFS4ERR_STALE_STATEID: 3547c478bd9Sstevel@tonic-gate return (EIO); 3557c478bd9Sstevel@tonic-gate case NFS4ERR_OLD_STATEID: 3567c478bd9Sstevel@tonic-gate return (EIO); 3577c478bd9Sstevel@tonic-gate case NFS4ERR_BAD_STATEID: 3587c478bd9Sstevel@tonic-gate return (EIO); 3597c478bd9Sstevel@tonic-gate case NFS4ERR_BAD_SEQID: 3607c478bd9Sstevel@tonic-gate return (EIO); 3617c478bd9Sstevel@tonic-gate case NFS4ERR_NOT_SAME: 3627c478bd9Sstevel@tonic-gate return (EPROTO); 3637c478bd9Sstevel@tonic-gate case NFS4ERR_LOCK_RANGE: 3647c478bd9Sstevel@tonic-gate return (EPROTO); 3657c478bd9Sstevel@tonic-gate case NFS4ERR_SYMLINK: 3667c478bd9Sstevel@tonic-gate return (EPROTO); 3677c478bd9Sstevel@tonic-gate case NFS4ERR_RESTOREFH: 3687c478bd9Sstevel@tonic-gate return (EPROTO); 3697c478bd9Sstevel@tonic-gate case NFS4ERR_LEASE_MOVED: 3707c478bd9Sstevel@tonic-gate return (EPROTO); 3717c478bd9Sstevel@tonic-gate case NFS4ERR_ATTRNOTSUPP: 3727c478bd9Sstevel@tonic-gate return (ENOTSUP); 3737c478bd9Sstevel@tonic-gate case NFS4ERR_NO_GRACE: 3747c478bd9Sstevel@tonic-gate return (EPROTO); 3757c478bd9Sstevel@tonic-gate case NFS4ERR_RECLAIM_BAD: 3767c478bd9Sstevel@tonic-gate return (EPROTO); 3777c478bd9Sstevel@tonic-gate case NFS4ERR_RECLAIM_CONFLICT: 3787c478bd9Sstevel@tonic-gate return (EPROTO); 3797c478bd9Sstevel@tonic-gate case NFS4ERR_BADXDR: 3807c478bd9Sstevel@tonic-gate return (EINVAL); 3817c478bd9Sstevel@tonic-gate case NFS4ERR_LOCKS_HELD: 3827c478bd9Sstevel@tonic-gate return (EIO); 3837c478bd9Sstevel@tonic-gate case NFS4ERR_OPENMODE: 3847c478bd9Sstevel@tonic-gate return (EACCES); 3857c478bd9Sstevel@tonic-gate case NFS4ERR_BADOWNER: 3867c478bd9Sstevel@tonic-gate /* 3877c478bd9Sstevel@tonic-gate * Client and server are in different DNS domains 3887c478bd9Sstevel@tonic-gate * and the NFSMAPID_DOMAIN in /etc/default/nfs 3897c478bd9Sstevel@tonic-gate * doesn't match. No good answer here. Return 3907c478bd9Sstevel@tonic-gate * EACCESS, which translates to "permission denied". 3917c478bd9Sstevel@tonic-gate */ 3927c478bd9Sstevel@tonic-gate return (EACCES); 3937c478bd9Sstevel@tonic-gate case NFS4ERR_BADCHAR: 3947c478bd9Sstevel@tonic-gate return (EINVAL); 3957c478bd9Sstevel@tonic-gate case NFS4ERR_BADNAME: 3967c478bd9Sstevel@tonic-gate return (EINVAL); 3977c478bd9Sstevel@tonic-gate case NFS4ERR_BAD_RANGE: 3987c478bd9Sstevel@tonic-gate return (EIO); 3997c478bd9Sstevel@tonic-gate case NFS4ERR_LOCK_NOTSUPP: 4007c478bd9Sstevel@tonic-gate return (ENOTSUP); 4017c478bd9Sstevel@tonic-gate case NFS4ERR_OP_ILLEGAL: 4027c478bd9Sstevel@tonic-gate return (EINVAL); 4037c478bd9Sstevel@tonic-gate case NFS4ERR_DEADLOCK: 4047c478bd9Sstevel@tonic-gate return (EDEADLK); 4057c478bd9Sstevel@tonic-gate case NFS4ERR_FILE_OPEN: 4067c478bd9Sstevel@tonic-gate return (EACCES); 4077c478bd9Sstevel@tonic-gate case NFS4ERR_ADMIN_REVOKED: 4087c478bd9Sstevel@tonic-gate return (EPROTO); 4097c478bd9Sstevel@tonic-gate case NFS4ERR_CB_PATH_DOWN: 4107c478bd9Sstevel@tonic-gate return (EPROTO); 4117c478bd9Sstevel@tonic-gate default: 4127c478bd9Sstevel@tonic-gate #ifdef DEBUG 4137c478bd9Sstevel@tonic-gate zcmn_err(getzoneid(), CE_WARN, "geterrno4: got status %d", 4147c478bd9Sstevel@tonic-gate status); 4157c478bd9Sstevel@tonic-gate #endif 4167c478bd9Sstevel@tonic-gate return ((int)status); 4177c478bd9Sstevel@tonic-gate } 4187c478bd9Sstevel@tonic-gate } 4197c478bd9Sstevel@tonic-gate 4207c478bd9Sstevel@tonic-gate void 4217c478bd9Sstevel@tonic-gate nfs4_log_badowner(mntinfo4_t *mi, nfs_opnum4 op) 4227c478bd9Sstevel@tonic-gate { 4237c478bd9Sstevel@tonic-gate nfs4_server_t *server; 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate /* 4267c478bd9Sstevel@tonic-gate * Return if already printed/queued a msg 4277c478bd9Sstevel@tonic-gate * for this mount point. 4287c478bd9Sstevel@tonic-gate */ 4297c478bd9Sstevel@tonic-gate if (mi->mi_flags & MI4_BADOWNER_DEBUG) 4307c478bd9Sstevel@tonic-gate return; 4317c478bd9Sstevel@tonic-gate /* 4327c478bd9Sstevel@tonic-gate * Happens once per client <-> server pair. 4337c478bd9Sstevel@tonic-gate */ 4347c478bd9Sstevel@tonic-gate if (nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 4357c478bd9Sstevel@tonic-gate mi->mi_flags & MI4_INT)) 4367c478bd9Sstevel@tonic-gate return; 4377c478bd9Sstevel@tonic-gate 4387c478bd9Sstevel@tonic-gate server = find_nfs4_server(mi); 4397c478bd9Sstevel@tonic-gate if (server == NULL) { 4407c478bd9Sstevel@tonic-gate nfs_rw_exit(&mi->mi_recovlock); 4417c478bd9Sstevel@tonic-gate return; 4427c478bd9Sstevel@tonic-gate } 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate if (!(server->s_flags & N4S_BADOWNER_DEBUG)) { 4457c478bd9Sstevel@tonic-gate zcmn_err(mi->mi_zone->zone_id, CE_WARN, 4467c478bd9Sstevel@tonic-gate "!NFSMAPID_DOMAIN does not match" 4477c478bd9Sstevel@tonic-gate " the server: %s domain.\n" 4487c478bd9Sstevel@tonic-gate "Please check configuration", 4497c478bd9Sstevel@tonic-gate mi->mi_curr_serv->sv_hostname); 4507c478bd9Sstevel@tonic-gate server->s_flags |= N4S_BADOWNER_DEBUG; 4517c478bd9Sstevel@tonic-gate } 4527c478bd9Sstevel@tonic-gate mutex_exit(&server->s_lock); 4537c478bd9Sstevel@tonic-gate nfs4_server_rele(server); 4547c478bd9Sstevel@tonic-gate nfs_rw_exit(&mi->mi_recovlock); 4557c478bd9Sstevel@tonic-gate 4567c478bd9Sstevel@tonic-gate /* 4577c478bd9Sstevel@tonic-gate * Happens once per mntinfo4_t. 4587c478bd9Sstevel@tonic-gate * This error is deemed as one of the recovery facts "RF_BADOWNER", 4597c478bd9Sstevel@tonic-gate * queue this in the mesg queue for this mount_info. This message 4607c478bd9Sstevel@tonic-gate * is not printed, meaning its absent from id_to_dump_solo_fact() 4617c478bd9Sstevel@tonic-gate * but its there for inspection if the queue is ever dumped/inspected. 4627c478bd9Sstevel@tonic-gate */ 4637c478bd9Sstevel@tonic-gate mutex_enter(&mi->mi_lock); 4647c478bd9Sstevel@tonic-gate if (!(mi->mi_flags & MI4_BADOWNER_DEBUG)) { 4657c478bd9Sstevel@tonic-gate nfs4_queue_fact(RF_BADOWNER, mi, NFS4ERR_BADOWNER, 0, op, 4667c478bd9Sstevel@tonic-gate FALSE, NULL, 0, NULL); 4677c478bd9Sstevel@tonic-gate mi->mi_flags |= MI4_BADOWNER_DEBUG; 4687c478bd9Sstevel@tonic-gate } 4697c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 4707c478bd9Sstevel@tonic-gate } 4717c478bd9Sstevel@tonic-gate 4727c478bd9Sstevel@tonic-gate 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate int 4757c478bd9Sstevel@tonic-gate nfs4_time_ntov(nfstime4 *ntime, timestruc_t *vatime) 4767c478bd9Sstevel@tonic-gate { 4777c478bd9Sstevel@tonic-gate int64_t sec; 4787c478bd9Sstevel@tonic-gate int32_t nsec; 4797c478bd9Sstevel@tonic-gate 4807c478bd9Sstevel@tonic-gate /* 4817c478bd9Sstevel@tonic-gate * Here check that the nfsv4 time is valid for the system. 4827c478bd9Sstevel@tonic-gate * nfsv4 time value is a signed 64-bit, and the system time 4837c478bd9Sstevel@tonic-gate * may be either int64_t or int32_t (depends on the kernel), 4847c478bd9Sstevel@tonic-gate * so if the kernel is 32-bit, the nfsv4 time value may not fit. 4857c478bd9Sstevel@tonic-gate */ 4867c478bd9Sstevel@tonic-gate #ifndef _LP64 4877c478bd9Sstevel@tonic-gate if (! NFS4_TIME_OK(ntime->seconds)) { 4887c478bd9Sstevel@tonic-gate return (EOVERFLOW); 4897c478bd9Sstevel@tonic-gate } 4907c478bd9Sstevel@tonic-gate #endif 4917c478bd9Sstevel@tonic-gate 492*9720e166Sjasmith /* Invalid to specify 1 billion (or more) nsecs */ 493*9720e166Sjasmith if (ntime->nseconds >= 1000000000) 494*9720e166Sjasmith return (EINVAL); 495*9720e166Sjasmith 4967c478bd9Sstevel@tonic-gate if (ntime->seconds < 0) { 4977c478bd9Sstevel@tonic-gate sec = ntime->seconds + 1; 4987c478bd9Sstevel@tonic-gate nsec = -1000000000 + ntime->nseconds; 4997c478bd9Sstevel@tonic-gate } else { 5007c478bd9Sstevel@tonic-gate sec = ntime->seconds; 5017c478bd9Sstevel@tonic-gate nsec = ntime->nseconds; 5027c478bd9Sstevel@tonic-gate } 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate vatime->tv_sec = sec; 5057c478bd9Sstevel@tonic-gate vatime->tv_nsec = nsec; 5067c478bd9Sstevel@tonic-gate 5077c478bd9Sstevel@tonic-gate return (0); 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate int 5117c478bd9Sstevel@tonic-gate nfs4_time_vton(timestruc_t *vatime, nfstime4 *ntime) 5127c478bd9Sstevel@tonic-gate { 5137c478bd9Sstevel@tonic-gate int64_t sec; 5147c478bd9Sstevel@tonic-gate uint32_t nsec; 5157c478bd9Sstevel@tonic-gate 5167c478bd9Sstevel@tonic-gate /* 5177c478bd9Sstevel@tonic-gate * nfsv4 time value is a signed 64-bit, and the system time 5187c478bd9Sstevel@tonic-gate * may be either int64_t or int32_t (depends on the kernel), 5197c478bd9Sstevel@tonic-gate * so all system time values will fit. 5207c478bd9Sstevel@tonic-gate */ 5217c478bd9Sstevel@tonic-gate if (vatime->tv_nsec >= 0) { 5227c478bd9Sstevel@tonic-gate sec = vatime->tv_sec; 5237c478bd9Sstevel@tonic-gate nsec = vatime->tv_nsec; 5247c478bd9Sstevel@tonic-gate } else { 5257c478bd9Sstevel@tonic-gate sec = vatime->tv_sec - 1; 5267c478bd9Sstevel@tonic-gate nsec = 1000000000 + vatime->tv_nsec; 5277c478bd9Sstevel@tonic-gate } 5287c478bd9Sstevel@tonic-gate ntime->seconds = sec; 5297c478bd9Sstevel@tonic-gate ntime->nseconds = nsec; 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate return (0); 5327c478bd9Sstevel@tonic-gate } 5337c478bd9Sstevel@tonic-gate 5347c478bd9Sstevel@tonic-gate /* 5357c478bd9Sstevel@tonic-gate * Converts a utf8 string to a valid null terminated filename string. 5367c478bd9Sstevel@tonic-gate * 5377c478bd9Sstevel@tonic-gate * XXX - Not actually translating the UTF-8 string as per RFC 2279. 5387c478bd9Sstevel@tonic-gate * For now, just validate that the UTF-8 string off the wire 5397c478bd9Sstevel@tonic-gate * does not have characters that will freak out UFS, and leave 5407c478bd9Sstevel@tonic-gate * it at that. 5417c478bd9Sstevel@tonic-gate */ 5427c478bd9Sstevel@tonic-gate char * 5437c478bd9Sstevel@tonic-gate utf8_to_fn(utf8string *u8s, uint_t *lenp, char *s) 5447c478bd9Sstevel@tonic-gate { 5457c478bd9Sstevel@tonic-gate ASSERT(lenp != NULL); 5467c478bd9Sstevel@tonic-gate 5477c478bd9Sstevel@tonic-gate if (u8s == NULL || u8s->utf8string_len <= 0 || 5487c478bd9Sstevel@tonic-gate u8s->utf8string_val == NULL) 5497c478bd9Sstevel@tonic-gate return (NULL); 5507c478bd9Sstevel@tonic-gate 5517c478bd9Sstevel@tonic-gate /* 5527c478bd9Sstevel@tonic-gate * Check for obvious illegal filename chars 5537c478bd9Sstevel@tonic-gate */ 5547c478bd9Sstevel@tonic-gate if (utf8_strchr(u8s, '/') != NULL) { 5557c478bd9Sstevel@tonic-gate #ifdef DEBUG 5567c478bd9Sstevel@tonic-gate if (nfs4_utf8_debug) { 5577c478bd9Sstevel@tonic-gate char *path; 5587c478bd9Sstevel@tonic-gate int len = u8s->utf8string_len; 5597c478bd9Sstevel@tonic-gate 5607c478bd9Sstevel@tonic-gate path = kmem_alloc(len + 1, KM_SLEEP); 5617c478bd9Sstevel@tonic-gate bcopy(u8s->utf8string_val, path, len); 5627c478bd9Sstevel@tonic-gate path[len] = '\0'; 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate zcmn_err(getzoneid(), CE_WARN, 5657c478bd9Sstevel@tonic-gate "Invalid UTF-8 filename: %s", path); 5667c478bd9Sstevel@tonic-gate 5677c478bd9Sstevel@tonic-gate kmem_free(path, len + 1); 5687c478bd9Sstevel@tonic-gate } 5697c478bd9Sstevel@tonic-gate #endif 5707c478bd9Sstevel@tonic-gate return (NULL); 5717c478bd9Sstevel@tonic-gate } 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate return (utf8_to_str(u8s, lenp, s)); 5747c478bd9Sstevel@tonic-gate } 5757c478bd9Sstevel@tonic-gate 5767c478bd9Sstevel@tonic-gate /* 5777c478bd9Sstevel@tonic-gate * Converts a utf8 string to a C string. 5787c478bd9Sstevel@tonic-gate * kmem_allocs a new string if not supplied 5797c478bd9Sstevel@tonic-gate */ 5807c478bd9Sstevel@tonic-gate char * 5817c478bd9Sstevel@tonic-gate utf8_to_str(utf8string *str, uint_t *lenp, char *s) 5827c478bd9Sstevel@tonic-gate { 5837c478bd9Sstevel@tonic-gate char *sp; 5847c478bd9Sstevel@tonic-gate char *u8p; 5857c478bd9Sstevel@tonic-gate int len; 5867c478bd9Sstevel@tonic-gate int i; 5877c478bd9Sstevel@tonic-gate 5887c478bd9Sstevel@tonic-gate ASSERT(lenp != NULL); 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate if (str == NULL) 5917c478bd9Sstevel@tonic-gate return (NULL); 5927c478bd9Sstevel@tonic-gate 5937c478bd9Sstevel@tonic-gate u8p = str->utf8string_val; 5947c478bd9Sstevel@tonic-gate len = str->utf8string_len; 5957c478bd9Sstevel@tonic-gate if (len <= 0 || u8p == NULL) { 5967c478bd9Sstevel@tonic-gate if (s) 5977c478bd9Sstevel@tonic-gate *s = '\0'; 5987c478bd9Sstevel@tonic-gate return (NULL); 5997c478bd9Sstevel@tonic-gate } 6007c478bd9Sstevel@tonic-gate 6017c478bd9Sstevel@tonic-gate sp = s; 6027c478bd9Sstevel@tonic-gate if (sp == NULL) 6037c478bd9Sstevel@tonic-gate sp = kmem_alloc(len + 1, KM_SLEEP); 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate /* 6067c478bd9Sstevel@tonic-gate * At least check for embedded nulls 6077c478bd9Sstevel@tonic-gate */ 6087c478bd9Sstevel@tonic-gate for (i = 0; i < len; i++) { 6097c478bd9Sstevel@tonic-gate sp[i] = u8p[i]; 6107c478bd9Sstevel@tonic-gate if (u8p[i] == '\0') { 6117c478bd9Sstevel@tonic-gate #ifdef DEBUG 6127c478bd9Sstevel@tonic-gate zcmn_err(getzoneid(), CE_WARN, 6137c478bd9Sstevel@tonic-gate "Embedded NULL in UTF-8 string"); 6147c478bd9Sstevel@tonic-gate #endif 6157c478bd9Sstevel@tonic-gate if (s == NULL) 6167c478bd9Sstevel@tonic-gate kmem_free(sp, len + 1); 6177c478bd9Sstevel@tonic-gate return (NULL); 6187c478bd9Sstevel@tonic-gate } 6197c478bd9Sstevel@tonic-gate } 6207c478bd9Sstevel@tonic-gate sp[len] = '\0'; 6217c478bd9Sstevel@tonic-gate *lenp = len + 1; 6227c478bd9Sstevel@tonic-gate 6237c478bd9Sstevel@tonic-gate return (sp); 6247c478bd9Sstevel@tonic-gate } 6257c478bd9Sstevel@tonic-gate 6267c478bd9Sstevel@tonic-gate /* 6277c478bd9Sstevel@tonic-gate * str_to_utf8 - converts a null-terminated C string to a utf8 string 6287c478bd9Sstevel@tonic-gate */ 6297c478bd9Sstevel@tonic-gate utf8string * 6307c478bd9Sstevel@tonic-gate str_to_utf8(char *nm, utf8string *str) 6317c478bd9Sstevel@tonic-gate { 6327c478bd9Sstevel@tonic-gate int len; 6337c478bd9Sstevel@tonic-gate 6347c478bd9Sstevel@tonic-gate if (str == NULL) 6357c478bd9Sstevel@tonic-gate return (NULL); 6367c478bd9Sstevel@tonic-gate 6377c478bd9Sstevel@tonic-gate if (nm == NULL || *nm == '\0') { 6387c478bd9Sstevel@tonic-gate str->utf8string_len = 0; 6397c478bd9Sstevel@tonic-gate str->utf8string_val = NULL; 6407c478bd9Sstevel@tonic-gate } 6417c478bd9Sstevel@tonic-gate 6427c478bd9Sstevel@tonic-gate len = strlen(nm); 6437c478bd9Sstevel@tonic-gate 6447c478bd9Sstevel@tonic-gate str->utf8string_val = kmem_alloc(len, KM_SLEEP); 6457c478bd9Sstevel@tonic-gate str->utf8string_len = len; 6467c478bd9Sstevel@tonic-gate bcopy(nm, str->utf8string_val, len); 6477c478bd9Sstevel@tonic-gate 6487c478bd9Sstevel@tonic-gate return (str); 6497c478bd9Sstevel@tonic-gate } 6507c478bd9Sstevel@tonic-gate 6517c478bd9Sstevel@tonic-gate utf8string * 6527c478bd9Sstevel@tonic-gate utf8_copy(utf8string *src, utf8string *dest) 6537c478bd9Sstevel@tonic-gate { 6547c478bd9Sstevel@tonic-gate if (src == NULL) 6557c478bd9Sstevel@tonic-gate return (NULL); 6567c478bd9Sstevel@tonic-gate if (dest == NULL) 6577c478bd9Sstevel@tonic-gate return (NULL); 6587c478bd9Sstevel@tonic-gate 6597c478bd9Sstevel@tonic-gate if (src->utf8string_len > 0) { 6607c478bd9Sstevel@tonic-gate dest->utf8string_val = kmem_alloc(src->utf8string_len, 6617c478bd9Sstevel@tonic-gate KM_SLEEP); 6627c478bd9Sstevel@tonic-gate bcopy(src->utf8string_val, dest->utf8string_val, 6637c478bd9Sstevel@tonic-gate src->utf8string_len); 6647c478bd9Sstevel@tonic-gate dest->utf8string_len = src->utf8string_len; 6657c478bd9Sstevel@tonic-gate } else { 6667c478bd9Sstevel@tonic-gate dest->utf8string_val = NULL; 6677c478bd9Sstevel@tonic-gate dest->utf8string_len = 0; 6687c478bd9Sstevel@tonic-gate } 6697c478bd9Sstevel@tonic-gate 6707c478bd9Sstevel@tonic-gate return (dest); 6717c478bd9Sstevel@tonic-gate } 6727c478bd9Sstevel@tonic-gate 6737c478bd9Sstevel@tonic-gate int 6747c478bd9Sstevel@tonic-gate utf8_compare(const utf8string *a, const utf8string *b) 6757c478bd9Sstevel@tonic-gate { 6767c478bd9Sstevel@tonic-gate int mlen, cmp; 6777c478bd9Sstevel@tonic-gate int alen, blen; 6787c478bd9Sstevel@tonic-gate char *aval, *bval; 6797c478bd9Sstevel@tonic-gate 6807c478bd9Sstevel@tonic-gate if ((a == NULL) && (b == NULL)) 6817c478bd9Sstevel@tonic-gate return (0); 6827c478bd9Sstevel@tonic-gate else if (a == NULL) 6837c478bd9Sstevel@tonic-gate return (-1); 6847c478bd9Sstevel@tonic-gate else if (b == NULL) 6857c478bd9Sstevel@tonic-gate return (1); 6867c478bd9Sstevel@tonic-gate 6877c478bd9Sstevel@tonic-gate alen = a->utf8string_len; 6887c478bd9Sstevel@tonic-gate blen = b->utf8string_len; 6897c478bd9Sstevel@tonic-gate aval = a->utf8string_val; 6907c478bd9Sstevel@tonic-gate bval = b->utf8string_val; 6917c478bd9Sstevel@tonic-gate 6927c478bd9Sstevel@tonic-gate if (((alen == 0) || (aval == NULL)) && 6937c478bd9Sstevel@tonic-gate ((blen == 0) || (bval == NULL))) 6947c478bd9Sstevel@tonic-gate return (0); 6957c478bd9Sstevel@tonic-gate else if ((alen == 0) || (aval == NULL)) 6967c478bd9Sstevel@tonic-gate return (-1); 6977c478bd9Sstevel@tonic-gate else if ((blen == 0) || (bval == NULL)) 6987c478bd9Sstevel@tonic-gate return (1); 6997c478bd9Sstevel@tonic-gate 7007c478bd9Sstevel@tonic-gate mlen = MIN(alen, blen); 7017c478bd9Sstevel@tonic-gate cmp = strncmp(aval, bval, mlen); 7027c478bd9Sstevel@tonic-gate 7037c478bd9Sstevel@tonic-gate if ((cmp == 0) && (alen == blen)) 7047c478bd9Sstevel@tonic-gate return (0); 7057c478bd9Sstevel@tonic-gate else if ((cmp == 0) && (alen < blen)) 7067c478bd9Sstevel@tonic-gate return (-1); 7077c478bd9Sstevel@tonic-gate else if (cmp == 0) 7087c478bd9Sstevel@tonic-gate return (1); 7097c478bd9Sstevel@tonic-gate else if (cmp < 0) 7107c478bd9Sstevel@tonic-gate return (-1); 7117c478bd9Sstevel@tonic-gate return (1); 7127c478bd9Sstevel@tonic-gate } 7137c478bd9Sstevel@tonic-gate 7147c478bd9Sstevel@tonic-gate /* 7157c478bd9Sstevel@tonic-gate * utf8_dir_verify - checks that the utf8 string is valid 7167c478bd9Sstevel@tonic-gate */ 7177c478bd9Sstevel@tonic-gate int 7187c478bd9Sstevel@tonic-gate utf8_dir_verify(utf8string *str) 7197c478bd9Sstevel@tonic-gate { 7207c478bd9Sstevel@tonic-gate char *nm; 7217c478bd9Sstevel@tonic-gate int len; 7227c478bd9Sstevel@tonic-gate 7237c478bd9Sstevel@tonic-gate if (str == NULL) 7247c478bd9Sstevel@tonic-gate return (0); 7257c478bd9Sstevel@tonic-gate 7267c478bd9Sstevel@tonic-gate nm = str->utf8string_val; 7277c478bd9Sstevel@tonic-gate len = str->utf8string_len; 7287c478bd9Sstevel@tonic-gate if (nm == NULL || len == 0) { 7297c478bd9Sstevel@tonic-gate return (0); 7307c478bd9Sstevel@tonic-gate } 7317c478bd9Sstevel@tonic-gate 7327c478bd9Sstevel@tonic-gate if (len == 1 && nm[0] == '.') 7337c478bd9Sstevel@tonic-gate return (0); 7347c478bd9Sstevel@tonic-gate if (len == 2 && nm[0] == '.' && nm[1] == '.') 7357c478bd9Sstevel@tonic-gate return (0); 7367c478bd9Sstevel@tonic-gate 7377c478bd9Sstevel@tonic-gate if (utf8_strchr(str, '/') != NULL) 7387c478bd9Sstevel@tonic-gate return (0); 7397c478bd9Sstevel@tonic-gate 7407c478bd9Sstevel@tonic-gate if (utf8_strchr(str, '\0') != NULL) 7417c478bd9Sstevel@tonic-gate return (0); 7427c478bd9Sstevel@tonic-gate 7437c478bd9Sstevel@tonic-gate return (1); 7447c478bd9Sstevel@tonic-gate } 7457c478bd9Sstevel@tonic-gate 7467c478bd9Sstevel@tonic-gate /* 7477c478bd9Sstevel@tonic-gate * from rpcsec module (common/rpcsec) 7487c478bd9Sstevel@tonic-gate */ 7497c478bd9Sstevel@tonic-gate extern int sec_clnt_geth(CLIENT *, struct sec_data *, cred_t *, AUTH **); 7507c478bd9Sstevel@tonic-gate extern void sec_clnt_freeh(AUTH *); 7517c478bd9Sstevel@tonic-gate extern void sec_clnt_freeinfo(struct sec_data *); 7527c478bd9Sstevel@tonic-gate 7537c478bd9Sstevel@tonic-gate /* 7547c478bd9Sstevel@tonic-gate * authget() gets an auth handle based on the security 7557c478bd9Sstevel@tonic-gate * information from the servinfo in mountinfo. 7567c478bd9Sstevel@tonic-gate * The auth handle is stored in ch_client->cl_auth. 7577c478bd9Sstevel@tonic-gate * 7587c478bd9Sstevel@tonic-gate * First security flavor of choice is to use sv_secdata 7597c478bd9Sstevel@tonic-gate * which is initiated by the client. If that fails, get 7607c478bd9Sstevel@tonic-gate * secinfo from the server and then select one from the 7617c478bd9Sstevel@tonic-gate * server secinfo list . 7627c478bd9Sstevel@tonic-gate * 7637c478bd9Sstevel@tonic-gate * For RPCSEC_GSS flavor, upon success, a secure context is 7647c478bd9Sstevel@tonic-gate * established between client and server. 7657c478bd9Sstevel@tonic-gate */ 7667c478bd9Sstevel@tonic-gate int 7677c478bd9Sstevel@tonic-gate authget(servinfo4_t *svp, CLIENT *ch_client, cred_t *cr) 7687c478bd9Sstevel@tonic-gate { 7697c478bd9Sstevel@tonic-gate int error, i; 7707c478bd9Sstevel@tonic-gate 7717c478bd9Sstevel@tonic-gate /* 7727c478bd9Sstevel@tonic-gate * SV4_TRYSECINFO indicates to try the secinfo list from 7737c478bd9Sstevel@tonic-gate * sv_secinfo until a successful one is reached. Point 7747c478bd9Sstevel@tonic-gate * sv_currsec to the selected security mechanism for 7757c478bd9Sstevel@tonic-gate * later sessions. 7767c478bd9Sstevel@tonic-gate */ 7777c478bd9Sstevel@tonic-gate (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0); 7787c478bd9Sstevel@tonic-gate if ((svp->sv_flags & SV4_TRYSECINFO) && svp->sv_secinfo) { 7797c478bd9Sstevel@tonic-gate for (i = svp->sv_secinfo->index; i < svp->sv_secinfo->count; 7807c478bd9Sstevel@tonic-gate i++) { 7817c478bd9Sstevel@tonic-gate if (!(error = sec_clnt_geth(ch_client, 7827c478bd9Sstevel@tonic-gate &svp->sv_secinfo->sdata[i], 7837c478bd9Sstevel@tonic-gate cr, &ch_client->cl_auth))) { 7847c478bd9Sstevel@tonic-gate 7857c478bd9Sstevel@tonic-gate svp->sv_currsec = &svp->sv_secinfo->sdata[i]; 7867c478bd9Sstevel@tonic-gate svp->sv_secinfo->index = i; 7877c478bd9Sstevel@tonic-gate /* done */ 7887c478bd9Sstevel@tonic-gate svp->sv_flags &= ~SV4_TRYSECINFO; 7897c478bd9Sstevel@tonic-gate break; 7907c478bd9Sstevel@tonic-gate } 7917c478bd9Sstevel@tonic-gate 7927c478bd9Sstevel@tonic-gate /* 7937c478bd9Sstevel@tonic-gate * Allow the caller retry with the security flavor 7947c478bd9Sstevel@tonic-gate * pointed by svp->sv_secinfo->index when 7957c478bd9Sstevel@tonic-gate * ETIMEDOUT/ECONNRESET occurs. 7967c478bd9Sstevel@tonic-gate */ 7977c478bd9Sstevel@tonic-gate if (error == ETIMEDOUT || error == ECONNRESET) { 7987c478bd9Sstevel@tonic-gate svp->sv_secinfo->index = i; 7997c478bd9Sstevel@tonic-gate break; 8007c478bd9Sstevel@tonic-gate } 8017c478bd9Sstevel@tonic-gate } 8027c478bd9Sstevel@tonic-gate } else { 8037c478bd9Sstevel@tonic-gate /* sv_currsec points to one of the entries in sv_secinfo */ 8047c478bd9Sstevel@tonic-gate if (svp->sv_currsec) { 8057c478bd9Sstevel@tonic-gate error = sec_clnt_geth(ch_client, svp->sv_currsec, cr, 8067c478bd9Sstevel@tonic-gate &ch_client->cl_auth); 8077c478bd9Sstevel@tonic-gate } else { 8087c478bd9Sstevel@tonic-gate /* If it's null, use sv_secdata. */ 8097c478bd9Sstevel@tonic-gate error = sec_clnt_geth(ch_client, svp->sv_secdata, cr, 8107c478bd9Sstevel@tonic-gate &ch_client->cl_auth); 8117c478bd9Sstevel@tonic-gate } 8127c478bd9Sstevel@tonic-gate } 8137c478bd9Sstevel@tonic-gate nfs_rw_exit(&svp->sv_lock); 8147c478bd9Sstevel@tonic-gate 8157c478bd9Sstevel@tonic-gate return (error); 8167c478bd9Sstevel@tonic-gate } 8177c478bd9Sstevel@tonic-gate 8187c478bd9Sstevel@tonic-gate /* 8197c478bd9Sstevel@tonic-gate * Common handle get program for NFS, NFS ACL, and NFS AUTH client. 8207c478bd9Sstevel@tonic-gate */ 8217c478bd9Sstevel@tonic-gate int 8227c478bd9Sstevel@tonic-gate clget4(clinfo_t *ci, servinfo4_t *svp, cred_t *cr, CLIENT **newcl, 8237c478bd9Sstevel@tonic-gate struct chtab **chp, struct nfs4_clnt *nfscl) 8247c478bd9Sstevel@tonic-gate { 8257c478bd9Sstevel@tonic-gate struct chhead *ch, *newch; 8267c478bd9Sstevel@tonic-gate struct chhead **plistp; 8277c478bd9Sstevel@tonic-gate struct chtab *cp; 8287c478bd9Sstevel@tonic-gate int error; 8297c478bd9Sstevel@tonic-gate k_sigset_t smask; 8307c478bd9Sstevel@tonic-gate 8317c478bd9Sstevel@tonic-gate if (newcl == NULL || chp == NULL || ci == NULL) 8327c478bd9Sstevel@tonic-gate return (EINVAL); 8337c478bd9Sstevel@tonic-gate 8347c478bd9Sstevel@tonic-gate *newcl = NULL; 8357c478bd9Sstevel@tonic-gate *chp = NULL; 8367c478bd9Sstevel@tonic-gate 8377c478bd9Sstevel@tonic-gate /* 8387c478bd9Sstevel@tonic-gate * Find an unused handle or create one 8397c478bd9Sstevel@tonic-gate */ 8407c478bd9Sstevel@tonic-gate newch = NULL; 8417c478bd9Sstevel@tonic-gate nfscl->nfscl_stat.clgets.value.ui64++; 8427c478bd9Sstevel@tonic-gate top: 8437c478bd9Sstevel@tonic-gate /* 8447c478bd9Sstevel@tonic-gate * Find the correct entry in the cache to check for free 8457c478bd9Sstevel@tonic-gate * client handles. The search is based on the RPC program 8467c478bd9Sstevel@tonic-gate * number, program version number, dev_t for the transport 8477c478bd9Sstevel@tonic-gate * device, and the protocol family. 8487c478bd9Sstevel@tonic-gate */ 8497c478bd9Sstevel@tonic-gate mutex_enter(&nfscl->nfscl_chtable4_lock); 8507c478bd9Sstevel@tonic-gate plistp = &nfscl->nfscl_chtable4; 8517c478bd9Sstevel@tonic-gate for (ch = nfscl->nfscl_chtable4; ch != NULL; ch = ch->ch_next) { 8527c478bd9Sstevel@tonic-gate if (ch->ch_prog == ci->cl_prog && 8537c478bd9Sstevel@tonic-gate ch->ch_vers == ci->cl_vers && 8547c478bd9Sstevel@tonic-gate ch->ch_dev == svp->sv_knconf->knc_rdev && 8557c478bd9Sstevel@tonic-gate (strcmp(ch->ch_protofmly, 8567c478bd9Sstevel@tonic-gate svp->sv_knconf->knc_protofmly) == 0)) 8577c478bd9Sstevel@tonic-gate break; 8587c478bd9Sstevel@tonic-gate plistp = &ch->ch_next; 8597c478bd9Sstevel@tonic-gate } 8607c478bd9Sstevel@tonic-gate 8617c478bd9Sstevel@tonic-gate /* 8627c478bd9Sstevel@tonic-gate * If we didn't find a cache entry for this quadruple, then 8637c478bd9Sstevel@tonic-gate * create one. If we don't have one already preallocated, 8647c478bd9Sstevel@tonic-gate * then drop the cache lock, create one, and then start over. 8657c478bd9Sstevel@tonic-gate * If we did have a preallocated entry, then just add it to 8667c478bd9Sstevel@tonic-gate * the front of the list. 8677c478bd9Sstevel@tonic-gate */ 8687c478bd9Sstevel@tonic-gate if (ch == NULL) { 8697c478bd9Sstevel@tonic-gate if (newch == NULL) { 8707c478bd9Sstevel@tonic-gate mutex_exit(&nfscl->nfscl_chtable4_lock); 8717c478bd9Sstevel@tonic-gate newch = kmem_alloc(sizeof (*newch), KM_SLEEP); 8727c478bd9Sstevel@tonic-gate newch->ch_timesused = 0; 8737c478bd9Sstevel@tonic-gate newch->ch_prog = ci->cl_prog; 8747c478bd9Sstevel@tonic-gate newch->ch_vers = ci->cl_vers; 8757c478bd9Sstevel@tonic-gate newch->ch_dev = svp->sv_knconf->knc_rdev; 8767c478bd9Sstevel@tonic-gate newch->ch_protofmly = kmem_alloc( 8777c478bd9Sstevel@tonic-gate strlen(svp->sv_knconf->knc_protofmly) + 1, 8787c478bd9Sstevel@tonic-gate KM_SLEEP); 8797c478bd9Sstevel@tonic-gate (void) strcpy(newch->ch_protofmly, 8807c478bd9Sstevel@tonic-gate svp->sv_knconf->knc_protofmly); 8817c478bd9Sstevel@tonic-gate newch->ch_list = NULL; 8827c478bd9Sstevel@tonic-gate goto top; 8837c478bd9Sstevel@tonic-gate } 8847c478bd9Sstevel@tonic-gate ch = newch; 8857c478bd9Sstevel@tonic-gate newch = NULL; 8867c478bd9Sstevel@tonic-gate ch->ch_next = nfscl->nfscl_chtable4; 8877c478bd9Sstevel@tonic-gate nfscl->nfscl_chtable4 = ch; 8887c478bd9Sstevel@tonic-gate /* 8897c478bd9Sstevel@tonic-gate * We found a cache entry, but if it isn't on the front of the 8907c478bd9Sstevel@tonic-gate * list, then move it to the front of the list to try to take 8917c478bd9Sstevel@tonic-gate * advantage of locality of operations. 8927c478bd9Sstevel@tonic-gate */ 8937c478bd9Sstevel@tonic-gate } else if (ch != nfscl->nfscl_chtable4) { 8947c478bd9Sstevel@tonic-gate *plistp = ch->ch_next; 8957c478bd9Sstevel@tonic-gate ch->ch_next = nfscl->nfscl_chtable4; 8967c478bd9Sstevel@tonic-gate nfscl->nfscl_chtable4 = ch; 8977c478bd9Sstevel@tonic-gate } 8987c478bd9Sstevel@tonic-gate 8997c478bd9Sstevel@tonic-gate /* 9007c478bd9Sstevel@tonic-gate * If there was a free client handle cached, then remove it 9017c478bd9Sstevel@tonic-gate * from the list, init it, and use it. 9027c478bd9Sstevel@tonic-gate */ 9037c478bd9Sstevel@tonic-gate if (ch->ch_list != NULL) { 9047c478bd9Sstevel@tonic-gate cp = ch->ch_list; 9057c478bd9Sstevel@tonic-gate ch->ch_list = cp->ch_list; 9067c478bd9Sstevel@tonic-gate mutex_exit(&nfscl->nfscl_chtable4_lock); 9077c478bd9Sstevel@tonic-gate if (newch != NULL) { 9087c478bd9Sstevel@tonic-gate kmem_free(newch->ch_protofmly, 9097c478bd9Sstevel@tonic-gate strlen(newch->ch_protofmly) + 1); 9107c478bd9Sstevel@tonic-gate kmem_free(newch, sizeof (*newch)); 9117c478bd9Sstevel@tonic-gate } 9127c478bd9Sstevel@tonic-gate (void) clnt_tli_kinit(cp->ch_client, svp->sv_knconf, 9137c478bd9Sstevel@tonic-gate &svp->sv_addr, ci->cl_readsize, ci->cl_retrans, cr); 9147c478bd9Sstevel@tonic-gate 9157c478bd9Sstevel@tonic-gate /* 9167c478bd9Sstevel@tonic-gate * Get an auth handle. 9177c478bd9Sstevel@tonic-gate */ 9187c478bd9Sstevel@tonic-gate error = authget(svp, cp->ch_client, cr); 9197c478bd9Sstevel@tonic-gate if (error || cp->ch_client->cl_auth == NULL) { 9207c478bd9Sstevel@tonic-gate CLNT_DESTROY(cp->ch_client); 9217c478bd9Sstevel@tonic-gate kmem_cache_free(chtab4_cache, cp); 9227c478bd9Sstevel@tonic-gate return ((error != 0) ? error : EINTR); 9237c478bd9Sstevel@tonic-gate } 9247c478bd9Sstevel@tonic-gate ch->ch_timesused++; 9257c478bd9Sstevel@tonic-gate *newcl = cp->ch_client; 9267c478bd9Sstevel@tonic-gate *chp = cp; 9277c478bd9Sstevel@tonic-gate return (0); 9287c478bd9Sstevel@tonic-gate } 9297c478bd9Sstevel@tonic-gate 9307c478bd9Sstevel@tonic-gate /* 9317c478bd9Sstevel@tonic-gate * There weren't any free client handles which fit, so allocate 9327c478bd9Sstevel@tonic-gate * a new one and use that. 9337c478bd9Sstevel@tonic-gate */ 9347c478bd9Sstevel@tonic-gate #ifdef DEBUG 9357c478bd9Sstevel@tonic-gate atomic_add_64(&nfscl->nfscl_stat.clalloc.value.ui64, 1); 9367c478bd9Sstevel@tonic-gate #endif 9377c478bd9Sstevel@tonic-gate mutex_exit(&nfscl->nfscl_chtable4_lock); 9387c478bd9Sstevel@tonic-gate 9397c478bd9Sstevel@tonic-gate nfscl->nfscl_stat.cltoomany.value.ui64++; 9407c478bd9Sstevel@tonic-gate if (newch != NULL) { 9417c478bd9Sstevel@tonic-gate kmem_free(newch->ch_protofmly, strlen(newch->ch_protofmly) + 1); 9427c478bd9Sstevel@tonic-gate kmem_free(newch, sizeof (*newch)); 9437c478bd9Sstevel@tonic-gate } 9447c478bd9Sstevel@tonic-gate 9457c478bd9Sstevel@tonic-gate cp = kmem_cache_alloc(chtab4_cache, KM_SLEEP); 9467c478bd9Sstevel@tonic-gate cp->ch_head = ch; 9477c478bd9Sstevel@tonic-gate 9487c478bd9Sstevel@tonic-gate sigintr(&smask, (int)ci->cl_flags & MI4_INT); 9497c478bd9Sstevel@tonic-gate error = clnt_tli_kcreate(svp->sv_knconf, &svp->sv_addr, ci->cl_prog, 9507c478bd9Sstevel@tonic-gate ci->cl_vers, ci->cl_readsize, ci->cl_retrans, cr, &cp->ch_client); 9517c478bd9Sstevel@tonic-gate sigunintr(&smask); 9527c478bd9Sstevel@tonic-gate 9537c478bd9Sstevel@tonic-gate if (error != 0) { 9547c478bd9Sstevel@tonic-gate kmem_cache_free(chtab4_cache, cp); 9557c478bd9Sstevel@tonic-gate #ifdef DEBUG 9567c478bd9Sstevel@tonic-gate atomic_add_64(&nfscl->nfscl_stat.clalloc.value.ui64, -1); 9577c478bd9Sstevel@tonic-gate #endif 9587c478bd9Sstevel@tonic-gate /* 9597c478bd9Sstevel@tonic-gate * Warning is unnecessary if error is EINTR. 9607c478bd9Sstevel@tonic-gate */ 9617c478bd9Sstevel@tonic-gate if (error != EINTR) { 9627c478bd9Sstevel@tonic-gate nfs_cmn_err(error, CE_WARN, 9637c478bd9Sstevel@tonic-gate "clget: couldn't create handle: %m\n"); 9647c478bd9Sstevel@tonic-gate } 9657c478bd9Sstevel@tonic-gate return (error); 9667c478bd9Sstevel@tonic-gate } 9677c478bd9Sstevel@tonic-gate (void) CLNT_CONTROL(cp->ch_client, CLSET_PROGRESS, NULL); 9687c478bd9Sstevel@tonic-gate auth_destroy(cp->ch_client->cl_auth); 9697c478bd9Sstevel@tonic-gate 9707c478bd9Sstevel@tonic-gate /* 9717c478bd9Sstevel@tonic-gate * Get an auth handle. 9727c478bd9Sstevel@tonic-gate */ 9737c478bd9Sstevel@tonic-gate error = authget(svp, cp->ch_client, cr); 9747c478bd9Sstevel@tonic-gate if (error || cp->ch_client->cl_auth == NULL) { 9757c478bd9Sstevel@tonic-gate CLNT_DESTROY(cp->ch_client); 9767c478bd9Sstevel@tonic-gate kmem_cache_free(chtab4_cache, cp); 9777c478bd9Sstevel@tonic-gate #ifdef DEBUG 9787c478bd9Sstevel@tonic-gate atomic_add_64(&nfscl->nfscl_stat.clalloc.value.ui64, -1); 9797c478bd9Sstevel@tonic-gate #endif 9807c478bd9Sstevel@tonic-gate return ((error != 0) ? error : EINTR); 9817c478bd9Sstevel@tonic-gate } 9827c478bd9Sstevel@tonic-gate ch->ch_timesused++; 9837c478bd9Sstevel@tonic-gate *newcl = cp->ch_client; 9847c478bd9Sstevel@tonic-gate ASSERT(cp->ch_client->cl_nosignal == FALSE); 9857c478bd9Sstevel@tonic-gate *chp = cp; 9867c478bd9Sstevel@tonic-gate return (0); 9877c478bd9Sstevel@tonic-gate } 9887c478bd9Sstevel@tonic-gate 9897c478bd9Sstevel@tonic-gate static int 9907c478bd9Sstevel@tonic-gate nfs_clget4(mntinfo4_t *mi, servinfo4_t *svp, cred_t *cr, CLIENT **newcl, 9917c478bd9Sstevel@tonic-gate struct chtab **chp, struct nfs4_clnt *nfscl) 9927c478bd9Sstevel@tonic-gate { 9937c478bd9Sstevel@tonic-gate clinfo_t ci; 9947c478bd9Sstevel@tonic-gate bool_t is_recov; 9957c478bd9Sstevel@tonic-gate int firstcall, error = 0; 9967c478bd9Sstevel@tonic-gate 9977c478bd9Sstevel@tonic-gate /* 9987c478bd9Sstevel@tonic-gate * Set read buffer size to rsize 9997c478bd9Sstevel@tonic-gate * and add room for RPC headers. 10007c478bd9Sstevel@tonic-gate */ 10017c478bd9Sstevel@tonic-gate ci.cl_readsize = mi->mi_tsize; 10027c478bd9Sstevel@tonic-gate if (ci.cl_readsize != 0) 10037c478bd9Sstevel@tonic-gate ci.cl_readsize += (RPC_MAXDATASIZE - NFS_MAXDATA); 10047c478bd9Sstevel@tonic-gate 10057c478bd9Sstevel@tonic-gate /* 10067c478bd9Sstevel@tonic-gate * If soft mount and server is down just try once. 10077c478bd9Sstevel@tonic-gate * meaning: do not retransmit. 10087c478bd9Sstevel@tonic-gate */ 10097c478bd9Sstevel@tonic-gate if (!(mi->mi_flags & MI4_HARD) && (mi->mi_flags & MI4_DOWN)) 10107c478bd9Sstevel@tonic-gate ci.cl_retrans = 0; 10117c478bd9Sstevel@tonic-gate else 10127c478bd9Sstevel@tonic-gate ci.cl_retrans = mi->mi_retrans; 10137c478bd9Sstevel@tonic-gate 10147c478bd9Sstevel@tonic-gate ci.cl_prog = mi->mi_prog; 10157c478bd9Sstevel@tonic-gate ci.cl_vers = mi->mi_vers; 10167c478bd9Sstevel@tonic-gate ci.cl_flags = mi->mi_flags; 10177c478bd9Sstevel@tonic-gate 10187c478bd9Sstevel@tonic-gate /* 10197c478bd9Sstevel@tonic-gate * clget4 calls authget() to get an auth handle. For RPCSEC_GSS 10207c478bd9Sstevel@tonic-gate * security flavor, the client tries to establish a security context 10217c478bd9Sstevel@tonic-gate * by contacting the server. If the connection is timed out or reset, 10227c478bd9Sstevel@tonic-gate * e.g. server reboot, we will try again. 10237c478bd9Sstevel@tonic-gate */ 10247c478bd9Sstevel@tonic-gate is_recov = (curthread == mi->mi_recovthread); 10257c478bd9Sstevel@tonic-gate firstcall = 1; 10267c478bd9Sstevel@tonic-gate 10277c478bd9Sstevel@tonic-gate do { 10287c478bd9Sstevel@tonic-gate error = clget4(&ci, svp, cr, newcl, chp, nfscl); 10297c478bd9Sstevel@tonic-gate 10307c478bd9Sstevel@tonic-gate if (error == 0) 10317c478bd9Sstevel@tonic-gate break; 10327c478bd9Sstevel@tonic-gate 10337c478bd9Sstevel@tonic-gate /* 10347c478bd9Sstevel@tonic-gate * For forced unmount and zone shutdown, bail out but 10357c478bd9Sstevel@tonic-gate * let the recovery thread do one more transmission. 10367c478bd9Sstevel@tonic-gate */ 10377c478bd9Sstevel@tonic-gate if ((FS_OR_ZONE_GONE4(mi->mi_vfsp)) && 10387c478bd9Sstevel@tonic-gate (!is_recov || !firstcall)) { 10397c478bd9Sstevel@tonic-gate error = EIO; 10407c478bd9Sstevel@tonic-gate break; 10417c478bd9Sstevel@tonic-gate } 10427c478bd9Sstevel@tonic-gate 10437c478bd9Sstevel@tonic-gate /* do not retry for soft mount */ 10447c478bd9Sstevel@tonic-gate if (!(mi->mi_flags & MI4_HARD)) 10457c478bd9Sstevel@tonic-gate break; 10467c478bd9Sstevel@tonic-gate 10477c478bd9Sstevel@tonic-gate /* let the caller deal with the failover case */ 10487c478bd9Sstevel@tonic-gate if (FAILOVER_MOUNT4(mi)) 10497c478bd9Sstevel@tonic-gate break; 10507c478bd9Sstevel@tonic-gate 10517c478bd9Sstevel@tonic-gate firstcall = 0; 10527c478bd9Sstevel@tonic-gate 10537c478bd9Sstevel@tonic-gate } while (error == ETIMEDOUT || error == ECONNRESET); 10547c478bd9Sstevel@tonic-gate 10557c478bd9Sstevel@tonic-gate return (error); 10567c478bd9Sstevel@tonic-gate } 10577c478bd9Sstevel@tonic-gate 10587c478bd9Sstevel@tonic-gate void 10597c478bd9Sstevel@tonic-gate clfree4(CLIENT *cl, struct chtab *cp, struct nfs4_clnt *nfscl) 10607c478bd9Sstevel@tonic-gate { 10617c478bd9Sstevel@tonic-gate if (cl->cl_auth != NULL) { 10627c478bd9Sstevel@tonic-gate sec_clnt_freeh(cl->cl_auth); 10637c478bd9Sstevel@tonic-gate cl->cl_auth = NULL; 10647c478bd9Sstevel@tonic-gate } 10657c478bd9Sstevel@tonic-gate 10667c478bd9Sstevel@tonic-gate /* 10677c478bd9Sstevel@tonic-gate * Timestamp this cache entry so that we know when it was last 10687c478bd9Sstevel@tonic-gate * used. 10697c478bd9Sstevel@tonic-gate */ 10707c478bd9Sstevel@tonic-gate cp->ch_freed = gethrestime_sec(); 10717c478bd9Sstevel@tonic-gate 10727c478bd9Sstevel@tonic-gate /* 10737c478bd9Sstevel@tonic-gate * Add the free client handle to the front of the list. 10747c478bd9Sstevel@tonic-gate * This way, the list will be sorted in youngest to oldest 10757c478bd9Sstevel@tonic-gate * order. 10767c478bd9Sstevel@tonic-gate */ 10777c478bd9Sstevel@tonic-gate mutex_enter(&nfscl->nfscl_chtable4_lock); 10787c478bd9Sstevel@tonic-gate cp->ch_list = cp->ch_head->ch_list; 10797c478bd9Sstevel@tonic-gate cp->ch_head->ch_list = cp; 10807c478bd9Sstevel@tonic-gate mutex_exit(&nfscl->nfscl_chtable4_lock); 10817c478bd9Sstevel@tonic-gate } 10827c478bd9Sstevel@tonic-gate 10837c478bd9Sstevel@tonic-gate #define CL_HOLDTIME 60 /* time to hold client handles */ 10847c478bd9Sstevel@tonic-gate 10857c478bd9Sstevel@tonic-gate static void 10867c478bd9Sstevel@tonic-gate clreclaim4_zone(struct nfs4_clnt *nfscl, uint_t cl_holdtime) 10877c478bd9Sstevel@tonic-gate { 10887c478bd9Sstevel@tonic-gate struct chhead *ch; 10897c478bd9Sstevel@tonic-gate struct chtab *cp; /* list of objects that can be reclaimed */ 10907c478bd9Sstevel@tonic-gate struct chtab *cpe; 10917c478bd9Sstevel@tonic-gate struct chtab *cpl; 10927c478bd9Sstevel@tonic-gate struct chtab **cpp; 10937c478bd9Sstevel@tonic-gate #ifdef DEBUG 10947c478bd9Sstevel@tonic-gate int n = 0; 10957c478bd9Sstevel@tonic-gate clstat4_debug.clreclaim.value.ui64++; 10967c478bd9Sstevel@tonic-gate #endif 10977c478bd9Sstevel@tonic-gate 10987c478bd9Sstevel@tonic-gate /* 10997c478bd9Sstevel@tonic-gate * Need to reclaim some memory, so step through the cache 11007c478bd9Sstevel@tonic-gate * looking through the lists for entries which can be freed. 11017c478bd9Sstevel@tonic-gate */ 11027c478bd9Sstevel@tonic-gate cp = NULL; 11037c478bd9Sstevel@tonic-gate 11047c478bd9Sstevel@tonic-gate mutex_enter(&nfscl->nfscl_chtable4_lock); 11057c478bd9Sstevel@tonic-gate 11067c478bd9Sstevel@tonic-gate /* 11077c478bd9Sstevel@tonic-gate * Here we step through each non-NULL quadruple and start to 11087c478bd9Sstevel@tonic-gate * construct the reclaim list pointed to by cp. Note that 11097c478bd9Sstevel@tonic-gate * cp will contain all eligible chtab entries. When this traversal 11107c478bd9Sstevel@tonic-gate * completes, chtab entries from the last quadruple will be at the 11117c478bd9Sstevel@tonic-gate * front of cp and entries from previously inspected quadruples have 11127c478bd9Sstevel@tonic-gate * been appended to the rear of cp. 11137c478bd9Sstevel@tonic-gate */ 11147c478bd9Sstevel@tonic-gate for (ch = nfscl->nfscl_chtable4; ch != NULL; ch = ch->ch_next) { 11157c478bd9Sstevel@tonic-gate if (ch->ch_list == NULL) 11167c478bd9Sstevel@tonic-gate continue; 11177c478bd9Sstevel@tonic-gate /* 11187c478bd9Sstevel@tonic-gate * Search each list for entries older then 11197c478bd9Sstevel@tonic-gate * cl_holdtime seconds. The lists are maintained 11207c478bd9Sstevel@tonic-gate * in youngest to oldest order so that when the 11217c478bd9Sstevel@tonic-gate * first entry is found which is old enough, then 11227c478bd9Sstevel@tonic-gate * all of the rest of the entries on the list will 11237c478bd9Sstevel@tonic-gate * be old enough as well. 11247c478bd9Sstevel@tonic-gate */ 11257c478bd9Sstevel@tonic-gate cpl = ch->ch_list; 11267c478bd9Sstevel@tonic-gate cpp = &ch->ch_list; 11277c478bd9Sstevel@tonic-gate while (cpl != NULL && 11287c478bd9Sstevel@tonic-gate cpl->ch_freed + cl_holdtime > gethrestime_sec()) { 11297c478bd9Sstevel@tonic-gate cpp = &cpl->ch_list; 11307c478bd9Sstevel@tonic-gate cpl = cpl->ch_list; 11317c478bd9Sstevel@tonic-gate } 11327c478bd9Sstevel@tonic-gate if (cpl != NULL) { 11337c478bd9Sstevel@tonic-gate *cpp = NULL; 11347c478bd9Sstevel@tonic-gate if (cp != NULL) { 11357c478bd9Sstevel@tonic-gate cpe = cpl; 11367c478bd9Sstevel@tonic-gate while (cpe->ch_list != NULL) 11377c478bd9Sstevel@tonic-gate cpe = cpe->ch_list; 11387c478bd9Sstevel@tonic-gate cpe->ch_list = cp; 11397c478bd9Sstevel@tonic-gate } 11407c478bd9Sstevel@tonic-gate cp = cpl; 11417c478bd9Sstevel@tonic-gate } 11427c478bd9Sstevel@tonic-gate } 11437c478bd9Sstevel@tonic-gate 11447c478bd9Sstevel@tonic-gate mutex_exit(&nfscl->nfscl_chtable4_lock); 11457c478bd9Sstevel@tonic-gate 11467c478bd9Sstevel@tonic-gate /* 11477c478bd9Sstevel@tonic-gate * If cp is empty, then there is nothing to reclaim here. 11487c478bd9Sstevel@tonic-gate */ 11497c478bd9Sstevel@tonic-gate if (cp == NULL) 11507c478bd9Sstevel@tonic-gate return; 11517c478bd9Sstevel@tonic-gate 11527c478bd9Sstevel@tonic-gate /* 11537c478bd9Sstevel@tonic-gate * Step through the list of entries to free, destroying each client 11547c478bd9Sstevel@tonic-gate * handle and kmem_free'ing the memory for each entry. 11557c478bd9Sstevel@tonic-gate */ 11567c478bd9Sstevel@tonic-gate while (cp != NULL) { 11577c478bd9Sstevel@tonic-gate #ifdef DEBUG 11587c478bd9Sstevel@tonic-gate n++; 11597c478bd9Sstevel@tonic-gate #endif 11607c478bd9Sstevel@tonic-gate CLNT_DESTROY(cp->ch_client); 11617c478bd9Sstevel@tonic-gate cpl = cp->ch_list; 11627c478bd9Sstevel@tonic-gate kmem_cache_free(chtab4_cache, cp); 11637c478bd9Sstevel@tonic-gate cp = cpl; 11647c478bd9Sstevel@tonic-gate } 11657c478bd9Sstevel@tonic-gate 11667c478bd9Sstevel@tonic-gate #ifdef DEBUG 11677c478bd9Sstevel@tonic-gate /* 11687c478bd9Sstevel@tonic-gate * Update clalloc so that nfsstat shows the current number 11697c478bd9Sstevel@tonic-gate * of allocated client handles. 11707c478bd9Sstevel@tonic-gate */ 11717c478bd9Sstevel@tonic-gate atomic_add_64(&nfscl->nfscl_stat.clalloc.value.ui64, -n); 11727c478bd9Sstevel@tonic-gate #endif 11737c478bd9Sstevel@tonic-gate } 11747c478bd9Sstevel@tonic-gate 11757c478bd9Sstevel@tonic-gate /* ARGSUSED */ 11767c478bd9Sstevel@tonic-gate static void 11777c478bd9Sstevel@tonic-gate clreclaim4(void *all) 11787c478bd9Sstevel@tonic-gate { 11797c478bd9Sstevel@tonic-gate struct nfs4_clnt *nfscl; 11807c478bd9Sstevel@tonic-gate 11817c478bd9Sstevel@tonic-gate /* 11827c478bd9Sstevel@tonic-gate * The system is low on memory; go through and try to reclaim some from 11837c478bd9Sstevel@tonic-gate * every zone on the system. 11847c478bd9Sstevel@tonic-gate */ 11857c478bd9Sstevel@tonic-gate mutex_enter(&nfs4_clnt_list_lock); 11867c478bd9Sstevel@tonic-gate nfscl = list_head(&nfs4_clnt_list); 11877c478bd9Sstevel@tonic-gate for (; nfscl != NULL; nfscl = list_next(&nfs4_clnt_list, nfscl)) 11887c478bd9Sstevel@tonic-gate clreclaim4_zone(nfscl, CL_HOLDTIME); 11897c478bd9Sstevel@tonic-gate mutex_exit(&nfs4_clnt_list_lock); 11907c478bd9Sstevel@tonic-gate } 11917c478bd9Sstevel@tonic-gate 11927c478bd9Sstevel@tonic-gate /* 11937c478bd9Sstevel@tonic-gate * Minimum time-out values indexed by call type 11947c478bd9Sstevel@tonic-gate * These units are in "eights" of a second to avoid multiplies 11957c478bd9Sstevel@tonic-gate */ 11967c478bd9Sstevel@tonic-gate static unsigned int minimum_timeo[] = { 11977c478bd9Sstevel@tonic-gate 6, 7, 10 11987c478bd9Sstevel@tonic-gate }; 11997c478bd9Sstevel@tonic-gate 12007c478bd9Sstevel@tonic-gate #define SHORTWAIT (NFS_COTS_TIMEO / 10) 12017c478bd9Sstevel@tonic-gate 12027c478bd9Sstevel@tonic-gate /* 12037c478bd9Sstevel@tonic-gate * Back off for retransmission timeout, MAXTIMO is in hz of a sec 12047c478bd9Sstevel@tonic-gate */ 12057c478bd9Sstevel@tonic-gate #define MAXTIMO (20*hz) 12067c478bd9Sstevel@tonic-gate #define backoff(tim) (((tim) < MAXTIMO) ? dobackoff(tim) : (tim)) 12077c478bd9Sstevel@tonic-gate #define dobackoff(tim) ((((tim) << 1) > MAXTIMO) ? MAXTIMO : ((tim) << 1)) 12087c478bd9Sstevel@tonic-gate 12097c478bd9Sstevel@tonic-gate static int 12107c478bd9Sstevel@tonic-gate nfs4_rfscall(mntinfo4_t *mi, rpcproc_t which, xdrproc_t xdrargs, caddr_t argsp, 12117c478bd9Sstevel@tonic-gate xdrproc_t xdrres, caddr_t resp, cred_t *cr, int *doqueue, 12127c478bd9Sstevel@tonic-gate enum clnt_stat *rpc_statusp, int flags, struct nfs4_clnt *nfscl) 12137c478bd9Sstevel@tonic-gate { 12147c478bd9Sstevel@tonic-gate CLIENT *client; 12157c478bd9Sstevel@tonic-gate struct chtab *ch; 12167c478bd9Sstevel@tonic-gate struct rpc_err rpcerr; 12177c478bd9Sstevel@tonic-gate enum clnt_stat status; 12187c478bd9Sstevel@tonic-gate int error; 12197c478bd9Sstevel@tonic-gate struct timeval wait; 12207c478bd9Sstevel@tonic-gate int timeo; /* in units of hz */ 12217c478bd9Sstevel@tonic-gate bool_t tryagain, is_recov; 12227c478bd9Sstevel@tonic-gate k_sigset_t smask; 12237c478bd9Sstevel@tonic-gate servinfo4_t *svp; 12247c478bd9Sstevel@tonic-gate #ifdef DEBUG 12257c478bd9Sstevel@tonic-gate char *bufp; 12267c478bd9Sstevel@tonic-gate #endif 12277c478bd9Sstevel@tonic-gate int firstcall; 12287c478bd9Sstevel@tonic-gate 12297c478bd9Sstevel@tonic-gate rpcerr.re_status = RPC_SUCCESS; 12307c478bd9Sstevel@tonic-gate 12317c478bd9Sstevel@tonic-gate /* 12327c478bd9Sstevel@tonic-gate * If we know that we are rebooting then let's 12337c478bd9Sstevel@tonic-gate * not bother with doing any over the wireness. 12347c478bd9Sstevel@tonic-gate */ 12357c478bd9Sstevel@tonic-gate mutex_enter(&mi->mi_lock); 12367c478bd9Sstevel@tonic-gate if (mi->mi_flags & MI4_SHUTDOWN) { 12377c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 12387c478bd9Sstevel@tonic-gate return (EIO); 12397c478bd9Sstevel@tonic-gate } 12407c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 12417c478bd9Sstevel@tonic-gate 12427c478bd9Sstevel@tonic-gate /* 12437c478bd9Sstevel@tonic-gate * clget() calls clnt_tli_kinit() which clears the xid, so we 12447c478bd9Sstevel@tonic-gate * are guaranteed to reprocess the retry as a new request. 12457c478bd9Sstevel@tonic-gate */ 12467c478bd9Sstevel@tonic-gate svp = mi->mi_curr_serv; 12477c478bd9Sstevel@tonic-gate rpcerr.re_errno = nfs_clget4(mi, svp, cr, &client, &ch, nfscl); 12487c478bd9Sstevel@tonic-gate if (rpcerr.re_errno != 0) 12497c478bd9Sstevel@tonic-gate return (rpcerr.re_errno); 12507c478bd9Sstevel@tonic-gate 12517c478bd9Sstevel@tonic-gate timeo = (mi->mi_timeo * hz) / 10; 12527c478bd9Sstevel@tonic-gate 12537c478bd9Sstevel@tonic-gate /* 12547c478bd9Sstevel@tonic-gate * If hard mounted fs, retry call forever unless hard error 12557c478bd9Sstevel@tonic-gate * occurs. 12567c478bd9Sstevel@tonic-gate * 12577c478bd9Sstevel@tonic-gate * For forced unmount, let the recovery thread through but return 12587c478bd9Sstevel@tonic-gate * an error for all others. This is so that user processes can 12597c478bd9Sstevel@tonic-gate * exit quickly. The recovery thread bails out after one 12607c478bd9Sstevel@tonic-gate * transmission so that it can tell if it needs to continue. 12617c478bd9Sstevel@tonic-gate * 12627c478bd9Sstevel@tonic-gate * For zone shutdown, behave as above to encourage quick 12637c478bd9Sstevel@tonic-gate * process exit, but also fail quickly when servers have 12647c478bd9Sstevel@tonic-gate * timed out before and reduce the timeouts. 12657c478bd9Sstevel@tonic-gate */ 12667c478bd9Sstevel@tonic-gate is_recov = (curthread == mi->mi_recovthread); 12677c478bd9Sstevel@tonic-gate firstcall = 1; 12687c478bd9Sstevel@tonic-gate do { 12697c478bd9Sstevel@tonic-gate tryagain = FALSE; 12707c478bd9Sstevel@tonic-gate 12717c478bd9Sstevel@tonic-gate NFS4_DEBUG(nfs4_rfscall_debug, (CE_NOTE, 12727c478bd9Sstevel@tonic-gate "nfs4_rfscall: vfs_flag=0x%x, %s", 12737c478bd9Sstevel@tonic-gate mi->mi_vfsp->vfs_flag, 12747c478bd9Sstevel@tonic-gate is_recov ? "recov thread" : "not recov thread")); 12757c478bd9Sstevel@tonic-gate 12767c478bd9Sstevel@tonic-gate /* 12777c478bd9Sstevel@tonic-gate * It's possible while we're retrying the admin 12787c478bd9Sstevel@tonic-gate * decided to reboot. 12797c478bd9Sstevel@tonic-gate */ 12807c478bd9Sstevel@tonic-gate mutex_enter(&mi->mi_lock); 12817c478bd9Sstevel@tonic-gate if (mi->mi_flags & MI4_SHUTDOWN) { 12827c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 12837c478bd9Sstevel@tonic-gate clfree4(client, ch, nfscl); 12847c478bd9Sstevel@tonic-gate return (EIO); 12857c478bd9Sstevel@tonic-gate } 12867c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 12877c478bd9Sstevel@tonic-gate 12887c478bd9Sstevel@tonic-gate if ((mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED) && 12897c478bd9Sstevel@tonic-gate (!is_recov || !firstcall)) { 12907c478bd9Sstevel@tonic-gate clfree4(client, ch, nfscl); 12917c478bd9Sstevel@tonic-gate return (EIO); 12927c478bd9Sstevel@tonic-gate } 12937c478bd9Sstevel@tonic-gate 12947c478bd9Sstevel@tonic-gate if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN) { 12957c478bd9Sstevel@tonic-gate mutex_enter(&mi->mi_lock); 12967c478bd9Sstevel@tonic-gate if ((mi->mi_flags & MI4_TIMEDOUT) || 12977c478bd9Sstevel@tonic-gate !is_recov || !firstcall) { 12987c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 12997c478bd9Sstevel@tonic-gate clfree4(client, ch, nfscl); 13007c478bd9Sstevel@tonic-gate return (EIO); 13017c478bd9Sstevel@tonic-gate } 13027c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 13037c478bd9Sstevel@tonic-gate timeo = (MIN(mi->mi_timeo, SHORTWAIT) * hz) / 10; 13047c478bd9Sstevel@tonic-gate } 13057c478bd9Sstevel@tonic-gate 13067c478bd9Sstevel@tonic-gate firstcall = 0; 13077c478bd9Sstevel@tonic-gate TICK_TO_TIMEVAL(timeo, &wait); 13087c478bd9Sstevel@tonic-gate 13097c478bd9Sstevel@tonic-gate /* 13107c478bd9Sstevel@tonic-gate * Mask out all signals except SIGHUP, SIGINT, SIGQUIT 13117c478bd9Sstevel@tonic-gate * and SIGTERM. (Preserving the existing masks). 13127c478bd9Sstevel@tonic-gate * Mask out SIGINT if mount option nointr is specified. 13137c478bd9Sstevel@tonic-gate */ 13147c478bd9Sstevel@tonic-gate sigintr(&smask, (int)mi->mi_flags & MI4_INT); 13157c478bd9Sstevel@tonic-gate if (!(mi->mi_flags & MI4_INT)) 13167c478bd9Sstevel@tonic-gate client->cl_nosignal = TRUE; 13177c478bd9Sstevel@tonic-gate 13187c478bd9Sstevel@tonic-gate /* 13197c478bd9Sstevel@tonic-gate * If there is a current signal, then don't bother 13207c478bd9Sstevel@tonic-gate * even trying to send out the request because we 13217c478bd9Sstevel@tonic-gate * won't be able to block waiting for the response. 13227c478bd9Sstevel@tonic-gate * Simply assume RPC_INTR and get on with it. 13237c478bd9Sstevel@tonic-gate */ 13247c478bd9Sstevel@tonic-gate if (ttolwp(curthread) != NULL && ISSIG(curthread, JUSTLOOKING)) 13257c478bd9Sstevel@tonic-gate status = RPC_INTR; 13267c478bd9Sstevel@tonic-gate else { 13277c478bd9Sstevel@tonic-gate status = CLNT_CALL(client, which, xdrargs, argsp, 13287c478bd9Sstevel@tonic-gate xdrres, resp, wait); 13297c478bd9Sstevel@tonic-gate } 13307c478bd9Sstevel@tonic-gate 13317c478bd9Sstevel@tonic-gate if (!(mi->mi_flags & MI4_INT)) 13327c478bd9Sstevel@tonic-gate client->cl_nosignal = FALSE; 13337c478bd9Sstevel@tonic-gate /* 13347c478bd9Sstevel@tonic-gate * restore original signal mask 13357c478bd9Sstevel@tonic-gate */ 13367c478bd9Sstevel@tonic-gate sigunintr(&smask); 13377c478bd9Sstevel@tonic-gate 13387c478bd9Sstevel@tonic-gate switch (status) { 13397c478bd9Sstevel@tonic-gate case RPC_SUCCESS: 13407c478bd9Sstevel@tonic-gate break; 13417c478bd9Sstevel@tonic-gate 13427c478bd9Sstevel@tonic-gate case RPC_INTR: 13437c478bd9Sstevel@tonic-gate /* 13447c478bd9Sstevel@tonic-gate * There is no way to recover from this error, 13457c478bd9Sstevel@tonic-gate * even if mount option nointr is specified. 13467c478bd9Sstevel@tonic-gate * SIGKILL, for example, cannot be blocked. 13477c478bd9Sstevel@tonic-gate */ 13487c478bd9Sstevel@tonic-gate rpcerr.re_status = RPC_INTR; 13497c478bd9Sstevel@tonic-gate rpcerr.re_errno = EINTR; 13507c478bd9Sstevel@tonic-gate break; 13517c478bd9Sstevel@tonic-gate 13527c478bd9Sstevel@tonic-gate case RPC_UDERROR: 13537c478bd9Sstevel@tonic-gate /* 13547c478bd9Sstevel@tonic-gate * If the NFS server is local (vold) and 13557c478bd9Sstevel@tonic-gate * it goes away then we get RPC_UDERROR. 13567c478bd9Sstevel@tonic-gate * This is a retryable error, so we would 13577c478bd9Sstevel@tonic-gate * loop, so check to see if the specific 13587c478bd9Sstevel@tonic-gate * error was ECONNRESET, indicating that 13597c478bd9Sstevel@tonic-gate * target did not exist at all. If so, 13607c478bd9Sstevel@tonic-gate * return with RPC_PROGUNAVAIL and 13617c478bd9Sstevel@tonic-gate * ECONNRESET to indicate why. 13627c478bd9Sstevel@tonic-gate */ 13637c478bd9Sstevel@tonic-gate CLNT_GETERR(client, &rpcerr); 13647c478bd9Sstevel@tonic-gate if (rpcerr.re_errno == ECONNRESET) { 13657c478bd9Sstevel@tonic-gate rpcerr.re_status = RPC_PROGUNAVAIL; 13667c478bd9Sstevel@tonic-gate rpcerr.re_errno = ECONNRESET; 13677c478bd9Sstevel@tonic-gate break; 13687c478bd9Sstevel@tonic-gate } 13697c478bd9Sstevel@tonic-gate /*FALLTHROUGH*/ 13707c478bd9Sstevel@tonic-gate 13717c478bd9Sstevel@tonic-gate default: /* probably RPC_TIMEDOUT */ 13727c478bd9Sstevel@tonic-gate 13737c478bd9Sstevel@tonic-gate if (IS_UNRECOVERABLE_RPC(status)) 13747c478bd9Sstevel@tonic-gate break; 13757c478bd9Sstevel@tonic-gate 13767c478bd9Sstevel@tonic-gate /* 13777c478bd9Sstevel@tonic-gate * increment server not responding count 13787c478bd9Sstevel@tonic-gate */ 13797c478bd9Sstevel@tonic-gate mutex_enter(&mi->mi_lock); 13807c478bd9Sstevel@tonic-gate mi->mi_noresponse++; 13817c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 13827c478bd9Sstevel@tonic-gate #ifdef DEBUG 13837c478bd9Sstevel@tonic-gate nfscl->nfscl_stat.noresponse.value.ui64++; 13847c478bd9Sstevel@tonic-gate #endif 13857c478bd9Sstevel@tonic-gate /* 13867c478bd9Sstevel@tonic-gate * On zone shutdown, mark server dead and move on. 13877c478bd9Sstevel@tonic-gate */ 13887c478bd9Sstevel@tonic-gate if (zone_status_get(curproc->p_zone) >= 13897c478bd9Sstevel@tonic-gate ZONE_IS_SHUTTING_DOWN) { 13907c478bd9Sstevel@tonic-gate mutex_enter(&mi->mi_lock); 13917c478bd9Sstevel@tonic-gate mi->mi_flags |= MI4_TIMEDOUT; 13927c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 13937c478bd9Sstevel@tonic-gate clfree4(client, ch, nfscl); 13947c478bd9Sstevel@tonic-gate return (EIO); 13957c478bd9Sstevel@tonic-gate } 13967c478bd9Sstevel@tonic-gate 13977c478bd9Sstevel@tonic-gate /* 13987c478bd9Sstevel@tonic-gate * NFS client failover support: 13997c478bd9Sstevel@tonic-gate * return and let the caller take care of 14007c478bd9Sstevel@tonic-gate * failover. We only return for failover mounts 14017c478bd9Sstevel@tonic-gate * because otherwise we want the "not responding" 14027c478bd9Sstevel@tonic-gate * message, the timer updates, etc. 14037c478bd9Sstevel@tonic-gate */ 14047c478bd9Sstevel@tonic-gate if (mi->mi_vers == 4 && FAILOVER_MOUNT4(mi) && 14057c478bd9Sstevel@tonic-gate (error = try_failover(status)) != 0) { 14067c478bd9Sstevel@tonic-gate clfree4(client, ch, nfscl); 14077c478bd9Sstevel@tonic-gate *rpc_statusp = status; 14087c478bd9Sstevel@tonic-gate return (error); 14097c478bd9Sstevel@tonic-gate } 14107c478bd9Sstevel@tonic-gate 14117c478bd9Sstevel@tonic-gate if (flags & RFSCALL_SOFT) 14127c478bd9Sstevel@tonic-gate break; 14137c478bd9Sstevel@tonic-gate 14147c478bd9Sstevel@tonic-gate tryagain = TRUE; 14157c478bd9Sstevel@tonic-gate 14167c478bd9Sstevel@tonic-gate /* 14177c478bd9Sstevel@tonic-gate * The call is in progress (over COTS). 14187c478bd9Sstevel@tonic-gate * Try the CLNT_CALL again, but don't 14197c478bd9Sstevel@tonic-gate * print a noisy error message. 14207c478bd9Sstevel@tonic-gate */ 14217c478bd9Sstevel@tonic-gate if (status == RPC_INPROGRESS) 14227c478bd9Sstevel@tonic-gate break; 14237c478bd9Sstevel@tonic-gate 14247c478bd9Sstevel@tonic-gate timeo = backoff(timeo); 14257c478bd9Sstevel@tonic-gate mutex_enter(&mi->mi_lock); 14267c478bd9Sstevel@tonic-gate if (!(mi->mi_flags & MI4_PRINTED)) { 14277c478bd9Sstevel@tonic-gate mi->mi_flags |= MI4_PRINTED; 14287c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 14297c478bd9Sstevel@tonic-gate nfs4_queue_fact(RF_SRV_NOT_RESPOND, mi, 0, 0, 0, 14307c478bd9Sstevel@tonic-gate FALSE, NULL, 0, NULL); 14317c478bd9Sstevel@tonic-gate } else 14327c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 14337c478bd9Sstevel@tonic-gate 14347c478bd9Sstevel@tonic-gate if (*doqueue && curproc->p_sessp->s_vp != NULL) { 14357c478bd9Sstevel@tonic-gate *doqueue = 0; 14367c478bd9Sstevel@tonic-gate if (!(mi->mi_flags & MI4_NOPRINT)) 14377c478bd9Sstevel@tonic-gate nfs4_queue_fact(RF_SRV_NOT_RESPOND, mi, 14387c478bd9Sstevel@tonic-gate 0, 0, 0, FALSE, NULL, 0, NULL); 14397c478bd9Sstevel@tonic-gate } 14407c478bd9Sstevel@tonic-gate } 14417c478bd9Sstevel@tonic-gate } while (tryagain); 14427c478bd9Sstevel@tonic-gate 14437c478bd9Sstevel@tonic-gate DTRACE_PROBE2(nfs4__rfscall_debug, enum clnt_stat, status, 14447c478bd9Sstevel@tonic-gate int, rpcerr.re_errno); 14457c478bd9Sstevel@tonic-gate 14467c478bd9Sstevel@tonic-gate if (status != RPC_SUCCESS) { 14477c478bd9Sstevel@tonic-gate zoneid_t zoneid = mi->mi_zone->zone_id; 14487c478bd9Sstevel@tonic-gate 14497c478bd9Sstevel@tonic-gate /* 14507c478bd9Sstevel@tonic-gate * Let soft mounts use the timed out message. 14517c478bd9Sstevel@tonic-gate */ 14527c478bd9Sstevel@tonic-gate if (status == RPC_INPROGRESS) 14537c478bd9Sstevel@tonic-gate status = RPC_TIMEDOUT; 14547c478bd9Sstevel@tonic-gate nfscl->nfscl_stat.badcalls.value.ui64++; 14557c478bd9Sstevel@tonic-gate if (status != RPC_INTR) { 14567c478bd9Sstevel@tonic-gate mutex_enter(&mi->mi_lock); 14577c478bd9Sstevel@tonic-gate mi->mi_flags |= MI4_DOWN; 14587c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 14597c478bd9Sstevel@tonic-gate CLNT_GETERR(client, &rpcerr); 14607c478bd9Sstevel@tonic-gate #ifdef DEBUG 14617c478bd9Sstevel@tonic-gate bufp = clnt_sperror(client, svp->sv_hostname); 14627c478bd9Sstevel@tonic-gate zprintf(zoneid, "NFS%d %s failed for %s\n", 14637c478bd9Sstevel@tonic-gate mi->mi_vers, mi->mi_rfsnames[which], bufp); 14647c478bd9Sstevel@tonic-gate if (curproc->p_sessp->s_vp != NULL) { 14657c478bd9Sstevel@tonic-gate if (!(mi->mi_flags & MI4_NOPRINT)) { 14667c478bd9Sstevel@tonic-gate uprintf("NFS%d %s failed for %s\n", 14677c478bd9Sstevel@tonic-gate mi->mi_vers, mi->mi_rfsnames[which], 14687c478bd9Sstevel@tonic-gate bufp); 14697c478bd9Sstevel@tonic-gate } 14707c478bd9Sstevel@tonic-gate } 14717c478bd9Sstevel@tonic-gate kmem_free(bufp, MAXPATHLEN); 14727c478bd9Sstevel@tonic-gate #else 14737c478bd9Sstevel@tonic-gate zprintf(zoneid, 14747c478bd9Sstevel@tonic-gate "NFS %s failed for server %s: error %d (%s)\n", 14757c478bd9Sstevel@tonic-gate mi->mi_rfsnames[which], svp->sv_hostname, 14767c478bd9Sstevel@tonic-gate status, clnt_sperrno(status)); 14777c478bd9Sstevel@tonic-gate if (curproc->p_sessp->s_vp != NULL) { 14787c478bd9Sstevel@tonic-gate if (!(mi->mi_flags & MI4_NOPRINT)) { 14797c478bd9Sstevel@tonic-gate uprintf( 14807c478bd9Sstevel@tonic-gate "NFS %s failed for server %s: error %d (%s)\n", 14817c478bd9Sstevel@tonic-gate mi->mi_rfsnames[which], 14827c478bd9Sstevel@tonic-gate svp->sv_hostname, status, 14837c478bd9Sstevel@tonic-gate clnt_sperrno(status)); 14847c478bd9Sstevel@tonic-gate } 14857c478bd9Sstevel@tonic-gate } 14867c478bd9Sstevel@tonic-gate #endif 14877c478bd9Sstevel@tonic-gate /* 14887c478bd9Sstevel@tonic-gate * when CLNT_CALL() fails with RPC_AUTHERROR, 14897c478bd9Sstevel@tonic-gate * re_errno is set appropriately depending on 14907c478bd9Sstevel@tonic-gate * the authentication error 14917c478bd9Sstevel@tonic-gate */ 14927c478bd9Sstevel@tonic-gate if (status == RPC_VERSMISMATCH || 14937c478bd9Sstevel@tonic-gate status == RPC_PROGVERSMISMATCH) 14947c478bd9Sstevel@tonic-gate rpcerr.re_errno = EIO; 14957c478bd9Sstevel@tonic-gate } 14967c478bd9Sstevel@tonic-gate } else { 14977c478bd9Sstevel@tonic-gate /* 14987c478bd9Sstevel@tonic-gate * Test the value of mi_down and mi_printed without 14997c478bd9Sstevel@tonic-gate * holding the mi_lock mutex. If they are both zero, 15007c478bd9Sstevel@tonic-gate * then it is okay to skip the down and printed 15017c478bd9Sstevel@tonic-gate * processing. This saves on a mutex_enter and 15027c478bd9Sstevel@tonic-gate * mutex_exit pair for a normal, successful RPC. 15037c478bd9Sstevel@tonic-gate * This was just complete overhead. 15047c478bd9Sstevel@tonic-gate */ 15057c478bd9Sstevel@tonic-gate if (mi->mi_flags & (MI4_DOWN | MI4_PRINTED)) { 15067c478bd9Sstevel@tonic-gate mutex_enter(&mi->mi_lock); 15077c478bd9Sstevel@tonic-gate mi->mi_flags &= ~MI4_DOWN; 15087c478bd9Sstevel@tonic-gate if (mi->mi_flags & MI4_PRINTED) { 15097c478bd9Sstevel@tonic-gate mi->mi_flags &= ~MI4_PRINTED; 15107c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 15117c478bd9Sstevel@tonic-gate if (!(mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED)) 15127c478bd9Sstevel@tonic-gate nfs4_queue_fact(RF_SRV_OK, mi, 0, 0, 15137c478bd9Sstevel@tonic-gate 0, FALSE, NULL, 0, NULL); 15147c478bd9Sstevel@tonic-gate } else 15157c478bd9Sstevel@tonic-gate mutex_exit(&mi->mi_lock); 15167c478bd9Sstevel@tonic-gate } 15177c478bd9Sstevel@tonic-gate 15187c478bd9Sstevel@tonic-gate if (*doqueue == 0) { 15197c478bd9Sstevel@tonic-gate if (!(mi->mi_flags & MI4_NOPRINT) && 15207c478bd9Sstevel@tonic-gate !(mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED)) 15217c478bd9Sstevel@tonic-gate nfs4_queue_fact(RF_SRV_OK, mi, 0, 0, 0, 15227c478bd9Sstevel@tonic-gate FALSE, NULL, 0, NULL); 15237c478bd9Sstevel@tonic-gate 15247c478bd9Sstevel@tonic-gate *doqueue = 1; 15257c478bd9Sstevel@tonic-gate } 15267c478bd9Sstevel@tonic-gate } 15277c478bd9Sstevel@tonic-gate 15287c478bd9Sstevel@tonic-gate clfree4(client, ch, nfscl); 15297c478bd9Sstevel@tonic-gate 15307c478bd9Sstevel@tonic-gate ASSERT(rpcerr.re_status == RPC_SUCCESS || rpcerr.re_errno != 0); 15317c478bd9Sstevel@tonic-gate 15327c478bd9Sstevel@tonic-gate TRACE_1(TR_FAC_NFS, TR_RFSCALL_END, "nfs4_rfscall_end:errno %d", 15337c478bd9Sstevel@tonic-gate rpcerr.re_errno); 15347c478bd9Sstevel@tonic-gate 15357c478bd9Sstevel@tonic-gate *rpc_statusp = status; 15367c478bd9Sstevel@tonic-gate return (rpcerr.re_errno); 15377c478bd9Sstevel@tonic-gate } 15387c478bd9Sstevel@tonic-gate 15397c478bd9Sstevel@tonic-gate /* 15407c478bd9Sstevel@tonic-gate * rfs4call - general wrapper for RPC calls initiated by the client 15417c478bd9Sstevel@tonic-gate */ 15427c478bd9Sstevel@tonic-gate void 15437c478bd9Sstevel@tonic-gate rfs4call(mntinfo4_t *mi, COMPOUND4args_clnt *argsp, COMPOUND4res_clnt *resp, 15447c478bd9Sstevel@tonic-gate cred_t *cr, int *doqueue, int flags, nfs4_error_t *ep) 15457c478bd9Sstevel@tonic-gate { 15467c478bd9Sstevel@tonic-gate int i, error; 15477c478bd9Sstevel@tonic-gate enum clnt_stat rpc_status = NFS4_OK; 15487c478bd9Sstevel@tonic-gate int num_resops; 15497c478bd9Sstevel@tonic-gate struct nfs4_clnt *nfscl; 15507c478bd9Sstevel@tonic-gate 15517c478bd9Sstevel@tonic-gate ASSERT(curproc->p_zone == mi->mi_zone); 15527c478bd9Sstevel@tonic-gate nfscl = zone_getspecific(nfs4clnt_zone_key, curproc->p_zone); 15537c478bd9Sstevel@tonic-gate ASSERT(nfscl != NULL); 15547c478bd9Sstevel@tonic-gate 15557c478bd9Sstevel@tonic-gate nfscl->nfscl_stat.calls.value.ui64++; 15567c478bd9Sstevel@tonic-gate mi->mi_reqs[NFSPROC4_COMPOUND].value.ui64++; 15577c478bd9Sstevel@tonic-gate 15587c478bd9Sstevel@tonic-gate /* Set up the results struct for XDR usage */ 15597c478bd9Sstevel@tonic-gate resp->argsp = argsp; 15607c478bd9Sstevel@tonic-gate resp->array = NULL; 15617c478bd9Sstevel@tonic-gate resp->status = 0; 15627c478bd9Sstevel@tonic-gate resp->decode_len = 0; 15637c478bd9Sstevel@tonic-gate 15647c478bd9Sstevel@tonic-gate error = nfs4_rfscall(mi, NFSPROC4_COMPOUND, 15657c478bd9Sstevel@tonic-gate xdr_COMPOUND4args_clnt, (caddr_t)argsp, 15667c478bd9Sstevel@tonic-gate xdr_COMPOUND4res_clnt, (caddr_t)resp, cr, 15677c478bd9Sstevel@tonic-gate doqueue, &rpc_status, flags, nfscl); 15687c478bd9Sstevel@tonic-gate 15697c478bd9Sstevel@tonic-gate /* Return now if it was an RPC error */ 15707c478bd9Sstevel@tonic-gate if (error) { 15717c478bd9Sstevel@tonic-gate ep->error = error; 15727c478bd9Sstevel@tonic-gate ep->stat = resp->status; 15737c478bd9Sstevel@tonic-gate ep->rpc_status = rpc_status; 15747c478bd9Sstevel@tonic-gate return; 15757c478bd9Sstevel@tonic-gate } 15767c478bd9Sstevel@tonic-gate 15777c478bd9Sstevel@tonic-gate /* else we'll count the processed operations */ 15787c478bd9Sstevel@tonic-gate num_resops = resp->decode_len; 15797c478bd9Sstevel@tonic-gate for (i = 0; i < num_resops; i++) { 15807c478bd9Sstevel@tonic-gate /* 15817c478bd9Sstevel@tonic-gate * Count the individual operations 15827c478bd9Sstevel@tonic-gate * processed by the server. 15837c478bd9Sstevel@tonic-gate */ 15847c478bd9Sstevel@tonic-gate if (resp->array[i].resop >= NFSPROC4_NULL && 15857c478bd9Sstevel@tonic-gate resp->array[i].resop <= OP_WRITE) 15867c478bd9Sstevel@tonic-gate mi->mi_reqs[resp->array[i].resop].value.ui64++; 15877c478bd9Sstevel@tonic-gate } 15887c478bd9Sstevel@tonic-gate 15897c478bd9Sstevel@tonic-gate ep->error = 0; 15907c478bd9Sstevel@tonic-gate ep->stat = resp->status; 15917c478bd9Sstevel@tonic-gate ep->rpc_status = rpc_status; 15927c478bd9Sstevel@tonic-gate } 15937c478bd9Sstevel@tonic-gate 15947c478bd9Sstevel@tonic-gate /* 15957c478bd9Sstevel@tonic-gate * nfs4rename_update - updates stored state after a rename. Currently this 15967c478bd9Sstevel@tonic-gate * is the path of the object and anything under it, and the filehandle of 15977c478bd9Sstevel@tonic-gate * the renamed object. 15987c478bd9Sstevel@tonic-gate */ 15997c478bd9Sstevel@tonic-gate void 16007c478bd9Sstevel@tonic-gate nfs4rename_update(vnode_t *renvp, vnode_t *ndvp, nfs_fh4 *nfh4p, char *nnm) 16017c478bd9Sstevel@tonic-gate { 16027c478bd9Sstevel@tonic-gate sfh4_update(VTOR4(renvp)->r_fh, nfh4p); 16037c478bd9Sstevel@tonic-gate fn_move(VTOSV(renvp)->sv_name, VTOSV(ndvp)->sv_name, nnm); 16047c478bd9Sstevel@tonic-gate } 16057c478bd9Sstevel@tonic-gate 16067c478bd9Sstevel@tonic-gate /* 16077c478bd9Sstevel@tonic-gate * Routine to look up the filehandle for the given path and rootvp. 16087c478bd9Sstevel@tonic-gate * 16097c478bd9Sstevel@tonic-gate * Return values: 16107c478bd9Sstevel@tonic-gate * - success: returns zero and *statp is set to NFS4_OK, and *fhp is 16117c478bd9Sstevel@tonic-gate * updated. 16127c478bd9Sstevel@tonic-gate * - error: return value (errno value) and/or *statp is set appropriately. 16137c478bd9Sstevel@tonic-gate */ 16147c478bd9Sstevel@tonic-gate #define RML_ORDINARY 1 16157c478bd9Sstevel@tonic-gate #define RML_NAMED_ATTR 2 16167c478bd9Sstevel@tonic-gate #define RML_ATTRDIR 3 16177c478bd9Sstevel@tonic-gate 16187c478bd9Sstevel@tonic-gate static void 16197c478bd9Sstevel@tonic-gate remap_lookup(nfs4_fname_t *fname, vnode_t *rootvp, 16207c478bd9Sstevel@tonic-gate int filetype, cred_t *cr, 16217c478bd9Sstevel@tonic-gate nfs_fh4 *fhp, nfs4_ga_res_t *garp, /* fh, attrs for object */ 16227c478bd9Sstevel@tonic-gate nfs_fh4 *pfhp, nfs4_ga_res_t *pgarp, /* fh, attrs for parent */ 16237c478bd9Sstevel@tonic-gate nfs4_error_t *ep) 16247c478bd9Sstevel@tonic-gate { 16257c478bd9Sstevel@tonic-gate COMPOUND4args_clnt args; 16267c478bd9Sstevel@tonic-gate COMPOUND4res_clnt res; 16277c478bd9Sstevel@tonic-gate nfs_argop4 *argop; 16287c478bd9Sstevel@tonic-gate nfs_resop4 *resop; 16297c478bd9Sstevel@tonic-gate int num_argops; 16307c478bd9Sstevel@tonic-gate lookup4_param_t lookuparg; 16317c478bd9Sstevel@tonic-gate nfs_fh4 *tmpfhp; 16327c478bd9Sstevel@tonic-gate int doqueue = 1; 16337c478bd9Sstevel@tonic-gate char *path; 16347c478bd9Sstevel@tonic-gate mntinfo4_t *mi; 16357c478bd9Sstevel@tonic-gate 16367c478bd9Sstevel@tonic-gate ASSERT(fname != NULL); 16377c478bd9Sstevel@tonic-gate ASSERT(rootvp->v_type == VDIR); 16387c478bd9Sstevel@tonic-gate 16397c478bd9Sstevel@tonic-gate mi = VTOMI4(rootvp); 16407c478bd9Sstevel@tonic-gate path = fn_path(fname); 16417c478bd9Sstevel@tonic-gate switch (filetype) { 16427c478bd9Sstevel@tonic-gate case RML_NAMED_ATTR: 16437c478bd9Sstevel@tonic-gate lookuparg.l4_getattrs = LKP4_LAST_NAMED_ATTR; 16447c478bd9Sstevel@tonic-gate args.ctag = TAG_REMAP_LOOKUP_NA; 16457c478bd9Sstevel@tonic-gate break; 16467c478bd9Sstevel@tonic-gate case RML_ATTRDIR: 16477c478bd9Sstevel@tonic-gate lookuparg.l4_getattrs = LKP4_LAST_ATTRDIR; 16487c478bd9Sstevel@tonic-gate args.ctag = TAG_REMAP_LOOKUP_AD; 16497c478bd9Sstevel@tonic-gate break; 16507c478bd9Sstevel@tonic-gate case RML_ORDINARY: 16517c478bd9Sstevel@tonic-gate lookuparg.l4_getattrs = LKP4_ALL_ATTRIBUTES; 16527c478bd9Sstevel@tonic-gate args.ctag = TAG_REMAP_LOOKUP; 16537c478bd9Sstevel@tonic-gate break; 16547c478bd9Sstevel@tonic-gate default: 16557c478bd9Sstevel@tonic-gate ep->error = EINVAL; 16567c478bd9Sstevel@tonic-gate return; 16577c478bd9Sstevel@tonic-gate } 16587c478bd9Sstevel@tonic-gate lookuparg.argsp = &args; 16597c478bd9Sstevel@tonic-gate lookuparg.resp = &res; 16607c478bd9Sstevel@tonic-gate lookuparg.header_len = 1; /* Putfh */ 16617c478bd9Sstevel@tonic-gate lookuparg.trailer_len = 0; 16627c478bd9Sstevel@tonic-gate lookuparg.ga_bits = NFS4_VATTR_MASK; 16637c478bd9Sstevel@tonic-gate lookuparg.mi = VTOMI4(rootvp); 16647c478bd9Sstevel@tonic-gate 16657c478bd9Sstevel@tonic-gate (void) nfs4lookup_setup(path, &lookuparg, 1); 16667c478bd9Sstevel@tonic-gate 16677c478bd9Sstevel@tonic-gate /* 0: putfh directory */ 16687c478bd9Sstevel@tonic-gate argop = args.array; 16697c478bd9Sstevel@tonic-gate argop[0].argop = OP_CPUTFH; 16707c478bd9Sstevel@tonic-gate argop[0].nfs_argop4_u.opcputfh.sfh = VTOR4(rootvp)->r_fh; 16717c478bd9Sstevel@tonic-gate 16727c478bd9Sstevel@tonic-gate num_argops = args.array_len; 16737c478bd9Sstevel@tonic-gate 16747c478bd9Sstevel@tonic-gate rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, ep); 16757c478bd9Sstevel@tonic-gate 16767c478bd9Sstevel@tonic-gate if (ep->error || res.status != NFS4_OK) 16777c478bd9Sstevel@tonic-gate goto exit; 16787c478bd9Sstevel@tonic-gate 16797c478bd9Sstevel@tonic-gate /* get the object filehandle */ 16807c478bd9Sstevel@tonic-gate resop = &res.array[res.array_len - 2]; 16817c478bd9Sstevel@tonic-gate if (resop->resop != OP_GETFH) { 16827c478bd9Sstevel@tonic-gate nfs4_queue_event(RE_FAIL_REMAP_OP, mi, NULL, 16837c478bd9Sstevel@tonic-gate 0, NULL, NULL, 0, NULL, 0, TAG_NONE, TAG_NONE, 0, 0); 16847c478bd9Sstevel@tonic-gate ep->stat = NFS4ERR_SERVERFAULT; 16857c478bd9Sstevel@tonic-gate goto exit; 16867c478bd9Sstevel@tonic-gate } 16877c478bd9Sstevel@tonic-gate tmpfhp = &resop->nfs_resop4_u.opgetfh.object; 16887c478bd9Sstevel@tonic-gate if (tmpfhp->nfs_fh4_len > NFS4_FHSIZE) { 16897c478bd9Sstevel@tonic-gate nfs4_queue_event(RE_FAIL_REMAP_LEN, mi, NULL, 16907c478bd9Sstevel@tonic-gate tmpfhp->nfs_fh4_len, NULL, NULL, 0, NULL, 0, TAG_NONE, 16917c478bd9Sstevel@tonic-gate TAG_NONE, 0, 0); 16927c478bd9Sstevel@tonic-gate ep->stat = NFS4ERR_SERVERFAULT; 16937c478bd9Sstevel@tonic-gate goto exit; 16947c478bd9Sstevel@tonic-gate } 16957c478bd9Sstevel@tonic-gate fhp->nfs_fh4_val = kmem_alloc(tmpfhp->nfs_fh4_len, KM_SLEEP); 16967c478bd9Sstevel@tonic-gate nfs_fh4_copy(tmpfhp, fhp); 16977c478bd9Sstevel@tonic-gate 16987c478bd9Sstevel@tonic-gate /* get the object attributes */ 16997c478bd9Sstevel@tonic-gate resop = &res.array[res.array_len - 1]; 17007c478bd9Sstevel@tonic-gate if (garp && resop->resop == OP_GETATTR) 17017c478bd9Sstevel@tonic-gate *garp = resop->nfs_resop4_u.opgetattr.ga_res; 17027c478bd9Sstevel@tonic-gate 17037c478bd9Sstevel@tonic-gate /* See if there are enough fields in the response for parent info */ 17047c478bd9Sstevel@tonic-gate if ((int)res.array_len - 5 <= 0) 17057c478bd9Sstevel@tonic-gate goto exit; 17067c478bd9Sstevel@tonic-gate 17077c478bd9Sstevel@tonic-gate /* get the parent filehandle */ 17087c478bd9Sstevel@tonic-gate resop = &res.array[res.array_len - 5]; 17097c478bd9Sstevel@tonic-gate if (resop->resop != OP_GETFH) { 17107c478bd9Sstevel@tonic-gate nfs4_queue_event(RE_FAIL_REMAP_OP, mi, NULL, 17117c478bd9Sstevel@tonic-gate 0, NULL, NULL, 0, NULL, 0, TAG_NONE, TAG_NONE, 0, 0); 17127c478bd9Sstevel@tonic-gate ep->stat = NFS4ERR_SERVERFAULT; 17137c478bd9Sstevel@tonic-gate goto exit; 17147c478bd9Sstevel@tonic-gate } 17157c478bd9Sstevel@tonic-gate tmpfhp = &resop->nfs_resop4_u.opgetfh.object; 17167c478bd9Sstevel@tonic-gate if (tmpfhp->nfs_fh4_len > NFS4_FHSIZE) { 17177c478bd9Sstevel@tonic-gate nfs4_queue_event(RE_FAIL_REMAP_LEN, mi, NULL, 17187c478bd9Sstevel@tonic-gate tmpfhp->nfs_fh4_len, NULL, NULL, 0, NULL, 0, TAG_NONE, 17197c478bd9Sstevel@tonic-gate TAG_NONE, 0, 0); 17207c478bd9Sstevel@tonic-gate ep->stat = NFS4ERR_SERVERFAULT; 17217c478bd9Sstevel@tonic-gate goto exit; 17227c478bd9Sstevel@tonic-gate } 17237c478bd9Sstevel@tonic-gate pfhp->nfs_fh4_val = kmem_alloc(tmpfhp->nfs_fh4_len, KM_SLEEP); 17247c478bd9Sstevel@tonic-gate nfs_fh4_copy(tmpfhp, pfhp); 17257c478bd9Sstevel@tonic-gate 17267c478bd9Sstevel@tonic-gate /* get the parent attributes */ 17277c478bd9Sstevel@tonic-gate resop = &res.array[res.array_len - 4]; 17287c478bd9Sstevel@tonic-gate if (pgarp && resop->resop == OP_GETATTR) 17297c478bd9Sstevel@tonic-gate *pgarp = resop->nfs_resop4_u.opgetattr.ga_res; 17307c478bd9Sstevel@tonic-gate 17317c478bd9Sstevel@tonic-gate exit: 17327c478bd9Sstevel@tonic-gate /* 17337c478bd9Sstevel@tonic-gate * It is too hard to remember where all the OP_LOOKUPs are 17347c478bd9Sstevel@tonic-gate */ 17357c478bd9Sstevel@tonic-gate nfs4args_lookup_free(argop, num_argops); 17367c478bd9Sstevel@tonic-gate kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4)); 17377c478bd9Sstevel@tonic-gate 17387c478bd9Sstevel@tonic-gate if (!ep->error) 17397c478bd9Sstevel@tonic-gate (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 17407c478bd9Sstevel@tonic-gate kmem_free(path, strlen(path)+1); 17417c478bd9Sstevel@tonic-gate } 17427c478bd9Sstevel@tonic-gate 17437c478bd9Sstevel@tonic-gate /* 17447c478bd9Sstevel@tonic-gate * NFS client failover / volatile filehandle support 17457c478bd9Sstevel@tonic-gate * 17467c478bd9Sstevel@tonic-gate * Recover the filehandle for the given rnode. 17477c478bd9Sstevel@tonic-gate * 17487c478bd9Sstevel@tonic-gate * Errors are returned via the nfs4_error_t parameter. 17497c478bd9Sstevel@tonic-gate */ 17507c478bd9Sstevel@tonic-gate 17517c478bd9Sstevel@tonic-gate void 17527c478bd9Sstevel@tonic-gate nfs4_remap_file(mntinfo4_t *mi, vnode_t *vp, int flags, nfs4_error_t *ep) 17537c478bd9Sstevel@tonic-gate { 17547c478bd9Sstevel@tonic-gate rnode4_t *rp = VTOR4(vp); 17557c478bd9Sstevel@tonic-gate vnode_t *rootvp = NULL; 17567c478bd9Sstevel@tonic-gate vnode_t *dvp = NULL; 17577c478bd9Sstevel@tonic-gate cred_t *cr, *cred_otw; 17587c478bd9Sstevel@tonic-gate nfs4_ga_res_t gar, pgar; 17597c478bd9Sstevel@tonic-gate nfs_fh4 newfh = {0, NULL}, newpfh = {0, NULL}; 17607c478bd9Sstevel@tonic-gate int filetype = RML_ORDINARY; 17617c478bd9Sstevel@tonic-gate nfs4_recov_state_t recov = {NULL, 0, 0}; 17627c478bd9Sstevel@tonic-gate int badfhcount = 0; 17637c478bd9Sstevel@tonic-gate nfs4_open_stream_t *osp = NULL; 17647c478bd9Sstevel@tonic-gate bool_t first_time = TRUE; /* first time getting OTW cred */ 17657c478bd9Sstevel@tonic-gate bool_t last_time = FALSE; /* last time getting OTW cred */ 17667c478bd9Sstevel@tonic-gate 17677c478bd9Sstevel@tonic-gate NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE, 17687c478bd9Sstevel@tonic-gate "nfs4_remap_file: remapping %s", rnode4info(rp))); 17697c478bd9Sstevel@tonic-gate ASSERT(nfs4_consistent_type(vp)); 17707c478bd9Sstevel@tonic-gate 17717c478bd9Sstevel@tonic-gate if (vp->v_flag & VROOT) { 17727c478bd9Sstevel@tonic-gate nfs4_remap_root(mi, ep, flags); 17737c478bd9Sstevel@tonic-gate return; 17747c478bd9Sstevel@tonic-gate } 17757c478bd9Sstevel@tonic-gate 17767c478bd9Sstevel@tonic-gate /* 17777c478bd9Sstevel@tonic-gate * Given the root fh, use the path stored in 17787c478bd9Sstevel@tonic-gate * the rnode to find the fh for the new server. 17797c478bd9Sstevel@tonic-gate */ 17807c478bd9Sstevel@tonic-gate ep->error = VFS_ROOT(mi->mi_vfsp, &rootvp); 17817c478bd9Sstevel@tonic-gate if (ep->error != 0) 17827c478bd9Sstevel@tonic-gate return; 17837c478bd9Sstevel@tonic-gate 17847c478bd9Sstevel@tonic-gate cr = curthread->t_cred; 17857c478bd9Sstevel@tonic-gate ASSERT(cr != NULL); 17867c478bd9Sstevel@tonic-gate get_remap_cred: 17877c478bd9Sstevel@tonic-gate /* 17887c478bd9Sstevel@tonic-gate * Releases the osp, if it is provided. 17897c478bd9Sstevel@tonic-gate * Puts a hold on the cred_otw and the new osp (if found). 17907c478bd9Sstevel@tonic-gate */ 17917c478bd9Sstevel@tonic-gate cred_otw = nfs4_get_otw_cred_by_osp(rp, cr, &osp, 17927c478bd9Sstevel@tonic-gate &first_time, &last_time); 17937c478bd9Sstevel@tonic-gate ASSERT(cred_otw != NULL); 17947c478bd9Sstevel@tonic-gate 17957c478bd9Sstevel@tonic-gate if (rp->r_flags & R4ISXATTR) { 17967c478bd9Sstevel@tonic-gate filetype = RML_NAMED_ATTR; 17977c478bd9Sstevel@tonic-gate (void) vtodv(vp, &dvp, cred_otw, FALSE); 17987c478bd9Sstevel@tonic-gate } 17997c478bd9Sstevel@tonic-gate 18007c478bd9Sstevel@tonic-gate if (vp->v_flag & V_XATTRDIR) { 18017c478bd9Sstevel@tonic-gate filetype = RML_ATTRDIR; 18027c478bd9Sstevel@tonic-gate } 18037c478bd9Sstevel@tonic-gate 18047c478bd9Sstevel@tonic-gate if (filetype == RML_ORDINARY && rootvp->v_type == VREG) { 18057c478bd9Sstevel@tonic-gate /* file mount, doesn't need a remap */ 18067c478bd9Sstevel@tonic-gate goto done; 18077c478bd9Sstevel@tonic-gate } 18087c478bd9Sstevel@tonic-gate 18097c478bd9Sstevel@tonic-gate again: 18107c478bd9Sstevel@tonic-gate remap_lookup(rp->r_svnode.sv_name, rootvp, filetype, cred_otw, 18117c478bd9Sstevel@tonic-gate &newfh, &gar, &newpfh, &pgar, ep); 18127c478bd9Sstevel@tonic-gate 18137c478bd9Sstevel@tonic-gate NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE, 18147c478bd9Sstevel@tonic-gate "nfs4_remap_file: remap_lookup returned %d/%d", 18157c478bd9Sstevel@tonic-gate ep->error, ep->stat)); 18167c478bd9Sstevel@tonic-gate 18177c478bd9Sstevel@tonic-gate if (last_time == FALSE && ep->error == EACCES) { 18187c478bd9Sstevel@tonic-gate crfree(cred_otw); 18197c478bd9Sstevel@tonic-gate if (dvp != NULL) 18207c478bd9Sstevel@tonic-gate VN_RELE(dvp); 18217c478bd9Sstevel@tonic-gate goto get_remap_cred; 18227c478bd9Sstevel@tonic-gate } 18237c478bd9Sstevel@tonic-gate if (ep->error != 0) 18247c478bd9Sstevel@tonic-gate goto done; 18257c478bd9Sstevel@tonic-gate 18267c478bd9Sstevel@tonic-gate switch (ep->stat) { 18277c478bd9Sstevel@tonic-gate case NFS4_OK: 18287c478bd9Sstevel@tonic-gate badfhcount = 0; 18297c478bd9Sstevel@tonic-gate if (recov.rs_flags & NFS4_RS_DELAY_MSG) { 18307c478bd9Sstevel@tonic-gate mutex_enter(&rp->r_statelock); 18317c478bd9Sstevel@tonic-gate rp->r_delay_interval = 0; 18327c478bd9Sstevel@tonic-gate mutex_exit(&rp->r_statelock); 18337c478bd9Sstevel@tonic-gate uprintf("NFS File Available..\n"); 18347c478bd9Sstevel@tonic-gate } 18357c478bd9Sstevel@tonic-gate break; 18367c478bd9Sstevel@tonic-gate case NFS4ERR_FHEXPIRED: 18377c478bd9Sstevel@tonic-gate case NFS4ERR_BADHANDLE: 18387c478bd9Sstevel@tonic-gate /* 18397c478bd9Sstevel@tonic-gate * If we ran into filehandle problems, we should try to 18407c478bd9Sstevel@tonic-gate * remap the root vnode first and hope life gets better. 18417c478bd9Sstevel@tonic-gate * But we need to avoid loops. 18427c478bd9Sstevel@tonic-gate */ 18437c478bd9Sstevel@tonic-gate if (badfhcount++ > 0) 18447c478bd9Sstevel@tonic-gate goto done; 18457c478bd9Sstevel@tonic-gate if (newfh.nfs_fh4_len != 0) { 18467c478bd9Sstevel@tonic-gate kmem_free(newfh.nfs_fh4_val, newfh.nfs_fh4_len); 18477c478bd9Sstevel@tonic-gate newfh.nfs_fh4_len = 0; 18487c478bd9Sstevel@tonic-gate } 18497c478bd9Sstevel@tonic-gate if (newpfh.nfs_fh4_len != 0) { 18507c478bd9Sstevel@tonic-gate kmem_free(newpfh.nfs_fh4_val, newpfh.nfs_fh4_len); 18517c478bd9Sstevel@tonic-gate newpfh.nfs_fh4_len = 0; 18527c478bd9Sstevel@tonic-gate } 18537c478bd9Sstevel@tonic-gate /* relative path - remap rootvp then retry */ 18547c478bd9Sstevel@tonic-gate VN_RELE(rootvp); 18557c478bd9Sstevel@tonic-gate rootvp = NULL; 18567c478bd9Sstevel@tonic-gate nfs4_remap_root(mi, ep, flags); 18577c478bd9Sstevel@tonic-gate if (ep->error != 0 || ep->stat != NFS4_OK) 18587c478bd9Sstevel@tonic-gate goto done; 18597c478bd9Sstevel@tonic-gate ep->error = VFS_ROOT(mi->mi_vfsp, &rootvp); 18607c478bd9Sstevel@tonic-gate if (ep->error != 0) 18617c478bd9Sstevel@tonic-gate goto done; 18627c478bd9Sstevel@tonic-gate goto again; 18637c478bd9Sstevel@tonic-gate case NFS4ERR_DELAY: 18647c478bd9Sstevel@tonic-gate badfhcount = 0; 18657c478bd9Sstevel@tonic-gate nfs4_set_delay_wait(vp); 18667c478bd9Sstevel@tonic-gate ep->error = nfs4_wait_for_delay(vp, &recov); 18677c478bd9Sstevel@tonic-gate if (ep->error != 0) 18687c478bd9Sstevel@tonic-gate goto done; 18697c478bd9Sstevel@tonic-gate goto again; 18707c478bd9Sstevel@tonic-gate case NFS4ERR_ACCESS: 18717c478bd9Sstevel@tonic-gate /* get new cred, try again */ 18727c478bd9Sstevel@tonic-gate if (last_time == TRUE) 18737c478bd9Sstevel@tonic-gate goto done; 18747c478bd9Sstevel@tonic-gate if (dvp != NULL) 18757c478bd9Sstevel@tonic-gate VN_RELE(dvp); 18767c478bd9Sstevel@tonic-gate crfree(cred_otw); 18777c478bd9Sstevel@tonic-gate goto get_remap_cred; 18787c478bd9Sstevel@tonic-gate default: 18797c478bd9Sstevel@tonic-gate goto done; 18807c478bd9Sstevel@tonic-gate } 18817c478bd9Sstevel@tonic-gate 18827c478bd9Sstevel@tonic-gate /* 18837c478bd9Sstevel@tonic-gate * Check on the new and old rnodes before updating; 18847c478bd9Sstevel@tonic-gate * if the vnode type or size changes, issue a warning 18857c478bd9Sstevel@tonic-gate * and mark the file dead. 18867c478bd9Sstevel@tonic-gate */ 18877c478bd9Sstevel@tonic-gate mutex_enter(&rp->r_statelock); 18887c478bd9Sstevel@tonic-gate if (flags & NFS4_REMAP_CKATTRS) { 18897c478bd9Sstevel@tonic-gate if (vp->v_type != gar.n4g_va.va_type || 18907c478bd9Sstevel@tonic-gate (vp->v_type != VDIR && 18917c478bd9Sstevel@tonic-gate rp->r_size != gar.n4g_va.va_size)) { 18927c478bd9Sstevel@tonic-gate NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE, 18937c478bd9Sstevel@tonic-gate "nfs4_remap_file: size %d vs. %d, type %d vs. %d", 18947c478bd9Sstevel@tonic-gate (int)rp->r_size, (int)gar.n4g_va.va_size, 18957c478bd9Sstevel@tonic-gate vp->v_type, gar.n4g_va.va_type)); 18967c478bd9Sstevel@tonic-gate mutex_exit(&rp->r_statelock); 18977c478bd9Sstevel@tonic-gate nfs4_queue_event(RE_FILE_DIFF, mi, 18987c478bd9Sstevel@tonic-gate rp->r_server->sv_hostname, 0, vp, NULL, 0, NULL, 0, 18997c478bd9Sstevel@tonic-gate TAG_NONE, TAG_NONE, 0, 0); 19007c478bd9Sstevel@tonic-gate nfs4_fail_recov(vp, NULL, 0, NFS4_OK); 19017c478bd9Sstevel@tonic-gate goto done; 19027c478bd9Sstevel@tonic-gate } 19037c478bd9Sstevel@tonic-gate } 19047c478bd9Sstevel@tonic-gate ASSERT(gar.n4g_va.va_type != VNON); 19057c478bd9Sstevel@tonic-gate rp->r_server = mi->mi_curr_serv; 19067c478bd9Sstevel@tonic-gate 19077c478bd9Sstevel@tonic-gate if (gar.n4g_fsid_valid) { 19087c478bd9Sstevel@tonic-gate (void) nfs_rw_enter_sig(&rp->r_server->sv_lock, RW_READER, 0); 19097c478bd9Sstevel@tonic-gate rp->r_srv_fsid = gar.n4g_fsid; 19107c478bd9Sstevel@tonic-gate if (FATTR4_FSID_EQ(&gar.n4g_fsid, &rp->r_server->sv_fsid)) 19117c478bd9Sstevel@tonic-gate rp->r_flags &= ~R4SRVSTUB; 19127c478bd9Sstevel@tonic-gate else 19137c478bd9Sstevel@tonic-gate rp->r_flags |= R4SRVSTUB; 19147c478bd9Sstevel@tonic-gate nfs_rw_exit(&rp->r_server->sv_lock); 19157c478bd9Sstevel@tonic-gate #ifdef DEBUG 19167c478bd9Sstevel@tonic-gate } else { 19177c478bd9Sstevel@tonic-gate NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE, 19187c478bd9Sstevel@tonic-gate "remap_file: fsid attr not provided by server. rp=%p", 19197c478bd9Sstevel@tonic-gate (void *)rp)); 19207c478bd9Sstevel@tonic-gate #endif 19217c478bd9Sstevel@tonic-gate } 19227c478bd9Sstevel@tonic-gate mutex_exit(&rp->r_statelock); 19237c478bd9Sstevel@tonic-gate nfs4_attrcache_noinval(vp, &gar, gethrtime()); /* force update */ 19247c478bd9Sstevel@tonic-gate sfh4_update(rp->r_fh, &newfh); 19257c478bd9Sstevel@tonic-gate ASSERT(nfs4_consistent_type(vp)); 19267c478bd9Sstevel@tonic-gate 19277c478bd9Sstevel@tonic-gate /* 19287c478bd9Sstevel@tonic-gate * If we got parent info, use it to update the parent 19297c478bd9Sstevel@tonic-gate */ 19307c478bd9Sstevel@tonic-gate if (newpfh.nfs_fh4_len != 0) { 19317c478bd9Sstevel@tonic-gate if (rp->r_svnode.sv_dfh != NULL) 19327c478bd9Sstevel@tonic-gate sfh4_update(rp->r_svnode.sv_dfh, &newpfh); 19337c478bd9Sstevel@tonic-gate if (dvp != NULL) { 19347c478bd9Sstevel@tonic-gate /* force update of attrs */ 19357c478bd9Sstevel@tonic-gate nfs4_attrcache_noinval(dvp, &pgar, gethrtime()); 19367c478bd9Sstevel@tonic-gate } 19377c478bd9Sstevel@tonic-gate } 19387c478bd9Sstevel@tonic-gate done: 19397c478bd9Sstevel@tonic-gate if (newfh.nfs_fh4_len != 0) 19407c478bd9Sstevel@tonic-gate kmem_free(newfh.nfs_fh4_val, newfh.nfs_fh4_len); 19417c478bd9Sstevel@tonic-gate if (newpfh.nfs_fh4_len != 0) 19427c478bd9Sstevel@tonic-gate kmem_free(newpfh.nfs_fh4_val, newpfh.nfs_fh4_len); 19437c478bd9Sstevel@tonic-gate if (cred_otw != NULL) 19447c478bd9Sstevel@tonic-gate crfree(cred_otw); 19457c478bd9Sstevel@tonic-gate if (rootvp != NULL) 19467c478bd9Sstevel@tonic-gate VN_RELE(rootvp); 19477c478bd9Sstevel@tonic-gate if (dvp != NULL) 19487c478bd9Sstevel@tonic-gate VN_RELE(dvp); 19497c478bd9Sstevel@tonic-gate if (osp != NULL) 19507c478bd9Sstevel@tonic-gate open_stream_rele(osp, rp); 19517c478bd9Sstevel@tonic-gate } 19527c478bd9Sstevel@tonic-gate 19537c478bd9Sstevel@tonic-gate /* 19547c478bd9Sstevel@tonic-gate * Client-side failover support: remap the filehandle for vp if it appears 19557c478bd9Sstevel@tonic-gate * necessary. errors are returned via the nfs4_error_t parameter; though, 19567c478bd9Sstevel@tonic-gate * if there is a problem, we will just try again later. 19577c478bd9Sstevel@tonic-gate */ 19587c478bd9Sstevel@tonic-gate 19597c478bd9Sstevel@tonic-gate void 19607c478bd9Sstevel@tonic-gate nfs4_check_remap(mntinfo4_t *mi, vnode_t *vp, int flags, nfs4_error_t *ep) 19617c478bd9Sstevel@tonic-gate { 19627c478bd9Sstevel@tonic-gate if (vp == NULL) 19637c478bd9Sstevel@tonic-gate return; 19647c478bd9Sstevel@tonic-gate 19657c478bd9Sstevel@tonic-gate if (!(vp->v_vfsp->vfs_flag & VFS_RDONLY)) 19667c478bd9Sstevel@tonic-gate return; 19677c478bd9Sstevel@tonic-gate 19687c478bd9Sstevel@tonic-gate if (VTOR4(vp)->r_server == mi->mi_curr_serv) 19697c478bd9Sstevel@tonic-gate return; 19707c478bd9Sstevel@tonic-gate 19717c478bd9Sstevel@tonic-gate nfs4_remap_file(mi, vp, flags, ep); 19727c478bd9Sstevel@tonic-gate } 19737c478bd9Sstevel@tonic-gate 19747c478bd9Sstevel@tonic-gate /* 19757c478bd9Sstevel@tonic-gate * nfs4_make_dotdot() - find or create a parent vnode of a non-root node. 19767c478bd9Sstevel@tonic-gate * 19777c478bd9Sstevel@tonic-gate * Our caller has a filehandle for ".." relative to a particular 19787c478bd9Sstevel@tonic-gate * directory object. We want to find or create a parent vnode 19797c478bd9Sstevel@tonic-gate * with that filehandle and return it. We can of course create 19807c478bd9Sstevel@tonic-gate * a vnode from this filehandle, but we need to also make sure 19817c478bd9Sstevel@tonic-gate * that if ".." is a regular file (i.e. dvp is a V_XATTRDIR) 19827c478bd9Sstevel@tonic-gate * that we have a parent FH for future reopens as well. If 19837c478bd9Sstevel@tonic-gate * we have a remap failure, we won't be able to reopen this 19847c478bd9Sstevel@tonic-gate * file, but we won't treat that as fatal because a reopen 19857c478bd9Sstevel@tonic-gate * is at least unlikely. Someday nfs4_reopen() should look 19867c478bd9Sstevel@tonic-gate * for a missing parent FH and try a remap to recover from it. 19877c478bd9Sstevel@tonic-gate * 19887c478bd9Sstevel@tonic-gate * need_start_op argument indicates whether this function should 19897c478bd9Sstevel@tonic-gate * do a start_op before calling remap_lookup(). This should 19907c478bd9Sstevel@tonic-gate * be FALSE, if you are the recovery thread or in an op; otherwise, 19917c478bd9Sstevel@tonic-gate * set it to TRUE. 19927c478bd9Sstevel@tonic-gate */ 19937c478bd9Sstevel@tonic-gate int 19947c478bd9Sstevel@tonic-gate nfs4_make_dotdot(nfs4_sharedfh_t *fhp, hrtime_t t, vnode_t *dvp, 19957c478bd9Sstevel@tonic-gate cred_t *cr, vnode_t **vpp, int need_start_op) 19967c478bd9Sstevel@tonic-gate { 19977c478bd9Sstevel@tonic-gate mntinfo4_t *mi = VTOMI4(dvp); 19987c478bd9Sstevel@tonic-gate nfs4_fname_t *np = NULL, *pnp = NULL; 19997c478bd9Sstevel@tonic-gate vnode_t *vp = NULL, *rootvp = NULL; 20007c478bd9Sstevel@tonic-gate rnode4_t *rp; 20017c478bd9Sstevel@tonic-gate nfs_fh4 newfh = {0, NULL}, newpfh = {0, NULL}; 20027c478bd9Sstevel@tonic-gate nfs4_ga_res_t gar, pgar; 20037c478bd9Sstevel@tonic-gate vattr_t va, pva; 20047c478bd9Sstevel@tonic-gate nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS }; 20057c478bd9Sstevel@tonic-gate nfs4_sharedfh_t *sfh = NULL, *psfh = NULL; 20067c478bd9Sstevel@tonic-gate nfs4_recov_state_t recov_state; 20077c478bd9Sstevel@tonic-gate 20087c478bd9Sstevel@tonic-gate #ifdef DEBUG 20097c478bd9Sstevel@tonic-gate /* 20107c478bd9Sstevel@tonic-gate * ensure need_start_op is correct 20117c478bd9Sstevel@tonic-gate */ 20127c478bd9Sstevel@tonic-gate { 20137c478bd9Sstevel@tonic-gate int no_need_start_op = (tsd_get(nfs4_tsd_key) || 20147c478bd9Sstevel@tonic-gate (curthread == mi->mi_recovthread)); 20157c478bd9Sstevel@tonic-gate /* C needs a ^^ operator! */ 20167c478bd9Sstevel@tonic-gate ASSERT(((need_start_op) && (!no_need_start_op)) || 20177c478bd9Sstevel@tonic-gate ((! need_start_op) && (no_need_start_op))); 20187c478bd9Sstevel@tonic-gate } 20197c478bd9Sstevel@tonic-gate #endif 20207c478bd9Sstevel@tonic-gate ASSERT(VTOMI4(dvp)->mi_zone == curproc->p_zone); 20217c478bd9Sstevel@tonic-gate 20227c478bd9Sstevel@tonic-gate NFS4_DEBUG(nfs4_client_shadow_debug, (CE_NOTE, 20237c478bd9Sstevel@tonic-gate "nfs4_make_dotdot: called with fhp %p, dvp %s", (void *)fhp, 20247c478bd9Sstevel@tonic-gate rnode4info(VTOR4(dvp)))); 20257c478bd9Sstevel@tonic-gate 20267c478bd9Sstevel@tonic-gate /* 20277c478bd9Sstevel@tonic-gate * rootvp might be needed eventually. Holding it now will 20287c478bd9Sstevel@tonic-gate * ensure that r4find_unlocked() will find it, if ".." is the root. 20297c478bd9Sstevel@tonic-gate */ 20307c478bd9Sstevel@tonic-gate e.error = VFS_ROOT(mi->mi_vfsp, &rootvp); 20317c478bd9Sstevel@tonic-gate if (e.error != 0) 20327c478bd9Sstevel@tonic-gate goto out; 20337c478bd9Sstevel@tonic-gate rp = r4find_unlocked(fhp, mi->mi_vfsp); 20347c478bd9Sstevel@tonic-gate if (rp != NULL) { 20357c478bd9Sstevel@tonic-gate *vpp = RTOV4(rp); 20367c478bd9Sstevel@tonic-gate VN_RELE(rootvp); 20377c478bd9Sstevel@tonic-gate return (0); 20387c478bd9Sstevel@tonic-gate } 20397c478bd9Sstevel@tonic-gate 20407c478bd9Sstevel@tonic-gate /* 20417c478bd9Sstevel@tonic-gate * Since we don't have the rnode, we have to go over the wire. 20427c478bd9Sstevel@tonic-gate * remap_lookup() can get all of the filehandles and attributes 20437c478bd9Sstevel@tonic-gate * we need in one operation. 20447c478bd9Sstevel@tonic-gate */ 20457c478bd9Sstevel@tonic-gate np = fn_parent(VTOSV(dvp)->sv_name); 20467c478bd9Sstevel@tonic-gate ASSERT(np != NULL); 20477c478bd9Sstevel@tonic-gate 20487c478bd9Sstevel@tonic-gate recov_state.rs_flags = 0; 20497c478bd9Sstevel@tonic-gate recov_state.rs_num_retry_despite_err = 0; 20507c478bd9Sstevel@tonic-gate recov_retry: 20517c478bd9Sstevel@tonic-gate if (need_start_op) { 20527c478bd9Sstevel@tonic-gate e.error = nfs4_start_fop(mi, rootvp, NULL, OH_LOOKUP, 20537c478bd9Sstevel@tonic-gate &recov_state, NULL); 20547c478bd9Sstevel@tonic-gate if (e.error != 0) { 20557c478bd9Sstevel@tonic-gate goto out; 20567c478bd9Sstevel@tonic-gate } 20577c478bd9Sstevel@tonic-gate } 20587c478bd9Sstevel@tonic-gate va.va_type = VNON; 20597c478bd9Sstevel@tonic-gate pva.va_type = VNON; 20607c478bd9Sstevel@tonic-gate remap_lookup(np, rootvp, RML_ORDINARY, cr, 20617c478bd9Sstevel@tonic-gate &newfh, &gar, &newpfh, &pgar, &e); 20627c478bd9Sstevel@tonic-gate if (nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp)) { 20637c478bd9Sstevel@tonic-gate if (need_start_op) { 20647c478bd9Sstevel@tonic-gate bool_t abort; 20657c478bd9Sstevel@tonic-gate 20667c478bd9Sstevel@tonic-gate abort = nfs4_start_recovery(&e, mi, 20677c478bd9Sstevel@tonic-gate rootvp, NULL, NULL, NULL, OP_LOOKUP, NULL); 20687c478bd9Sstevel@tonic-gate if (abort) { 20697c478bd9Sstevel@tonic-gate nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP, 20707c478bd9Sstevel@tonic-gate &recov_state, FALSE); 20717c478bd9Sstevel@tonic-gate if (e.error == 0) 20727c478bd9Sstevel@tonic-gate e.error = EIO; 20737c478bd9Sstevel@tonic-gate goto out; 20747c478bd9Sstevel@tonic-gate } 20757c478bd9Sstevel@tonic-gate nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP, 20767c478bd9Sstevel@tonic-gate &recov_state, TRUE); 20777c478bd9Sstevel@tonic-gate goto recov_retry; 20787c478bd9Sstevel@tonic-gate } 20797c478bd9Sstevel@tonic-gate if (e.error == 0) 20807c478bd9Sstevel@tonic-gate e.error = EIO; 20817c478bd9Sstevel@tonic-gate goto out; 20827c478bd9Sstevel@tonic-gate } 20837c478bd9Sstevel@tonic-gate 20847c478bd9Sstevel@tonic-gate if (!e.error) { 20857c478bd9Sstevel@tonic-gate va = gar.n4g_va; 20867c478bd9Sstevel@tonic-gate pva = pgar.n4g_va; 20877c478bd9Sstevel@tonic-gate } 20887c478bd9Sstevel@tonic-gate 20897c478bd9Sstevel@tonic-gate if ((e.error != 0) || 20907c478bd9Sstevel@tonic-gate (va.va_type != VDIR)) { 20917c478bd9Sstevel@tonic-gate if (e.error == 0) 20927c478bd9Sstevel@tonic-gate e.error = EIO; 20937c478bd9Sstevel@tonic-gate goto out; 20947c478bd9Sstevel@tonic-gate } 20957c478bd9Sstevel@tonic-gate 20967c478bd9Sstevel@tonic-gate if (e.stat != NFS4_OK) { 20977c478bd9Sstevel@tonic-gate e.error = EIO; 20987c478bd9Sstevel@tonic-gate goto out; 20997c478bd9Sstevel@tonic-gate } 21007c478bd9Sstevel@tonic-gate 21017c478bd9Sstevel@tonic-gate /* 21027c478bd9Sstevel@tonic-gate * It is possible for remap_lookup() to return with no error, 21037c478bd9Sstevel@tonic-gate * but without providing the parent filehandle and attrs. 21047c478bd9Sstevel@tonic-gate */ 21057c478bd9Sstevel@tonic-gate if (pva.va_type != VDIR) { 21067c478bd9Sstevel@tonic-gate /* 21077c478bd9Sstevel@tonic-gate * Call remap_lookup() again, this time with the 21087c478bd9Sstevel@tonic-gate * newpfh and pgar args in the first position. 21097c478bd9Sstevel@tonic-gate */ 21107c478bd9Sstevel@tonic-gate pnp = fn_parent(np); 21117c478bd9Sstevel@tonic-gate if (pnp != NULL) { 21127c478bd9Sstevel@tonic-gate remap_lookup(pnp, rootvp, RML_ORDINARY, cr, 21137c478bd9Sstevel@tonic-gate &newpfh, &pgar, NULL, NULL, &e); 21147c478bd9Sstevel@tonic-gate if (nfs4_needs_recovery(&e, FALSE, 21157c478bd9Sstevel@tonic-gate mi->mi_vfsp)) { 21167c478bd9Sstevel@tonic-gate if (need_start_op) { 21177c478bd9Sstevel@tonic-gate bool_t abort; 21187c478bd9Sstevel@tonic-gate 21197c478bd9Sstevel@tonic-gate abort = nfs4_start_recovery(&e, mi, 21207c478bd9Sstevel@tonic-gate rootvp, NULL, NULL, NULL, 21217c478bd9Sstevel@tonic-gate OP_LOOKUP, NULL); 21227c478bd9Sstevel@tonic-gate if (abort) { 21237c478bd9Sstevel@tonic-gate nfs4_end_fop(mi, rootvp, NULL, 21247c478bd9Sstevel@tonic-gate OH_LOOKUP, &recov_state, 21257c478bd9Sstevel@tonic-gate FALSE); 21267c478bd9Sstevel@tonic-gate if (e.error == 0) 21277c478bd9Sstevel@tonic-gate e.error = EIO; 21287c478bd9Sstevel@tonic-gate goto out; 21297c478bd9Sstevel@tonic-gate } 21307c478bd9Sstevel@tonic-gate nfs4_end_fop(mi, rootvp, NULL, 21317c478bd9Sstevel@tonic-gate OH_LOOKUP, &recov_state, TRUE); 21327c478bd9Sstevel@tonic-gate goto recov_retry; 21337c478bd9Sstevel@tonic-gate } 21347c478bd9Sstevel@tonic-gate if (e.error == 0) 21357c478bd9Sstevel@tonic-gate e.error = EIO; 21367c478bd9Sstevel@tonic-gate goto out; 21377c478bd9Sstevel@tonic-gate } 21387c478bd9Sstevel@tonic-gate 21397c478bd9Sstevel@tonic-gate if (e.stat != NFS4_OK) { 21407c478bd9Sstevel@tonic-gate e.error = EIO; 21417c478bd9Sstevel@tonic-gate goto out; 21427c478bd9Sstevel@tonic-gate } 21437c478bd9Sstevel@tonic-gate } 21447c478bd9Sstevel@tonic-gate if ((pnp == NULL) || 21457c478bd9Sstevel@tonic-gate (e.error != 0) || 21467c478bd9Sstevel@tonic-gate (pva.va_type == VNON)) { 21477c478bd9Sstevel@tonic-gate if (need_start_op) 21487c478bd9Sstevel@tonic-gate nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP, 21497c478bd9Sstevel@tonic-gate &recov_state, FALSE); 21507c478bd9Sstevel@tonic-gate if (e.error == 0) 21517c478bd9Sstevel@tonic-gate e.error = EIO; 21527c478bd9Sstevel@tonic-gate goto out; 21537c478bd9Sstevel@tonic-gate } 21547c478bd9Sstevel@tonic-gate } 21557c478bd9Sstevel@tonic-gate ASSERT(newpfh.nfs_fh4_len != 0); 21567c478bd9Sstevel@tonic-gate if (need_start_op) 21577c478bd9Sstevel@tonic-gate nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP, &recov_state, FALSE); 21587c478bd9Sstevel@tonic-gate psfh = sfh4_get(&newpfh, mi); 21597c478bd9Sstevel@tonic-gate 21607c478bd9Sstevel@tonic-gate sfh = sfh4_get(&newfh, mi); 21617c478bd9Sstevel@tonic-gate vp = makenfs4node_by_fh(sfh, psfh, &np, &gar, mi, cr, t); 21627c478bd9Sstevel@tonic-gate 21637c478bd9Sstevel@tonic-gate out: 21647c478bd9Sstevel@tonic-gate if (np != NULL) 21657c478bd9Sstevel@tonic-gate fn_rele(&np); 21667c478bd9Sstevel@tonic-gate if (pnp != NULL) 21677c478bd9Sstevel@tonic-gate fn_rele(&pnp); 21687c478bd9Sstevel@tonic-gate if (newfh.nfs_fh4_len != 0) 21697c478bd9Sstevel@tonic-gate kmem_free(newfh.nfs_fh4_val, newfh.nfs_fh4_len); 21707c478bd9Sstevel@tonic-gate if (newpfh.nfs_fh4_len != 0) 21717c478bd9Sstevel@tonic-gate kmem_free(newpfh.nfs_fh4_val, newpfh.nfs_fh4_len); 21727c478bd9Sstevel@tonic-gate if (sfh != NULL) 21737c478bd9Sstevel@tonic-gate sfh4_rele(&sfh); 21747c478bd9Sstevel@tonic-gate if (psfh != NULL) 21757c478bd9Sstevel@tonic-gate sfh4_rele(&psfh); 21767c478bd9Sstevel@tonic-gate if (rootvp != NULL) 21777c478bd9Sstevel@tonic-gate VN_RELE(rootvp); 21787c478bd9Sstevel@tonic-gate *vpp = vp; 21797c478bd9Sstevel@tonic-gate return (e.error); 21807c478bd9Sstevel@tonic-gate } 21817c478bd9Sstevel@tonic-gate 21827c478bd9Sstevel@tonic-gate #ifdef DEBUG 21837c478bd9Sstevel@tonic-gate size_t r_path_memuse = 0; 21847c478bd9Sstevel@tonic-gate #endif 21857c478bd9Sstevel@tonic-gate 21867c478bd9Sstevel@tonic-gate /* 21877c478bd9Sstevel@tonic-gate * NFS client failover support 21887c478bd9Sstevel@tonic-gate * 21897c478bd9Sstevel@tonic-gate * sv4_free() frees the malloc'd portion of a "servinfo_t". 21907c478bd9Sstevel@tonic-gate */ 21917c478bd9Sstevel@tonic-gate void 21927c478bd9Sstevel@tonic-gate sv4_free(servinfo4_t *svp) 21937c478bd9Sstevel@tonic-gate { 21947c478bd9Sstevel@tonic-gate servinfo4_t *next; 21957c478bd9Sstevel@tonic-gate struct knetconfig *knconf; 21967c478bd9Sstevel@tonic-gate 21977c478bd9Sstevel@tonic-gate while (svp != NULL) { 21987c478bd9Sstevel@tonic-gate next = svp->sv_next; 21997c478bd9Sstevel@tonic-gate if (svp->sv_dhsec) 22007c478bd9Sstevel@tonic-gate sec_clnt_freeinfo(svp->sv_dhsec); 22017c478bd9Sstevel@tonic-gate if (svp->sv_secdata) 22027c478bd9Sstevel@tonic-gate sec_clnt_freeinfo(svp->sv_secdata); 22037c478bd9Sstevel@tonic-gate if (svp->sv_save_secinfo && 22047c478bd9Sstevel@tonic-gate svp->sv_save_secinfo != svp->sv_secinfo) 22057c478bd9Sstevel@tonic-gate secinfo_free(svp->sv_save_secinfo); 22067c478bd9Sstevel@tonic-gate if (svp->sv_secinfo) 22077c478bd9Sstevel@tonic-gate secinfo_free(svp->sv_secinfo); 22087c478bd9Sstevel@tonic-gate if (svp->sv_hostname && svp->sv_hostnamelen > 0) 22097c478bd9Sstevel@tonic-gate kmem_free(svp->sv_hostname, svp->sv_hostnamelen); 22107c478bd9Sstevel@tonic-gate knconf = svp->sv_knconf; 22117c478bd9Sstevel@tonic-gate if (knconf != NULL) { 22127c478bd9Sstevel@tonic-gate if (knconf->knc_protofmly != NULL) 22137c478bd9Sstevel@tonic-gate kmem_free(knconf->knc_protofmly, KNC_STRSIZE); 22147c478bd9Sstevel@tonic-gate if (knconf->knc_proto != NULL) 22157c478bd9Sstevel@tonic-gate kmem_free(knconf->knc_proto, KNC_STRSIZE); 22167c478bd9Sstevel@tonic-gate kmem_free(knconf, sizeof (*knconf)); 22177c478bd9Sstevel@tonic-gate } 22187c478bd9Sstevel@tonic-gate knconf = svp->sv_origknconf; 22197c478bd9Sstevel@tonic-gate if (knconf != NULL) { 22207c478bd9Sstevel@tonic-gate if (knconf->knc_protofmly != NULL) 22217c478bd9Sstevel@tonic-gate kmem_free(knconf->knc_protofmly, KNC_STRSIZE); 22227c478bd9Sstevel@tonic-gate if (knconf->knc_proto != NULL) 22237c478bd9Sstevel@tonic-gate kmem_free(knconf->knc_proto, KNC_STRSIZE); 22247c478bd9Sstevel@tonic-gate kmem_free(knconf, sizeof (*knconf)); 22257c478bd9Sstevel@tonic-gate } 22267c478bd9Sstevel@tonic-gate if (svp->sv_addr.buf != NULL && svp->sv_addr.maxlen != 0) 22277c478bd9Sstevel@tonic-gate kmem_free(svp->sv_addr.buf, svp->sv_addr.maxlen); 22287c478bd9Sstevel@tonic-gate if (svp->sv_path != NULL) { 22297c478bd9Sstevel@tonic-gate kmem_free(svp->sv_path, svp->sv_pathlen); 22307c478bd9Sstevel@tonic-gate } 22317c478bd9Sstevel@tonic-gate nfs_rw_destroy(&svp->sv_lock); 22327c478bd9Sstevel@tonic-gate kmem_free(svp, sizeof (*svp)); 22337c478bd9Sstevel@tonic-gate svp = next; 22347c478bd9Sstevel@tonic-gate } 22357c478bd9Sstevel@tonic-gate } 22367c478bd9Sstevel@tonic-gate 22377c478bd9Sstevel@tonic-gate void 22387c478bd9Sstevel@tonic-gate nfs4_printfhandle(nfs4_fhandle_t *fhp) 22397c478bd9Sstevel@tonic-gate { 22407c478bd9Sstevel@tonic-gate int *ip; 22417c478bd9Sstevel@tonic-gate char *buf; 22427c478bd9Sstevel@tonic-gate size_t bufsize; 22437c478bd9Sstevel@tonic-gate char *cp; 22447c478bd9Sstevel@tonic-gate 22457c478bd9Sstevel@tonic-gate /* 22467c478bd9Sstevel@tonic-gate * 13 == "(file handle:" 22477c478bd9Sstevel@tonic-gate * maximum of NFS_FHANDLE / sizeof (*ip) elements in fh_buf times 22487c478bd9Sstevel@tonic-gate * 1 == ' ' 22497c478bd9Sstevel@tonic-gate * 8 == maximum strlen of "%x" 22507c478bd9Sstevel@tonic-gate * 3 == ")\n\0" 22517c478bd9Sstevel@tonic-gate */ 22527c478bd9Sstevel@tonic-gate bufsize = 13 + ((NFS_FHANDLE_LEN / sizeof (*ip)) * (1 + 8)) + 3; 22537c478bd9Sstevel@tonic-gate buf = kmem_alloc(bufsize, KM_NOSLEEP); 22547c478bd9Sstevel@tonic-gate if (buf == NULL) 22557c478bd9Sstevel@tonic-gate return; 22567c478bd9Sstevel@tonic-gate 22577c478bd9Sstevel@tonic-gate cp = buf; 22587c478bd9Sstevel@tonic-gate (void) strcpy(cp, "(file handle:"); 22597c478bd9Sstevel@tonic-gate while (*cp != '\0') 22607c478bd9Sstevel@tonic-gate cp++; 22617c478bd9Sstevel@tonic-gate for (ip = (int *)fhp->fh_buf; 22627c478bd9Sstevel@tonic-gate ip < (int *)&fhp->fh_buf[fhp->fh_len]; 22637c478bd9Sstevel@tonic-gate ip++) { 22647c478bd9Sstevel@tonic-gate (void) sprintf(cp, " %x", *ip); 22657c478bd9Sstevel@tonic-gate while (*cp != '\0') 22667c478bd9Sstevel@tonic-gate cp++; 22677c478bd9Sstevel@tonic-gate } 22687c478bd9Sstevel@tonic-gate (void) strcpy(cp, ")\n"); 22697c478bd9Sstevel@tonic-gate 22707c478bd9Sstevel@tonic-gate zcmn_err(getzoneid(), CE_CONT, "%s", buf); 22717c478bd9Sstevel@tonic-gate 22727c478bd9Sstevel@tonic-gate kmem_free(buf, bufsize); 22737c478bd9Sstevel@tonic-gate } 22747c478bd9Sstevel@tonic-gate 22757c478bd9Sstevel@tonic-gate /* 22767c478bd9Sstevel@tonic-gate * The NFSv4 readdir cache subsystem. 22777c478bd9Sstevel@tonic-gate * 22787c478bd9Sstevel@tonic-gate * We provide a set of interfaces to allow the rest of the system to utilize 22797c478bd9Sstevel@tonic-gate * a caching mechanism while encapsulating the details of the actual 22807c478bd9Sstevel@tonic-gate * implementation. This should allow for better maintainability and 22817c478bd9Sstevel@tonic-gate * extensibilty by consolidating the implementation details in one location. 22827c478bd9Sstevel@tonic-gate */ 22837c478bd9Sstevel@tonic-gate 22847c478bd9Sstevel@tonic-gate /* 22857c478bd9Sstevel@tonic-gate * Comparator used by AVL routines. 22867c478bd9Sstevel@tonic-gate */ 22877c478bd9Sstevel@tonic-gate static int 22887c478bd9Sstevel@tonic-gate rddir4_cache_compar(const void *x, const void *y) 22897c478bd9Sstevel@tonic-gate { 22907c478bd9Sstevel@tonic-gate rddir4_cache_impl *ai = (rddir4_cache_impl *)x; 22917c478bd9Sstevel@tonic-gate rddir4_cache_impl *bi = (rddir4_cache_impl *)y; 22927c478bd9Sstevel@tonic-gate rddir4_cache *a = &ai->rc; 22937c478bd9Sstevel@tonic-gate rddir4_cache *b = &bi->rc; 22947c478bd9Sstevel@tonic-gate 22957c478bd9Sstevel@tonic-gate if (a->nfs4_cookie == b->nfs4_cookie) { 22967c478bd9Sstevel@tonic-gate if (a->buflen == b->buflen) 22977c478bd9Sstevel@tonic-gate return (0); 22987c478bd9Sstevel@tonic-gate if (a->buflen < b->buflen) 22997c478bd9Sstevel@tonic-gate return (-1); 23007c478bd9Sstevel@tonic-gate return (1); 23017c478bd9Sstevel@tonic-gate } 23027c478bd9Sstevel@tonic-gate 23037c478bd9Sstevel@tonic-gate if (a->nfs4_cookie < b->nfs4_cookie) 23047c478bd9Sstevel@tonic-gate return (-1); 23057c478bd9Sstevel@tonic-gate 23067c478bd9Sstevel@tonic-gate return (1); 23077c478bd9Sstevel@tonic-gate } 23087c478bd9Sstevel@tonic-gate 23097c478bd9Sstevel@tonic-gate /* 23107c478bd9Sstevel@tonic-gate * Allocate an opaque handle for the readdir cache. 23117c478bd9Sstevel@tonic-gate */ 23127c478bd9Sstevel@tonic-gate void 23137c478bd9Sstevel@tonic-gate rddir4_cache_create(rnode4_t *rp) 23147c478bd9Sstevel@tonic-gate { 23157c478bd9Sstevel@tonic-gate ASSERT(rp->r_dir == NULL); 23167c478bd9Sstevel@tonic-gate 23177c478bd9Sstevel@tonic-gate rp->r_dir = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP); 23187c478bd9Sstevel@tonic-gate 23197c478bd9Sstevel@tonic-gate avl_create(rp->r_dir, rddir4_cache_compar, sizeof (rddir4_cache_impl), 23207c478bd9Sstevel@tonic-gate offsetof(rddir4_cache_impl, tree)); 23217c478bd9Sstevel@tonic-gate } 23227c478bd9Sstevel@tonic-gate 23237c478bd9Sstevel@tonic-gate /* 23247c478bd9Sstevel@tonic-gate * Purge the cache of all cached readdir responses. 23257c478bd9Sstevel@tonic-gate */ 23267c478bd9Sstevel@tonic-gate void 23277c478bd9Sstevel@tonic-gate rddir4_cache_purge(rnode4_t *rp) 23287c478bd9Sstevel@tonic-gate { 23297c478bd9Sstevel@tonic-gate rddir4_cache_impl *rdip; 23307c478bd9Sstevel@tonic-gate rddir4_cache_impl *nrdip; 23317c478bd9Sstevel@tonic-gate 23327c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&rp->r_statelock)); 23337c478bd9Sstevel@tonic-gate 23347c478bd9Sstevel@tonic-gate if (rp->r_dir == NULL) 23357c478bd9Sstevel@tonic-gate return; 23367c478bd9Sstevel@tonic-gate 23377c478bd9Sstevel@tonic-gate rdip = avl_first(rp->r_dir); 23387c478bd9Sstevel@tonic-gate 23397c478bd9Sstevel@tonic-gate while (rdip != NULL) { 23407c478bd9Sstevel@tonic-gate nrdip = AVL_NEXT(rp->r_dir, rdip); 23417c478bd9Sstevel@tonic-gate avl_remove(rp->r_dir, rdip); 23427c478bd9Sstevel@tonic-gate rdip->rc.flags &= ~RDDIRCACHED; 23437c478bd9Sstevel@tonic-gate rddir4_cache_rele(rp, &rdip->rc); 23447c478bd9Sstevel@tonic-gate rdip = nrdip; 23457c478bd9Sstevel@tonic-gate } 23467c478bd9Sstevel@tonic-gate ASSERT(avl_numnodes(rp->r_dir) == 0); 23477c478bd9Sstevel@tonic-gate } 23487c478bd9Sstevel@tonic-gate 23497c478bd9Sstevel@tonic-gate /* 23507c478bd9Sstevel@tonic-gate * Destroy the readdir cache. 23517c478bd9Sstevel@tonic-gate */ 23527c478bd9Sstevel@tonic-gate void 23537c478bd9Sstevel@tonic-gate rddir4_cache_destroy(rnode4_t *rp) 23547c478bd9Sstevel@tonic-gate { 23557c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&rp->r_statelock)); 23567c478bd9Sstevel@tonic-gate if (rp->r_dir == NULL) 23577c478bd9Sstevel@tonic-gate return; 23587c478bd9Sstevel@tonic-gate 23597c478bd9Sstevel@tonic-gate rddir4_cache_purge(rp); 23607c478bd9Sstevel@tonic-gate avl_destroy(rp->r_dir); 23617c478bd9Sstevel@tonic-gate kmem_free(rp->r_dir, sizeof (avl_tree_t)); 23627c478bd9Sstevel@tonic-gate rp->r_dir = NULL; 23637c478bd9Sstevel@tonic-gate } 23647c478bd9Sstevel@tonic-gate 23657c478bd9Sstevel@tonic-gate /* 23667c478bd9Sstevel@tonic-gate * Locate a readdir response from the readdir cache. 23677c478bd9Sstevel@tonic-gate * 23687c478bd9Sstevel@tonic-gate * Return values: 23697c478bd9Sstevel@tonic-gate * 23707c478bd9Sstevel@tonic-gate * NULL - If there is an unrecoverable situation like the operation may have 23717c478bd9Sstevel@tonic-gate * been interrupted. 23727c478bd9Sstevel@tonic-gate * 23737c478bd9Sstevel@tonic-gate * rddir4_cache * - A pointer to a rddir4_cache is returned to the caller. 23747c478bd9Sstevel@tonic-gate * The flags are set approprately, such that the caller knows 23757c478bd9Sstevel@tonic-gate * what state the entry is in. 23767c478bd9Sstevel@tonic-gate */ 23777c478bd9Sstevel@tonic-gate rddir4_cache * 23787c478bd9Sstevel@tonic-gate rddir4_cache_lookup(rnode4_t *rp, offset_t cookie, int count) 23797c478bd9Sstevel@tonic-gate { 23807c478bd9Sstevel@tonic-gate rddir4_cache_impl *rdip = NULL; 23817c478bd9Sstevel@tonic-gate rddir4_cache_impl srdip; 23827c478bd9Sstevel@tonic-gate rddir4_cache *srdc; 23837c478bd9Sstevel@tonic-gate rddir4_cache *rdc = NULL; 23847c478bd9Sstevel@tonic-gate rddir4_cache *nrdc = NULL; 23857c478bd9Sstevel@tonic-gate avl_index_t where; 23867c478bd9Sstevel@tonic-gate 23877c478bd9Sstevel@tonic-gate top: 23887c478bd9Sstevel@tonic-gate ASSERT(nfs_rw_lock_held(&rp->r_rwlock, RW_READER)); 23897c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&rp->r_statelock)); 23907c478bd9Sstevel@tonic-gate /* 23917c478bd9Sstevel@tonic-gate * Check to see if the readdir cache has been disabled. If so, then 23927c478bd9Sstevel@tonic-gate * simply allocate an rddir4_cache entry and return it, since caching 23937c478bd9Sstevel@tonic-gate * operations do not apply. 23947c478bd9Sstevel@tonic-gate */ 23957c478bd9Sstevel@tonic-gate if (rp->r_dir == NULL) { 23967c478bd9Sstevel@tonic-gate if (nrdc == NULL) { 23977c478bd9Sstevel@tonic-gate /* 23987c478bd9Sstevel@tonic-gate * Drop the lock because we are doing a sleeping 23997c478bd9Sstevel@tonic-gate * allocation. 24007c478bd9Sstevel@tonic-gate */ 24017c478bd9Sstevel@tonic-gate mutex_exit(&rp->r_statelock); 24027c478bd9Sstevel@tonic-gate rdc = rddir4_cache_alloc(KM_SLEEP); 24037c478bd9Sstevel@tonic-gate rdc->nfs4_cookie = cookie; 24047c478bd9Sstevel@tonic-gate rdc->buflen = count; 24057c478bd9Sstevel@tonic-gate mutex_enter(&rp->r_statelock); 24067c478bd9Sstevel@tonic-gate return (rdc); 24077c478bd9Sstevel@tonic-gate } 24087c478bd9Sstevel@tonic-gate return (nrdc); 24097c478bd9Sstevel@tonic-gate } 24107c478bd9Sstevel@tonic-gate 24117c478bd9Sstevel@tonic-gate srdc = &srdip.rc; 24127c478bd9Sstevel@tonic-gate srdc->nfs4_cookie = cookie; 24137c478bd9Sstevel@tonic-gate srdc->buflen = count; 24147c478bd9Sstevel@tonic-gate 24157c478bd9Sstevel@tonic-gate rdip = avl_find(rp->r_dir, &srdip, &where); 24167c478bd9Sstevel@tonic-gate 24177c478bd9Sstevel@tonic-gate /* 24187c478bd9Sstevel@tonic-gate * If we didn't find an entry then create one and insert it 24197c478bd9Sstevel@tonic-gate * into the cache. 24207c478bd9Sstevel@tonic-gate */ 24217c478bd9Sstevel@tonic-gate if (rdip == NULL) { 24227c478bd9Sstevel@tonic-gate /* 24237c478bd9Sstevel@tonic-gate * Check for the case where we have made a second pass through 24247c478bd9Sstevel@tonic-gate * the cache due to a lockless allocation. If we find that no 24257c478bd9Sstevel@tonic-gate * thread has already inserted this entry, do the insert now 24267c478bd9Sstevel@tonic-gate * and return. 24277c478bd9Sstevel@tonic-gate */ 24287c478bd9Sstevel@tonic-gate if (nrdc != NULL) { 24297c478bd9Sstevel@tonic-gate avl_insert(rp->r_dir, nrdc->data, where); 24307c478bd9Sstevel@tonic-gate nrdc->flags |= RDDIRCACHED; 24317c478bd9Sstevel@tonic-gate rddir4_cache_hold(nrdc); 24327c478bd9Sstevel@tonic-gate return (nrdc); 24337c478bd9Sstevel@tonic-gate } 24347c478bd9Sstevel@tonic-gate 24357c478bd9Sstevel@tonic-gate #ifdef DEBUG 24367c478bd9Sstevel@tonic-gate nfs4_readdir_cache_misses++; 24377c478bd9Sstevel@tonic-gate #endif 24387c478bd9Sstevel@tonic-gate /* 24397c478bd9Sstevel@tonic-gate * First, try to allocate an entry without sleeping. If that 24407c478bd9Sstevel@tonic-gate * fails then drop the lock and do a sleeping allocation. 24417c478bd9Sstevel@tonic-gate */ 24427c478bd9Sstevel@tonic-gate nrdc = rddir4_cache_alloc(KM_NOSLEEP); 24437c478bd9Sstevel@tonic-gate if (nrdc != NULL) { 24447c478bd9Sstevel@tonic-gate nrdc->nfs4_cookie = cookie; 24457c478bd9Sstevel@tonic-gate nrdc->buflen = count; 24467c478bd9Sstevel@tonic-gate avl_insert(rp->r_dir, nrdc->data, where); 24477c478bd9Sstevel@tonic-gate nrdc->flags |= RDDIRCACHED; 24487c478bd9Sstevel@tonic-gate rddir4_cache_hold(nrdc); 24497c478bd9Sstevel@tonic-gate return (nrdc); 24507c478bd9Sstevel@tonic-gate } 24517c478bd9Sstevel@tonic-gate 24527c478bd9Sstevel@tonic-gate /* 24537c478bd9Sstevel@tonic-gate * Drop the lock and do a sleeping allocation. We incur 24547c478bd9Sstevel@tonic-gate * additional overhead by having to search the cache again, 24557c478bd9Sstevel@tonic-gate * but this case should be rare. 24567c478bd9Sstevel@tonic-gate */ 24577c478bd9Sstevel@tonic-gate mutex_exit(&rp->r_statelock); 24587c478bd9Sstevel@tonic-gate nrdc = rddir4_cache_alloc(KM_SLEEP); 24597c478bd9Sstevel@tonic-gate nrdc->nfs4_cookie = cookie; 24607c478bd9Sstevel@tonic-gate nrdc->buflen = count; 24617c478bd9Sstevel@tonic-gate mutex_enter(&rp->r_statelock); 24627c478bd9Sstevel@tonic-gate /* 24637c478bd9Sstevel@tonic-gate * We need to take another pass through the cache 24647c478bd9Sstevel@tonic-gate * since we dropped our lock to perform the alloc. 24657c478bd9Sstevel@tonic-gate * Another thread may have come by and inserted the 24667c478bd9Sstevel@tonic-gate * entry we are interested in. 24677c478bd9Sstevel@tonic-gate */ 24687c478bd9Sstevel@tonic-gate goto top; 24697c478bd9Sstevel@tonic-gate } 24707c478bd9Sstevel@tonic-gate 24717c478bd9Sstevel@tonic-gate /* 24727c478bd9Sstevel@tonic-gate * Check to see if we need to free our entry. This can happen if 24737c478bd9Sstevel@tonic-gate * another thread came along beat us to the insert. We can 24747c478bd9Sstevel@tonic-gate * safely call rddir4_cache_free directly because no other thread 24757c478bd9Sstevel@tonic-gate * would have a reference to this entry. 24767c478bd9Sstevel@tonic-gate */ 24777c478bd9Sstevel@tonic-gate if (nrdc != NULL) 24787c478bd9Sstevel@tonic-gate rddir4_cache_free((rddir4_cache_impl *)nrdc->data); 24797c478bd9Sstevel@tonic-gate 24807c478bd9Sstevel@tonic-gate #ifdef DEBUG 24817c478bd9Sstevel@tonic-gate nfs4_readdir_cache_hits++; 24827c478bd9Sstevel@tonic-gate #endif 24837c478bd9Sstevel@tonic-gate /* 24847c478bd9Sstevel@tonic-gate * Found something. Make sure it's ready to return. 24857c478bd9Sstevel@tonic-gate */ 24867c478bd9Sstevel@tonic-gate rdc = &rdip->rc; 24877c478bd9Sstevel@tonic-gate rddir4_cache_hold(rdc); 24887c478bd9Sstevel@tonic-gate /* 24897c478bd9Sstevel@tonic-gate * If the cache entry is in the process of being filled in, wait 24907c478bd9Sstevel@tonic-gate * until this completes. The RDDIRWAIT bit is set to indicate that 24917c478bd9Sstevel@tonic-gate * someone is waiting and when the thread currently filling the entry 24927c478bd9Sstevel@tonic-gate * is done, it should do a cv_broadcast to wakeup all of the threads 24937c478bd9Sstevel@tonic-gate * waiting for it to finish. If the thread wakes up to find that 24947c478bd9Sstevel@tonic-gate * someone new is now trying to complete the the entry, go back 24957c478bd9Sstevel@tonic-gate * to sleep. 24967c478bd9Sstevel@tonic-gate */ 24977c478bd9Sstevel@tonic-gate while (rdc->flags & RDDIR) { 24987c478bd9Sstevel@tonic-gate /* 24997c478bd9Sstevel@tonic-gate * The entry is not complete. 25007c478bd9Sstevel@tonic-gate */ 25017c478bd9Sstevel@tonic-gate nfs_rw_exit(&rp->r_rwlock); 25027c478bd9Sstevel@tonic-gate rdc->flags |= RDDIRWAIT; 25037c478bd9Sstevel@tonic-gate #ifdef DEBUG 25047c478bd9Sstevel@tonic-gate nfs4_readdir_cache_waits++; 25057c478bd9Sstevel@tonic-gate #endif 25067c478bd9Sstevel@tonic-gate while (rdc->flags & RDDIRWAIT) { 25077c478bd9Sstevel@tonic-gate if (!cv_wait_sig(&rdc->cv, &rp->r_statelock)) { 25087c478bd9Sstevel@tonic-gate /* 25097c478bd9Sstevel@tonic-gate * We got interrupted, probably the user 25107c478bd9Sstevel@tonic-gate * typed ^C or an alarm fired. We free the 25117c478bd9Sstevel@tonic-gate * new entry if we allocated one. 25127c478bd9Sstevel@tonic-gate */ 25137c478bd9Sstevel@tonic-gate rddir4_cache_rele(rp, rdc); 25147c478bd9Sstevel@tonic-gate mutex_exit(&rp->r_statelock); 25157c478bd9Sstevel@tonic-gate (void) nfs_rw_enter_sig(&rp->r_rwlock, 25167c478bd9Sstevel@tonic-gate RW_READER, FALSE); 25177c478bd9Sstevel@tonic-gate mutex_enter(&rp->r_statelock); 25187c478bd9Sstevel@tonic-gate return (NULL); 25197c478bd9Sstevel@tonic-gate } 25207c478bd9Sstevel@tonic-gate } 25217c478bd9Sstevel@tonic-gate mutex_exit(&rp->r_statelock); 25227c478bd9Sstevel@tonic-gate (void) nfs_rw_enter_sig(&rp->r_rwlock, 25237c478bd9Sstevel@tonic-gate RW_READER, FALSE); 25247c478bd9Sstevel@tonic-gate mutex_enter(&rp->r_statelock); 25257c478bd9Sstevel@tonic-gate } 25267c478bd9Sstevel@tonic-gate 25277c478bd9Sstevel@tonic-gate /* 25287c478bd9Sstevel@tonic-gate * The entry we were waiting on may have been purged from 25297c478bd9Sstevel@tonic-gate * the cache and should no longer be used, release it and 25307c478bd9Sstevel@tonic-gate * start over. 25317c478bd9Sstevel@tonic-gate */ 25327c478bd9Sstevel@tonic-gate if (!(rdc->flags & RDDIRCACHED)) { 25337c478bd9Sstevel@tonic-gate rddir4_cache_rele(rp, rdc); 25347c478bd9Sstevel@tonic-gate goto top; 25357c478bd9Sstevel@tonic-gate } 25367c478bd9Sstevel@tonic-gate 25377c478bd9Sstevel@tonic-gate /* 25387c478bd9Sstevel@tonic-gate * The entry is completed. Return it. 25397c478bd9Sstevel@tonic-gate */ 25407c478bd9Sstevel@tonic-gate return (rdc); 25417c478bd9Sstevel@tonic-gate } 25427c478bd9Sstevel@tonic-gate 25437c478bd9Sstevel@tonic-gate /* 25447c478bd9Sstevel@tonic-gate * Allocate a cache element and return it. Can return NULL if memory is 25457c478bd9Sstevel@tonic-gate * low. 25467c478bd9Sstevel@tonic-gate */ 25477c478bd9Sstevel@tonic-gate static rddir4_cache * 25487c478bd9Sstevel@tonic-gate rddir4_cache_alloc(int flags) 25497c478bd9Sstevel@tonic-gate { 25507c478bd9Sstevel@tonic-gate rddir4_cache_impl *rdip = NULL; 25517c478bd9Sstevel@tonic-gate rddir4_cache *rc = NULL; 25527c478bd9Sstevel@tonic-gate 25537c478bd9Sstevel@tonic-gate rdip = kmem_alloc(sizeof (rddir4_cache_impl), flags); 25547c478bd9Sstevel@tonic-gate 25557c478bd9Sstevel@tonic-gate if (rdip != NULL) { 25567c478bd9Sstevel@tonic-gate rc = &rdip->rc; 25577c478bd9Sstevel@tonic-gate rc->data = (void *)rdip; 25587c478bd9Sstevel@tonic-gate rc->nfs4_cookie = 0; 25597c478bd9Sstevel@tonic-gate rc->nfs4_ncookie = 0; 25607c478bd9Sstevel@tonic-gate rc->entries = NULL; 25617c478bd9Sstevel@tonic-gate rc->eof = 0; 25627c478bd9Sstevel@tonic-gate rc->entlen = 0; 25637c478bd9Sstevel@tonic-gate rc->buflen = 0; 25647c478bd9Sstevel@tonic-gate rc->actlen = 0; 25657c478bd9Sstevel@tonic-gate /* 25667c478bd9Sstevel@tonic-gate * A readdir is required so set the flag. 25677c478bd9Sstevel@tonic-gate */ 25687c478bd9Sstevel@tonic-gate rc->flags = RDDIRREQ; 25697c478bd9Sstevel@tonic-gate cv_init(&rc->cv, NULL, CV_DEFAULT, NULL); 25707c478bd9Sstevel@tonic-gate rc->error = 0; 25717c478bd9Sstevel@tonic-gate mutex_init(&rdip->lock, NULL, MUTEX_DEFAULT, NULL); 25727c478bd9Sstevel@tonic-gate rdip->count = 1; 25737c478bd9Sstevel@tonic-gate #ifdef DEBUG 25747c478bd9Sstevel@tonic-gate atomic_add_64(&clstat4_debug.dirent.value.ui64, 1); 25757c478bd9Sstevel@tonic-gate #endif 25767c478bd9Sstevel@tonic-gate } 25777c478bd9Sstevel@tonic-gate return (rc); 25787c478bd9Sstevel@tonic-gate } 25797c478bd9Sstevel@tonic-gate 25807c478bd9Sstevel@tonic-gate /* 25817c478bd9Sstevel@tonic-gate * Increment the reference count to this cache element. 25827c478bd9Sstevel@tonic-gate */ 25837c478bd9Sstevel@tonic-gate static void 25847c478bd9Sstevel@tonic-gate rddir4_cache_hold(rddir4_cache *rc) 25857c478bd9Sstevel@tonic-gate { 25867c478bd9Sstevel@tonic-gate rddir4_cache_impl *rdip = (rddir4_cache_impl *)rc->data; 25877c478bd9Sstevel@tonic-gate 25887c478bd9Sstevel@tonic-gate mutex_enter(&rdip->lock); 25897c478bd9Sstevel@tonic-gate rdip->count++; 25907c478bd9Sstevel@tonic-gate mutex_exit(&rdip->lock); 25917c478bd9Sstevel@tonic-gate } 25927c478bd9Sstevel@tonic-gate 25937c478bd9Sstevel@tonic-gate /* 25947c478bd9Sstevel@tonic-gate * Release a reference to this cache element. If the count is zero then 25957c478bd9Sstevel@tonic-gate * free the element. 25967c478bd9Sstevel@tonic-gate */ 25977c478bd9Sstevel@tonic-gate void 25987c478bd9Sstevel@tonic-gate rddir4_cache_rele(rnode4_t *rp, rddir4_cache *rdc) 25997c478bd9Sstevel@tonic-gate { 26007c478bd9Sstevel@tonic-gate rddir4_cache_impl *rdip = (rddir4_cache_impl *)rdc->data; 26017c478bd9Sstevel@tonic-gate 26027c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&rp->r_statelock)); 26037c478bd9Sstevel@tonic-gate 26047c478bd9Sstevel@tonic-gate /* 26057c478bd9Sstevel@tonic-gate * Check to see if we have any waiters. If so, we can wake them 26067c478bd9Sstevel@tonic-gate * so that they can proceed. 26077c478bd9Sstevel@tonic-gate */ 26087c478bd9Sstevel@tonic-gate if (rdc->flags & RDDIRWAIT) { 26097c478bd9Sstevel@tonic-gate rdc->flags &= ~RDDIRWAIT; 26107c478bd9Sstevel@tonic-gate cv_broadcast(&rdc->cv); 26117c478bd9Sstevel@tonic-gate } 26127c478bd9Sstevel@tonic-gate 26137c478bd9Sstevel@tonic-gate mutex_enter(&rdip->lock); 26147c478bd9Sstevel@tonic-gate ASSERT(rdip->count > 0); 26157c478bd9Sstevel@tonic-gate if (--rdip->count == 0) { 26167c478bd9Sstevel@tonic-gate mutex_exit(&rdip->lock); 26177c478bd9Sstevel@tonic-gate rddir4_cache_free(rdip); 26187c478bd9Sstevel@tonic-gate } else 26197c478bd9Sstevel@tonic-gate mutex_exit(&rdip->lock); 26207c478bd9Sstevel@tonic-gate } 26217c478bd9Sstevel@tonic-gate 26227c478bd9Sstevel@tonic-gate /* 26237c478bd9Sstevel@tonic-gate * Free a cache element. 26247c478bd9Sstevel@tonic-gate */ 26257c478bd9Sstevel@tonic-gate static void 26267c478bd9Sstevel@tonic-gate rddir4_cache_free(rddir4_cache_impl *rdip) 26277c478bd9Sstevel@tonic-gate { 26287c478bd9Sstevel@tonic-gate rddir4_cache *rc = &rdip->rc; 26297c478bd9Sstevel@tonic-gate 26307c478bd9Sstevel@tonic-gate #ifdef DEBUG 26317c478bd9Sstevel@tonic-gate atomic_add_64(&clstat4_debug.dirent.value.ui64, -1); 26327c478bd9Sstevel@tonic-gate #endif 26337c478bd9Sstevel@tonic-gate if (rc->entries != NULL) 26347c478bd9Sstevel@tonic-gate kmem_free(rc->entries, rc->buflen); 26357c478bd9Sstevel@tonic-gate cv_destroy(&rc->cv); 26367c478bd9Sstevel@tonic-gate mutex_destroy(&rdip->lock); 26377c478bd9Sstevel@tonic-gate kmem_free(rdip, sizeof (*rdip)); 26387c478bd9Sstevel@tonic-gate } 26397c478bd9Sstevel@tonic-gate 26407c478bd9Sstevel@tonic-gate /* 26417c478bd9Sstevel@tonic-gate * Snapshot callback for nfs:0:nfs4_client as registered with the kstat 26427c478bd9Sstevel@tonic-gate * framework. 26437c478bd9Sstevel@tonic-gate */ 26447c478bd9Sstevel@tonic-gate static int 26457c478bd9Sstevel@tonic-gate cl4_snapshot(kstat_t *ksp, void *buf, int rw) 26467c478bd9Sstevel@tonic-gate { 26477c478bd9Sstevel@tonic-gate ksp->ks_snaptime = gethrtime(); 26487c478bd9Sstevel@tonic-gate if (rw == KSTAT_WRITE) { 26497c478bd9Sstevel@tonic-gate bcopy(buf, ksp->ks_private, sizeof (clstat4_tmpl)); 26507c478bd9Sstevel@tonic-gate #ifdef DEBUG 26517c478bd9Sstevel@tonic-gate /* 26527c478bd9Sstevel@tonic-gate * Currently only the global zone can write to kstats, but we 26537c478bd9Sstevel@tonic-gate * add the check just for paranoia. 26547c478bd9Sstevel@tonic-gate */ 26557c478bd9Sstevel@tonic-gate if (INGLOBALZONE(curproc)) 26567c478bd9Sstevel@tonic-gate bcopy((char *)buf + sizeof (clstat4_tmpl), &clstat4_debug, 26577c478bd9Sstevel@tonic-gate sizeof (clstat4_debug)); 26587c478bd9Sstevel@tonic-gate #endif 26597c478bd9Sstevel@tonic-gate } else { 26607c478bd9Sstevel@tonic-gate bcopy(ksp->ks_private, buf, sizeof (clstat4_tmpl)); 26617c478bd9Sstevel@tonic-gate #ifdef DEBUG 26627c478bd9Sstevel@tonic-gate /* 26637c478bd9Sstevel@tonic-gate * If we're displaying the "global" debug kstat values, we 26647c478bd9Sstevel@tonic-gate * display them as-is to all zones since in fact they apply to 26657c478bd9Sstevel@tonic-gate * the system as a whole. 26667c478bd9Sstevel@tonic-gate */ 26677c478bd9Sstevel@tonic-gate bcopy(&clstat4_debug, (char *)buf + sizeof (clstat4_tmpl), 26687c478bd9Sstevel@tonic-gate sizeof (clstat4_debug)); 26697c478bd9Sstevel@tonic-gate #endif 26707c478bd9Sstevel@tonic-gate } 26717c478bd9Sstevel@tonic-gate return (0); 26727c478bd9Sstevel@tonic-gate } 26737c478bd9Sstevel@tonic-gate 26747c478bd9Sstevel@tonic-gate 26757c478bd9Sstevel@tonic-gate 26767c478bd9Sstevel@tonic-gate /* 26777c478bd9Sstevel@tonic-gate * Zone support 26787c478bd9Sstevel@tonic-gate */ 26797c478bd9Sstevel@tonic-gate static void * 26807c478bd9Sstevel@tonic-gate clinit4_zone(zoneid_t zoneid) 26817c478bd9Sstevel@tonic-gate { 26827c478bd9Sstevel@tonic-gate kstat_t *nfs4_client_kstat; 26837c478bd9Sstevel@tonic-gate struct nfs4_clnt *nfscl; 26847c478bd9Sstevel@tonic-gate uint_t ndata; 26857c478bd9Sstevel@tonic-gate 26867c478bd9Sstevel@tonic-gate nfscl = kmem_alloc(sizeof (*nfscl), KM_SLEEP); 26877c478bd9Sstevel@tonic-gate mutex_init(&nfscl->nfscl_chtable4_lock, NULL, MUTEX_DEFAULT, NULL); 26887c478bd9Sstevel@tonic-gate nfscl->nfscl_chtable4 = NULL; 26897c478bd9Sstevel@tonic-gate nfscl->nfscl_zoneid = zoneid; 26907c478bd9Sstevel@tonic-gate 26917c478bd9Sstevel@tonic-gate bcopy(&clstat4_tmpl, &nfscl->nfscl_stat, sizeof (clstat4_tmpl)); 26927c478bd9Sstevel@tonic-gate ndata = sizeof (clstat4_tmpl) / sizeof (kstat_named_t); 26937c478bd9Sstevel@tonic-gate #ifdef DEBUG 26947c478bd9Sstevel@tonic-gate ndata += sizeof (clstat4_debug) / sizeof (kstat_named_t); 26957c478bd9Sstevel@tonic-gate #endif 26967c478bd9Sstevel@tonic-gate if ((nfs4_client_kstat = kstat_create_zone("nfs", 0, "nfs4_client", 26977c478bd9Sstevel@tonic-gate "misc", KSTAT_TYPE_NAMED, ndata, 26987c478bd9Sstevel@tonic-gate KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE, zoneid)) != NULL) { 26997c478bd9Sstevel@tonic-gate nfs4_client_kstat->ks_private = &nfscl->nfscl_stat; 27007c478bd9Sstevel@tonic-gate nfs4_client_kstat->ks_snapshot = cl4_snapshot; 27017c478bd9Sstevel@tonic-gate kstat_install(nfs4_client_kstat); 27027c478bd9Sstevel@tonic-gate } 27037c478bd9Sstevel@tonic-gate mutex_enter(&nfs4_clnt_list_lock); 27047c478bd9Sstevel@tonic-gate list_insert_head(&nfs4_clnt_list, nfscl); 27057c478bd9Sstevel@tonic-gate mutex_exit(&nfs4_clnt_list_lock); 27067c478bd9Sstevel@tonic-gate return (nfscl); 27077c478bd9Sstevel@tonic-gate } 27087c478bd9Sstevel@tonic-gate 27097c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 27107c478bd9Sstevel@tonic-gate static void 27117c478bd9Sstevel@tonic-gate clfini4_zone(zoneid_t zoneid, void *arg) 27127c478bd9Sstevel@tonic-gate { 27137c478bd9Sstevel@tonic-gate struct nfs4_clnt *nfscl = arg; 27147c478bd9Sstevel@tonic-gate chhead_t *chp, *next; 27157c478bd9Sstevel@tonic-gate 27167c478bd9Sstevel@tonic-gate if (nfscl == NULL) 27177c478bd9Sstevel@tonic-gate return; 27187c478bd9Sstevel@tonic-gate mutex_enter(&nfs4_clnt_list_lock); 27197c478bd9Sstevel@tonic-gate list_remove(&nfs4_clnt_list, nfscl); 27207c478bd9Sstevel@tonic-gate mutex_exit(&nfs4_clnt_list_lock); 27217c478bd9Sstevel@tonic-gate clreclaim4_zone(nfscl, 0); 27227c478bd9Sstevel@tonic-gate for (chp = nfscl->nfscl_chtable4; chp != NULL; chp = next) { 27237c478bd9Sstevel@tonic-gate ASSERT(chp->ch_list == NULL); 27247c478bd9Sstevel@tonic-gate kmem_free(chp->ch_protofmly, strlen(chp->ch_protofmly) + 1); 27257c478bd9Sstevel@tonic-gate next = chp->ch_next; 27267c478bd9Sstevel@tonic-gate kmem_free(chp, sizeof (*chp)); 27277c478bd9Sstevel@tonic-gate } 27287c478bd9Sstevel@tonic-gate kstat_delete_byname_zone("nfs", 0, "nfs4_client", zoneid); 27297c478bd9Sstevel@tonic-gate mutex_destroy(&nfscl->nfscl_chtable4_lock); 27307c478bd9Sstevel@tonic-gate kmem_free(nfscl, sizeof (*nfscl)); 27317c478bd9Sstevel@tonic-gate } 27327c478bd9Sstevel@tonic-gate 27337c478bd9Sstevel@tonic-gate /* 27347c478bd9Sstevel@tonic-gate * Called by endpnt_destructor to make sure the client handles are 27357c478bd9Sstevel@tonic-gate * cleaned up before the RPC endpoints. This becomes a no-op if 27367c478bd9Sstevel@tonic-gate * clfini_zone (above) is called first. This function is needed 27377c478bd9Sstevel@tonic-gate * (rather than relying on clfini_zone to clean up) because the ZSD 27387c478bd9Sstevel@tonic-gate * callbacks have no ordering mechanism, so we have no way to ensure 27397c478bd9Sstevel@tonic-gate * that clfini_zone is called before endpnt_destructor. 27407c478bd9Sstevel@tonic-gate */ 27417c478bd9Sstevel@tonic-gate void 27427c478bd9Sstevel@tonic-gate clcleanup4_zone(zoneid_t zoneid) 27437c478bd9Sstevel@tonic-gate { 27447c478bd9Sstevel@tonic-gate struct nfs4_clnt *nfscl; 27457c478bd9Sstevel@tonic-gate 27467c478bd9Sstevel@tonic-gate mutex_enter(&nfs4_clnt_list_lock); 27477c478bd9Sstevel@tonic-gate nfscl = list_head(&nfs4_clnt_list); 27487c478bd9Sstevel@tonic-gate for (; nfscl != NULL; nfscl = list_next(&nfs4_clnt_list, nfscl)) { 27497c478bd9Sstevel@tonic-gate if (nfscl->nfscl_zoneid == zoneid) { 27507c478bd9Sstevel@tonic-gate clreclaim4_zone(nfscl, 0); 27517c478bd9Sstevel@tonic-gate break; 27527c478bd9Sstevel@tonic-gate } 27537c478bd9Sstevel@tonic-gate } 27547c478bd9Sstevel@tonic-gate mutex_exit(&nfs4_clnt_list_lock); 27557c478bd9Sstevel@tonic-gate } 27567c478bd9Sstevel@tonic-gate 27577c478bd9Sstevel@tonic-gate int 27587c478bd9Sstevel@tonic-gate nfs4_subr_init(void) 27597c478bd9Sstevel@tonic-gate { 27607c478bd9Sstevel@tonic-gate /* 27617c478bd9Sstevel@tonic-gate * Allocate and initialize the client handle cache 27627c478bd9Sstevel@tonic-gate */ 27637c478bd9Sstevel@tonic-gate chtab4_cache = kmem_cache_create("client_handle4_cache", 27647c478bd9Sstevel@tonic-gate sizeof (struct chtab), 0, NULL, NULL, clreclaim4, NULL, 27657c478bd9Sstevel@tonic-gate NULL, 0); 27667c478bd9Sstevel@tonic-gate 27677c478bd9Sstevel@tonic-gate /* 27687c478bd9Sstevel@tonic-gate * Initialize the list of per-zone client handles (and associated data). 27697c478bd9Sstevel@tonic-gate * This needs to be done before we call zone_key_create(). 27707c478bd9Sstevel@tonic-gate */ 27717c478bd9Sstevel@tonic-gate list_create(&nfs4_clnt_list, sizeof (struct nfs4_clnt), 27727c478bd9Sstevel@tonic-gate offsetof(struct nfs4_clnt, nfscl_node)); 27737c478bd9Sstevel@tonic-gate 27747c478bd9Sstevel@tonic-gate /* 27757c478bd9Sstevel@tonic-gate * Initialize the zone_key for per-zone client handle lists. 27767c478bd9Sstevel@tonic-gate */ 27777c478bd9Sstevel@tonic-gate zone_key_create(&nfs4clnt_zone_key, clinit4_zone, NULL, clfini4_zone); 27787c478bd9Sstevel@tonic-gate 27797c478bd9Sstevel@tonic-gate if (nfs4err_delay_time == 0) 27807c478bd9Sstevel@tonic-gate nfs4err_delay_time = NFS4ERR_DELAY_TIME; 27817c478bd9Sstevel@tonic-gate 27827c478bd9Sstevel@tonic-gate return (0); 27837c478bd9Sstevel@tonic-gate } 27847c478bd9Sstevel@tonic-gate 27857c478bd9Sstevel@tonic-gate int 27867c478bd9Sstevel@tonic-gate nfs4_subr_fini(void) 27877c478bd9Sstevel@tonic-gate { 27887c478bd9Sstevel@tonic-gate /* 27897c478bd9Sstevel@tonic-gate * Deallocate the client handle cache 27907c478bd9Sstevel@tonic-gate */ 27917c478bd9Sstevel@tonic-gate kmem_cache_destroy(chtab4_cache); 27927c478bd9Sstevel@tonic-gate 27937c478bd9Sstevel@tonic-gate /* 27947c478bd9Sstevel@tonic-gate * Destroy the zone_key 27957c478bd9Sstevel@tonic-gate */ 27967c478bd9Sstevel@tonic-gate (void) zone_key_delete(nfs4clnt_zone_key); 27977c478bd9Sstevel@tonic-gate 27987c478bd9Sstevel@tonic-gate return (0); 27997c478bd9Sstevel@tonic-gate } 28007c478bd9Sstevel@tonic-gate /* 28017c478bd9Sstevel@tonic-gate * Set or Clear direct I/O flag 28027c478bd9Sstevel@tonic-gate * VOP_RWLOCK() is held for write access to prevent a race condition 28037c478bd9Sstevel@tonic-gate * which would occur if a process is in the middle of a write when 28047c478bd9Sstevel@tonic-gate * directio flag gets set. It is possible that all pages may not get flushed. 28057c478bd9Sstevel@tonic-gate * 28067c478bd9Sstevel@tonic-gate * This is a copy of nfs_directio, changes here may need to be made 28077c478bd9Sstevel@tonic-gate * there and vice versa. 28087c478bd9Sstevel@tonic-gate */ 28097c478bd9Sstevel@tonic-gate 28107c478bd9Sstevel@tonic-gate int 28117c478bd9Sstevel@tonic-gate nfs4_directio(vnode_t *vp, int cmd, cred_t *cr) 28127c478bd9Sstevel@tonic-gate { 28137c478bd9Sstevel@tonic-gate int error = 0; 28147c478bd9Sstevel@tonic-gate rnode4_t *rp; 28157c478bd9Sstevel@tonic-gate 28167c478bd9Sstevel@tonic-gate rp = VTOR4(vp); 28177c478bd9Sstevel@tonic-gate 28187c478bd9Sstevel@tonic-gate if (cmd == DIRECTIO_ON) { 28197c478bd9Sstevel@tonic-gate 28207c478bd9Sstevel@tonic-gate if (rp->r_flags & R4DIRECTIO) 28217c478bd9Sstevel@tonic-gate return (0); 28227c478bd9Sstevel@tonic-gate 28237c478bd9Sstevel@tonic-gate /* 28247c478bd9Sstevel@tonic-gate * Flush the page cache. 28257c478bd9Sstevel@tonic-gate */ 28267c478bd9Sstevel@tonic-gate 28277c478bd9Sstevel@tonic-gate (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); 28287c478bd9Sstevel@tonic-gate 28297c478bd9Sstevel@tonic-gate if (rp->r_flags & R4DIRECTIO) { 28307c478bd9Sstevel@tonic-gate VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 28317c478bd9Sstevel@tonic-gate return (0); 28327c478bd9Sstevel@tonic-gate } 28337c478bd9Sstevel@tonic-gate 28347c478bd9Sstevel@tonic-gate if (nfs4_has_pages(vp) && 28357c478bd9Sstevel@tonic-gate ((rp->r_flags & R4DIRTY) || rp->r_awcount > 0)) { 28367c478bd9Sstevel@tonic-gate error = VOP_PUTPAGE(vp, (offset_t)0, (uint_t)0, 28377c478bd9Sstevel@tonic-gate B_INVAL, cr); 28387c478bd9Sstevel@tonic-gate if (error) { 28397c478bd9Sstevel@tonic-gate if (error == ENOSPC || error == EDQUOT) { 28407c478bd9Sstevel@tonic-gate mutex_enter(&rp->r_statelock); 28417c478bd9Sstevel@tonic-gate if (!rp->r_error) 28427c478bd9Sstevel@tonic-gate rp->r_error = error; 28437c478bd9Sstevel@tonic-gate mutex_exit(&rp->r_statelock); 28447c478bd9Sstevel@tonic-gate } 28457c478bd9Sstevel@tonic-gate VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 28467c478bd9Sstevel@tonic-gate return (error); 28477c478bd9Sstevel@tonic-gate } 28487c478bd9Sstevel@tonic-gate } 28497c478bd9Sstevel@tonic-gate 28507c478bd9Sstevel@tonic-gate mutex_enter(&rp->r_statelock); 28517c478bd9Sstevel@tonic-gate rp->r_flags |= R4DIRECTIO; 28527c478bd9Sstevel@tonic-gate mutex_exit(&rp->r_statelock); 28537c478bd9Sstevel@tonic-gate VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 28547c478bd9Sstevel@tonic-gate return (0); 28557c478bd9Sstevel@tonic-gate } 28567c478bd9Sstevel@tonic-gate 28577c478bd9Sstevel@tonic-gate if (cmd == DIRECTIO_OFF) { 28587c478bd9Sstevel@tonic-gate mutex_enter(&rp->r_statelock); 28597c478bd9Sstevel@tonic-gate rp->r_flags &= ~R4DIRECTIO; /* disable direct mode */ 28607c478bd9Sstevel@tonic-gate mutex_exit(&rp->r_statelock); 28617c478bd9Sstevel@tonic-gate return (0); 28627c478bd9Sstevel@tonic-gate } 28637c478bd9Sstevel@tonic-gate 28647c478bd9Sstevel@tonic-gate return (EINVAL); 28657c478bd9Sstevel@tonic-gate } 28667c478bd9Sstevel@tonic-gate 28677c478bd9Sstevel@tonic-gate /* 28687c478bd9Sstevel@tonic-gate * Return TRUE if the file has any pages. Always go back to 28697c478bd9Sstevel@tonic-gate * the master vnode to check v_pages since none of the shadows 28707c478bd9Sstevel@tonic-gate * can have pages. 28717c478bd9Sstevel@tonic-gate */ 28727c478bd9Sstevel@tonic-gate 28737c478bd9Sstevel@tonic-gate bool_t 28747c478bd9Sstevel@tonic-gate nfs4_has_pages(vnode_t *vp) 28757c478bd9Sstevel@tonic-gate { 28767c478bd9Sstevel@tonic-gate rnode4_t *rp; 28777c478bd9Sstevel@tonic-gate 28787c478bd9Sstevel@tonic-gate rp = VTOR4(vp); 28797c478bd9Sstevel@tonic-gate if (IS_SHADOW(vp, rp)) 28807c478bd9Sstevel@tonic-gate vp = RTOV4(rp); /* RTOV4 always gives the master */ 28817c478bd9Sstevel@tonic-gate 28827c478bd9Sstevel@tonic-gate return (vn_has_cached_data(vp)); 28837c478bd9Sstevel@tonic-gate } 28847c478bd9Sstevel@tonic-gate 28857c478bd9Sstevel@tonic-gate /* 28867c478bd9Sstevel@tonic-gate * This table is used to determine whether the client should attempt 28877c478bd9Sstevel@tonic-gate * failover based on the clnt_stat value returned by CLNT_CALL. The 28887c478bd9Sstevel@tonic-gate * clnt_stat is used as an index into the table. If 28897c478bd9Sstevel@tonic-gate * the error value that corresponds to the clnt_stat value in the 28907c478bd9Sstevel@tonic-gate * table is non-zero, then that is the error to be returned AND 28917c478bd9Sstevel@tonic-gate * that signals that failover should be attempted. 28927c478bd9Sstevel@tonic-gate * 28937c478bd9Sstevel@tonic-gate * Special note: If the RPC_ values change, then direct indexing of the 28947c478bd9Sstevel@tonic-gate * table is no longer valid, but having the RPC_ values in the table 28957c478bd9Sstevel@tonic-gate * allow the functions to detect the change and issue a warning. 28967c478bd9Sstevel@tonic-gate * In this case, the code will always attempt failover as a defensive 28977c478bd9Sstevel@tonic-gate * measure. 28987c478bd9Sstevel@tonic-gate */ 28997c478bd9Sstevel@tonic-gate 29007c478bd9Sstevel@tonic-gate static struct try_failover_tab { 29017c478bd9Sstevel@tonic-gate enum clnt_stat cstat; 29027c478bd9Sstevel@tonic-gate int error; 29037c478bd9Sstevel@tonic-gate } try_failover_table [] = { 29047c478bd9Sstevel@tonic-gate 29057c478bd9Sstevel@tonic-gate RPC_SUCCESS, 0, 29067c478bd9Sstevel@tonic-gate RPC_CANTENCODEARGS, 0, 29077c478bd9Sstevel@tonic-gate RPC_CANTDECODERES, 0, 29087c478bd9Sstevel@tonic-gate RPC_CANTSEND, ECOMM, 29097c478bd9Sstevel@tonic-gate RPC_CANTRECV, ECOMM, 29107c478bd9Sstevel@tonic-gate RPC_TIMEDOUT, ETIMEDOUT, 29117c478bd9Sstevel@tonic-gate RPC_VERSMISMATCH, 0, 29127c478bd9Sstevel@tonic-gate RPC_AUTHERROR, 0, 29137c478bd9Sstevel@tonic-gate RPC_PROGUNAVAIL, 0, 29147c478bd9Sstevel@tonic-gate RPC_PROGVERSMISMATCH, 0, 29157c478bd9Sstevel@tonic-gate RPC_PROCUNAVAIL, 0, 29167c478bd9Sstevel@tonic-gate RPC_CANTDECODEARGS, 0, 29177c478bd9Sstevel@tonic-gate RPC_SYSTEMERROR, ENOSR, 29187c478bd9Sstevel@tonic-gate RPC_UNKNOWNHOST, EHOSTUNREACH, 29197c478bd9Sstevel@tonic-gate RPC_RPCBFAILURE, ENETUNREACH, 29207c478bd9Sstevel@tonic-gate RPC_PROGNOTREGISTERED, ECONNREFUSED, 29217c478bd9Sstevel@tonic-gate RPC_FAILED, ETIMEDOUT, 29227c478bd9Sstevel@tonic-gate RPC_UNKNOWNPROTO, EHOSTUNREACH, 29237c478bd9Sstevel@tonic-gate RPC_INTR, 0, 29247c478bd9Sstevel@tonic-gate RPC_UNKNOWNADDR, EHOSTUNREACH, 29257c478bd9Sstevel@tonic-gate RPC_TLIERROR, 0, 29267c478bd9Sstevel@tonic-gate RPC_NOBROADCAST, EHOSTUNREACH, 29277c478bd9Sstevel@tonic-gate RPC_N2AXLATEFAILURE, ECONNREFUSED, 29287c478bd9Sstevel@tonic-gate RPC_UDERROR, 0, 29297c478bd9Sstevel@tonic-gate RPC_INPROGRESS, 0, 29307c478bd9Sstevel@tonic-gate RPC_STALERACHANDLE, EINVAL, 29317c478bd9Sstevel@tonic-gate RPC_CANTCONNECT, ECONNREFUSED, 29327c478bd9Sstevel@tonic-gate RPC_XPRTFAILED, ECONNABORTED, 29337c478bd9Sstevel@tonic-gate RPC_CANTCREATESTREAM, ECONNREFUSED, 29347c478bd9Sstevel@tonic-gate RPC_CANTSTORE, ENOBUFS 29357c478bd9Sstevel@tonic-gate }; 29367c478bd9Sstevel@tonic-gate 29377c478bd9Sstevel@tonic-gate /* 29387c478bd9Sstevel@tonic-gate * nfs4_try_failover - determine whether the client should 29397c478bd9Sstevel@tonic-gate * attempt failover based on the values stored in the nfs4_error_t. 29407c478bd9Sstevel@tonic-gate */ 29417c478bd9Sstevel@tonic-gate int 29427c478bd9Sstevel@tonic-gate nfs4_try_failover(nfs4_error_t *ep) 29437c478bd9Sstevel@tonic-gate { 29447c478bd9Sstevel@tonic-gate if (ep->error == ETIMEDOUT || ep->stat == NFS4ERR_RESOURCE) 29457c478bd9Sstevel@tonic-gate return (TRUE); 29467c478bd9Sstevel@tonic-gate 29477c478bd9Sstevel@tonic-gate if (ep->error && ep->rpc_status != RPC_SUCCESS) 29487c478bd9Sstevel@tonic-gate return (try_failover(ep->rpc_status) != 0 ? TRUE : FALSE); 29497c478bd9Sstevel@tonic-gate 29507c478bd9Sstevel@tonic-gate return (FALSE); 29517c478bd9Sstevel@tonic-gate } 29527c478bd9Sstevel@tonic-gate 29537c478bd9Sstevel@tonic-gate /* 29547c478bd9Sstevel@tonic-gate * try_failover - internal version of nfs4_try_failover, called 29557c478bd9Sstevel@tonic-gate * only by rfscall and aclcall. Determine if failover is warranted 29567c478bd9Sstevel@tonic-gate * based on the clnt_stat and return the error number if it is. 29577c478bd9Sstevel@tonic-gate */ 29587c478bd9Sstevel@tonic-gate static int 29597c478bd9Sstevel@tonic-gate try_failover(enum clnt_stat rpc_status) 29607c478bd9Sstevel@tonic-gate { 29617c478bd9Sstevel@tonic-gate int err = 0; 29627c478bd9Sstevel@tonic-gate 29637c478bd9Sstevel@tonic-gate if (rpc_status == RPC_SUCCESS) 29647c478bd9Sstevel@tonic-gate return (0); 29657c478bd9Sstevel@tonic-gate 29667c478bd9Sstevel@tonic-gate #ifdef DEBUG 29677c478bd9Sstevel@tonic-gate if (rpc_status != 0 && nfs4_try_failover_any) { 29687c478bd9Sstevel@tonic-gate err = ETIMEDOUT; 29697c478bd9Sstevel@tonic-gate goto done; 29707c478bd9Sstevel@tonic-gate } 29717c478bd9Sstevel@tonic-gate #endif 29727c478bd9Sstevel@tonic-gate /* 29737c478bd9Sstevel@tonic-gate * The rpc status is used as an index into the table. 29747c478bd9Sstevel@tonic-gate * If the rpc status is outside of the range of the 29757c478bd9Sstevel@tonic-gate * table or if the rpc error numbers have been changed 29767c478bd9Sstevel@tonic-gate * since the table was constructed, then print a warning 29777c478bd9Sstevel@tonic-gate * (DEBUG only) and try failover anyway. Otherwise, just 29787c478bd9Sstevel@tonic-gate * grab the resulting error number out of the table. 29797c478bd9Sstevel@tonic-gate */ 29807c478bd9Sstevel@tonic-gate if (rpc_status < RPC_SUCCESS || rpc_status >= 29817c478bd9Sstevel@tonic-gate sizeof (try_failover_table)/sizeof (try_failover_table[0]) || 29827c478bd9Sstevel@tonic-gate try_failover_table[rpc_status].cstat != rpc_status) { 29837c478bd9Sstevel@tonic-gate 29847c478bd9Sstevel@tonic-gate err = ETIMEDOUT; 29857c478bd9Sstevel@tonic-gate #ifdef DEBUG 29867c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "try_failover: unexpected rpc error %d", 29877c478bd9Sstevel@tonic-gate rpc_status); 29887c478bd9Sstevel@tonic-gate #endif 29897c478bd9Sstevel@tonic-gate } else 29907c478bd9Sstevel@tonic-gate err = try_failover_table[rpc_status].error; 29917c478bd9Sstevel@tonic-gate 29927c478bd9Sstevel@tonic-gate done: 29937c478bd9Sstevel@tonic-gate if (rpc_status) 29947c478bd9Sstevel@tonic-gate NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE, 29957c478bd9Sstevel@tonic-gate "nfs4_try_failover: %strying failover on error %d", 29967c478bd9Sstevel@tonic-gate err ? "" : "NOT ", rpc_status)); 29977c478bd9Sstevel@tonic-gate 29987c478bd9Sstevel@tonic-gate return (err); 29997c478bd9Sstevel@tonic-gate } 30007c478bd9Sstevel@tonic-gate 30017c478bd9Sstevel@tonic-gate void 30027c478bd9Sstevel@tonic-gate nfs4_error_zinit(nfs4_error_t *ep) 30037c478bd9Sstevel@tonic-gate { 30047c478bd9Sstevel@tonic-gate ep->error = 0; 30057c478bd9Sstevel@tonic-gate ep->stat = NFS4_OK; 30067c478bd9Sstevel@tonic-gate ep->rpc_status = RPC_SUCCESS; 30077c478bd9Sstevel@tonic-gate } 30087c478bd9Sstevel@tonic-gate 30097c478bd9Sstevel@tonic-gate void 30107c478bd9Sstevel@tonic-gate nfs4_error_init(nfs4_error_t *ep, int error) 30117c478bd9Sstevel@tonic-gate { 30127c478bd9Sstevel@tonic-gate ep->error = error; 30137c478bd9Sstevel@tonic-gate ep->stat = NFS4_OK; 30147c478bd9Sstevel@tonic-gate ep->rpc_status = RPC_SUCCESS; 30157c478bd9Sstevel@tonic-gate } 30167c478bd9Sstevel@tonic-gate 30177c478bd9Sstevel@tonic-gate 30187c478bd9Sstevel@tonic-gate #ifdef DEBUG 30197c478bd9Sstevel@tonic-gate 30207c478bd9Sstevel@tonic-gate /* 30217c478bd9Sstevel@tonic-gate * Return a 16-bit hash for filehandle, stateid, clientid, owner. 30227c478bd9Sstevel@tonic-gate * use the same algorithm as for NFS v3. 30237c478bd9Sstevel@tonic-gate * 30247c478bd9Sstevel@tonic-gate */ 30257c478bd9Sstevel@tonic-gate int 30267c478bd9Sstevel@tonic-gate hash16(void *p, int len) 30277c478bd9Sstevel@tonic-gate { 30287c478bd9Sstevel@tonic-gate int i, rem; 30297c478bd9Sstevel@tonic-gate uint_t *wp; 30307c478bd9Sstevel@tonic-gate uint_t key = 0; 30317c478bd9Sstevel@tonic-gate 30327c478bd9Sstevel@tonic-gate /* protect against non word aligned */ 30337c478bd9Sstevel@tonic-gate if ((rem = len & 3) != 0) 30347c478bd9Sstevel@tonic-gate len &= ~3; 30357c478bd9Sstevel@tonic-gate 30367c478bd9Sstevel@tonic-gate for (i = 0, wp = (uint_t *)p; i < len; i += 4, wp++) { 30377c478bd9Sstevel@tonic-gate key ^= (*wp >> 16) ^ *wp; 30387c478bd9Sstevel@tonic-gate } 30397c478bd9Sstevel@tonic-gate 30407c478bd9Sstevel@tonic-gate /* hash left-over bytes */ 30417c478bd9Sstevel@tonic-gate for (i = 0; i < rem; i++) 30427c478bd9Sstevel@tonic-gate key ^= *((uchar_t *)p + i); 30437c478bd9Sstevel@tonic-gate 30447c478bd9Sstevel@tonic-gate return (key & 0xffff); 30457c478bd9Sstevel@tonic-gate } 30467c478bd9Sstevel@tonic-gate 30477c478bd9Sstevel@tonic-gate /* 30487c478bd9Sstevel@tonic-gate * rnode4info - return filehandle and path information for an rnode. 30497c478bd9Sstevel@tonic-gate * XXX MT issues: uses a single static buffer, no locking of path. 30507c478bd9Sstevel@tonic-gate */ 30517c478bd9Sstevel@tonic-gate char * 30527c478bd9Sstevel@tonic-gate rnode4info(rnode4_t *rp) 30537c478bd9Sstevel@tonic-gate { 30547c478bd9Sstevel@tonic-gate static char buf[80]; 30557c478bd9Sstevel@tonic-gate nfs4_fhandle_t fhandle; 30567c478bd9Sstevel@tonic-gate char *path; 30577c478bd9Sstevel@tonic-gate char *type; 30587c478bd9Sstevel@tonic-gate 30597c478bd9Sstevel@tonic-gate if (rp == NULL) 30607c478bd9Sstevel@tonic-gate return ("null"); 30617c478bd9Sstevel@tonic-gate if (rp->r_flags & R4ISXATTR) 30627c478bd9Sstevel@tonic-gate type = "attr"; 30637c478bd9Sstevel@tonic-gate else if (RTOV4(rp)->v_flag & V_XATTRDIR) 30647c478bd9Sstevel@tonic-gate type = "attrdir"; 30657c478bd9Sstevel@tonic-gate else if (RTOV4(rp)->v_flag & VROOT) 30667c478bd9Sstevel@tonic-gate type = "root"; 30677c478bd9Sstevel@tonic-gate else if (RTOV4(rp)->v_type == VDIR) 30687c478bd9Sstevel@tonic-gate type = "dir"; 30697c478bd9Sstevel@tonic-gate else if (RTOV4(rp)->v_type == VREG) 30707c478bd9Sstevel@tonic-gate type = "file"; 30717c478bd9Sstevel@tonic-gate else 30727c478bd9Sstevel@tonic-gate type = "other"; 30737c478bd9Sstevel@tonic-gate sfh4_copyval(rp->r_fh, &fhandle); 30747c478bd9Sstevel@tonic-gate path = fn_path(rp->r_svnode.sv_name); 30757c478bd9Sstevel@tonic-gate (void) snprintf(buf, 80, "$%p[%s], type=%s, flags=%04X, FH=%04X\n", 30767c478bd9Sstevel@tonic-gate (void *)rp, path, type, rp->r_flags, 30777c478bd9Sstevel@tonic-gate hash16((void *)&fhandle.fh_buf, fhandle.fh_len)); 30787c478bd9Sstevel@tonic-gate kmem_free(path, strlen(path)+1); 30797c478bd9Sstevel@tonic-gate return (buf); 30807c478bd9Sstevel@tonic-gate } 30817c478bd9Sstevel@tonic-gate #endif 3082