35 #include <sys/kmem.h>
  36 #include <sys/pathname.h>
  37 #include <sys/utsname.h>
  38 #include <sys/debug.h>
  39 #include <sys/door.h>
  40 #include <sys/sdt.h>
  41 #include <sys/thread.h>
  42 #include <sys/avl.h>
  43 
  44 #include <rpc/types.h>
  45 #include <rpc/auth.h>
  46 #include <rpc/clnt.h>
  47 
  48 #include <nfs/nfs.h>
  49 #include <nfs/export.h>
  50 #include <nfs/nfs_clnt.h>
  51 #include <nfs/auth.h>
  52 
  53 static struct kmem_cache *exi_cache_handle;
  54 static void exi_cache_reclaim(void *);
  55 static void exi_cache_trim(struct exportinfo *exi);
  56 static void *nfsauth_zone_init(zoneid_t);
  57 static void nfsauth_zone_shutdown(zoneid_t zoneid, void *data);
  58 static void nfsauth_zone_fini(zoneid_t, void *);
  59 
  60 extern pri_t minclsyspri;
  61 
  62 /* NFS auth cache statistics */
  63 volatile uint_t nfsauth_cache_hit;
  64 volatile uint_t nfsauth_cache_miss;
  65 volatile uint_t nfsauth_cache_refresh;
  66 volatile uint_t nfsauth_cache_reclaim;
  67 volatile uint_t exi_cache_auth_reclaim_failed;
  68 volatile uint_t exi_cache_clnt_reclaim_failed;
  69 
  70 /*
  71  * The lifetime of an auth cache entry:
  72  * ------------------------------------
  73  *
  74  * An auth cache entry is created with both the auth_time
  75  * and auth_freshness times set to the current time.
  76  *
  77  * Upon every client access which results in a hit, the
  78  * auth_time will be updated.
 
 
 159         /*
 160          * A list_t would be overkill.  These are auth_cache entries which are
 161          * no longer linked to an exi.  It should be the case that all of their
 162          * states are NFS_AUTH_INVALID, i.e., the only way to be put on this
 163          * list is iff their state indicated that they had been placed on the
 164          * refreshq_queue.
 165          *
 166          * Note that while there is no link from the exi or back to the exi,
 167          * the exi can not go away until these entries are harvested.
 168          */
 169         struct auth_cache               *refreshq_dead_entries;
 170         nfsauth_refreshq_thread_state_t refreshq_thread_state;
 171 
 172 } nfsauth_globals_t;
 173 
 174 static void nfsauth_free_node(struct auth_cache *);
 175 static void nfsauth_refresh_thread(nfsauth_globals_t *);
 176 
 177 static int nfsauth_cache_compar(const void *, const void *);
 178 
 179 static zone_key_t       nfsauth_zone_key;
 180 
 181 void
 182 mountd_args(uint_t did)
 183 {
 184         nfsauth_globals_t *nag;
 185 
 186         nag = zone_getspecific(nfsauth_zone_key, curzone);
 187         mutex_enter(&nag->mountd_lock);
 188         if (nag->mountd_dh != NULL)
 189                 door_ki_rele(nag->mountd_dh);
 190         nag->mountd_dh = door_ki_lookup(did);
 191         mutex_exit(&nag->mountd_lock);
 192 }
 193 
 194 void
 195 nfsauth_init(void)
 196 {
 197         zone_key_create(&nfsauth_zone_key, nfsauth_zone_init,
 198             nfsauth_zone_shutdown, nfsauth_zone_fini);
 199 
 200         exi_cache_handle = kmem_cache_create("exi_cache_handle",
 201             sizeof (struct auth_cache), 0, NULL, NULL,
 202             exi_cache_reclaim, NULL, NULL, 0);
 203 }
 204 
 205 void
 206 nfsauth_fini(void)
 207 {
 208         kmem_cache_destroy(exi_cache_handle);
 209 }
 210 
 211 /*ARGSUSED*/
 212 static void *
 213 nfsauth_zone_init(zoneid_t zoneid)
 214 {
 215         nfsauth_globals_t *nag;
 216 
 217         nag = kmem_zalloc(sizeof (*nag), KM_SLEEP);
 218 
 219         /*
 220          * mountd can be restarted by smf(5).  We need to make sure
 221          * the updated door handle will safely make it to mountd_dh.
 222          */
 223         mutex_init(&nag->mountd_lock, NULL, MUTEX_DEFAULT, NULL);
 224         mutex_init(&nag->refreshq_lock, NULL, MUTEX_DEFAULT, NULL);
 225         list_create(&nag->refreshq_queue, sizeof (refreshq_exi_node_t),
 226             offsetof(refreshq_exi_node_t, ren_node));
 227         cv_init(&nag->refreshq_cv, NULL, CV_DEFAULT, NULL);
 228         nag->refreshq_thread_state = REFRESHQ_THREAD_NEED_CREATE;
 229 
 230         return (nag);
 231 }
 232 
 233 /*ARGSUSED*/
 234 static void
 235 nfsauth_zone_shutdown(zoneid_t zoneid, void *data)
 236 {
 237         refreshq_exi_node_t     *ren;
 238         nfsauth_globals_t       *nag = data;
 239 
 240         /* Prevent the nfsauth_refresh_thread from getting new work */
 241         mutex_enter(&nag->refreshq_lock);
 242         if (nag->refreshq_thread_state == REFRESHQ_THREAD_RUNNING) {
 243                 nag->refreshq_thread_state = REFRESHQ_THREAD_FINI_REQ;
 244                 cv_broadcast(&nag->refreshq_cv);
 245 
 246                 /* Wait for nfsauth_refresh_thread() to exit */
 247                 while (nag->refreshq_thread_state != REFRESHQ_THREAD_HALTED)
 248                         cv_wait(&nag->refreshq_cv, &nag->refreshq_lock);
 249         }
 250         mutex_exit(&nag->refreshq_lock);
 251 
 252         /*
 253          * Walk the exi_list and in turn, walk the auth_lists and free all
 254          * lists.  In addition, free INVALID auth_cache entries.
 255          */
 256         while ((ren = list_remove_head(&nag->refreshq_queue))) {
 257                 refreshq_auth_node_t *ran;
 258 
 259                 while ((ran = list_remove_head(&ren->ren_authlist)) != NULL) {
 260                         struct auth_cache *p = ran->ran_auth;
 261                         if (p->auth_state == NFS_AUTH_INVALID)
 262                                 nfsauth_free_node(p);
 263                         strfree(ran->ran_netid);
 264                         kmem_free(ran, sizeof (*ran));
 265                 }
 266 
 267                 list_destroy(&ren->ren_authlist);
 268                 exi_rele(ren->ren_exi);
 269                 kmem_free(ren, sizeof (*ren));
 270         }
 271 }
 272 
 273 /*ARGSUSED*/
 274 static void
 275 nfsauth_zone_fini(zoneid_t zoneid, void *data)
 276 {
 277         nfsauth_globals_t *nag = data;
 278 
 279         list_destroy(&nag->refreshq_queue);
 280         cv_destroy(&nag->refreshq_cv);
 281         mutex_destroy(&nag->refreshq_lock);
 282         mutex_destroy(&nag->mountd_lock);
 283         /* Extra cleanup. */
 284         if (nag->mountd_dh != NULL)
 285                 door_ki_rele(nag->mountd_dh);
 286         kmem_free(nag, sizeof (*nag));
 287 }
 288 
 289 /*
 290  * Convert the address in a netbuf to
 291  * a hash index for the auth_cache table.
 292  */
 293 static int
 294 hash(struct netbuf *a)
 295 {
 296         int i, h = 0;
 297 
 298         for (i = 0; i < a->len; i++)
 
 861         struct netbuf           *taddrmask;
 862         struct netbuf           addr;   /* temporary copy of client's address */
 863         const struct netbuf     *claddr;
 864         avl_tree_t              *tree;
 865         struct auth_cache       ac;     /* used as a template for avl_find() */
 866         struct auth_cache_clnt  *c;
 867         struct auth_cache_clnt  acc;    /* used as a template for avl_find() */
 868         struct auth_cache       *p = NULL;
 869         int                     access;
 870 
 871         uid_t                   tmpuid;
 872         gid_t                   tmpgid;
 873         uint_t                  tmpngids;
 874         gid_t                   *tmpgids;
 875 
 876         avl_index_t             where;  /* used for avl_find()/avl_insert() */
 877 
 878         ASSERT(cr != NULL);
 879 
 880         ASSERT3P(curzone, ==, exi->exi_zone);
 881         nag = zone_getspecific(nfsauth_zone_key, curzone);
 882 
 883         /*
 884          * Now check whether this client already
 885          * has an entry for this flavor in the cache
 886          * for this export.
 887          * Get the caller's address, mask off the
 888          * parts of the address that do not identify
 889          * the host (port number, etc), and then hash
 890          * it to find the chain of cache entries.
 891          */
 892 
 893         claddr = svc_getrpccaller(req->rq_xprt);
 894         addr = *claddr;
 895         addr.buf = kmem_alloc(addr.maxlen, KM_SLEEP);
 896         bcopy(claddr->buf, addr.buf, claddr->len);
 897 
 898         SVC_GETADDRMASK(req->rq_xprt, SVC_TATTR_ADDRMASK, (void **)&taddrmask);
 899         ASSERT(taddrmask != NULL);
 900         addrmask(&addr, taddrmask);
 901 
 
 
1438 nfsauth_cache_free(struct exportinfo *exi)
1439 {
1440         int i;
1441 
1442         /*
1443          * The only way we got here was with an exi_rele, which means that no
1444          * auth cache entry is being refreshed.
1445          */
1446 
1447         for (i = 0; i < AUTH_TABLESIZE; i++) {
1448                 avl_tree_t *tree = exi->exi_cache[i];
1449                 void *cookie = NULL;
1450                 struct auth_cache_clnt *node;
1451 
1452                 while ((node = avl_destroy_nodes(tree, &cookie)) != NULL)
1453                         nfsauth_free_clnt_node(node);
1454         }
1455 }
1456 
1457 /*
1458  * Called by the kernel memory allocator when
1459  * memory is low. Free unused cache entries.
1460  * If that's not enough, the VM system will
1461  * call again for some more.
1462  */
1463 /*ARGSUSED*/
1464 void
1465 exi_cache_reclaim(void *cdrarg)
1466 {
1467         int i;
1468         struct exportinfo *exi;
1469         nfs_export_t *ne = nfs_get_export();
1470 
1471         rw_enter(&ne->exported_lock, RW_READER);
1472 
1473         for (i = 0; i < EXPTABLESIZE; i++) {
1474                 for (exi = ne->exptable[i]; exi; exi = exi->fid_hash.next)
1475                         exi_cache_trim(exi);
1476         }
1477 
1478         rw_exit(&ne->exported_lock);
1479 
1480         atomic_inc_uint(&nfsauth_cache_reclaim);
1481 }
1482 
1483 void
1484 exi_cache_trim(struct exportinfo *exi)
1485 {
1486         struct auth_cache_clnt *c;
1487         struct auth_cache_clnt *nextc;
1488         struct auth_cache *p;
1489         struct auth_cache *next;
1490         int i;
1491         time_t stale_time;
1492         avl_tree_t *tree;
1493 
1494         for (i = 0; i < AUTH_TABLESIZE; i++) {
1495                 tree = exi->exi_cache[i];
1496                 stale_time = gethrestime_sec() - NFSAUTH_CACHE_TRIM;
1497                 rw_enter(&exi->exi_cache_lock, RW_READER);
1498 
1499                 /*
1500                  * Free entries that have not been
1501                  * used for NFSAUTH_CACHE_TRIM seconds.
1502                  */
1503                 for (c = avl_first(tree); c != NULL; c = AVL_NEXT(tree, c)) {
 
 | 
 
 
  35 #include <sys/kmem.h>
  36 #include <sys/pathname.h>
  37 #include <sys/utsname.h>
  38 #include <sys/debug.h>
  39 #include <sys/door.h>
  40 #include <sys/sdt.h>
  41 #include <sys/thread.h>
  42 #include <sys/avl.h>
  43 
  44 #include <rpc/types.h>
  45 #include <rpc/auth.h>
  46 #include <rpc/clnt.h>
  47 
  48 #include <nfs/nfs.h>
  49 #include <nfs/export.h>
  50 #include <nfs/nfs_clnt.h>
  51 #include <nfs/auth.h>
  52 
  53 static struct kmem_cache *exi_cache_handle;
  54 static void exi_cache_reclaim(void *);
  55 static void exi_cache_reclaim_zone(nfs_globals_t *);
  56 static void exi_cache_trim(struct exportinfo *exi);
  57 
  58 extern pri_t minclsyspri;
  59 
  60 /* NFS auth cache statistics */
  61 volatile uint_t nfsauth_cache_hit;
  62 volatile uint_t nfsauth_cache_miss;
  63 volatile uint_t nfsauth_cache_refresh;
  64 volatile uint_t nfsauth_cache_reclaim;
  65 volatile uint_t exi_cache_auth_reclaim_failed;
  66 volatile uint_t exi_cache_clnt_reclaim_failed;
  67 
  68 /*
  69  * The lifetime of an auth cache entry:
  70  * ------------------------------------
  71  *
  72  * An auth cache entry is created with both the auth_time
  73  * and auth_freshness times set to the current time.
  74  *
  75  * Upon every client access which results in a hit, the
  76  * auth_time will be updated.
 
 
 157         /*
 158          * A list_t would be overkill.  These are auth_cache entries which are
 159          * no longer linked to an exi.  It should be the case that all of their
 160          * states are NFS_AUTH_INVALID, i.e., the only way to be put on this
 161          * list is iff their state indicated that they had been placed on the
 162          * refreshq_queue.
 163          *
 164          * Note that while there is no link from the exi or back to the exi,
 165          * the exi can not go away until these entries are harvested.
 166          */
 167         struct auth_cache               *refreshq_dead_entries;
 168         nfsauth_refreshq_thread_state_t refreshq_thread_state;
 169 
 170 } nfsauth_globals_t;
 171 
 172 static void nfsauth_free_node(struct auth_cache *);
 173 static void nfsauth_refresh_thread(nfsauth_globals_t *);
 174 
 175 static int nfsauth_cache_compar(const void *, const void *);
 176 
 177 static nfsauth_globals_t *
 178 nfsauth_get_zg(void)
 179 {
 180         nfs_globals_t *ng = zone_getspecific(nfssrv_zone_key, curzone);
 181         nfsauth_globals_t *nag = ng->nfs_auth;
 182         ASSERT(nag != NULL);
 183         return (nag);
 184 }
 185 
 186 void
 187 mountd_args(uint_t did)
 188 {
 189         nfsauth_globals_t *nag;
 190 
 191         nag = nfsauth_get_zg();
 192         mutex_enter(&nag->mountd_lock);
 193         if (nag->mountd_dh != NULL)
 194                 door_ki_rele(nag->mountd_dh);
 195         nag->mountd_dh = door_ki_lookup(did);
 196         mutex_exit(&nag->mountd_lock);
 197 }
 198 
 199 void
 200 nfsauth_init(void)
 201 {
 202         exi_cache_handle = kmem_cache_create("exi_cache_handle",
 203             sizeof (struct auth_cache), 0, NULL, NULL,
 204             exi_cache_reclaim, NULL, NULL, 0);
 205 }
 206 
 207 void
 208 nfsauth_fini(void)
 209 {
 210         kmem_cache_destroy(exi_cache_handle);
 211 }
 212 
 213 void
 214 nfsauth_zone_init(nfs_globals_t *ng)
 215 {
 216         nfsauth_globals_t *nag;
 217 
 218         nag = kmem_zalloc(sizeof (*nag), KM_SLEEP);
 219 
 220         /*
 221          * mountd can be restarted by smf(5).  We need to make sure
 222          * the updated door handle will safely make it to mountd_dh.
 223          */
 224         mutex_init(&nag->mountd_lock, NULL, MUTEX_DEFAULT, NULL);
 225         mutex_init(&nag->refreshq_lock, NULL, MUTEX_DEFAULT, NULL);
 226         list_create(&nag->refreshq_queue, sizeof (refreshq_exi_node_t),
 227             offsetof(refreshq_exi_node_t, ren_node));
 228         cv_init(&nag->refreshq_cv, NULL, CV_DEFAULT, NULL);
 229         nag->refreshq_thread_state = REFRESHQ_THREAD_NEED_CREATE;
 230 
 231         ng->nfs_auth = nag;
 232 }
 233 
 234 void
 235 nfsauth_zone_shutdown(nfs_globals_t *ng)
 236 {
 237         refreshq_exi_node_t     *ren;
 238         nfsauth_globals_t       *nag = ng->nfs_auth;
 239 
 240         /* Prevent the nfsauth_refresh_thread from getting new work */
 241         mutex_enter(&nag->refreshq_lock);
 242         if (nag->refreshq_thread_state == REFRESHQ_THREAD_RUNNING) {
 243                 nag->refreshq_thread_state = REFRESHQ_THREAD_FINI_REQ;
 244                 cv_broadcast(&nag->refreshq_cv);
 245 
 246                 /* Wait for nfsauth_refresh_thread() to exit */
 247                 while (nag->refreshq_thread_state != REFRESHQ_THREAD_HALTED)
 248                         cv_wait(&nag->refreshq_cv, &nag->refreshq_lock);
 249         }
 250         mutex_exit(&nag->refreshq_lock);
 251 
 252         /*
 253          * Walk the exi_list and in turn, walk the auth_lists and free all
 254          * lists.  In addition, free INVALID auth_cache entries.
 255          */
 256         while ((ren = list_remove_head(&nag->refreshq_queue))) {
 257                 refreshq_auth_node_t *ran;
 258 
 259                 while ((ran = list_remove_head(&ren->ren_authlist)) != NULL) {
 260                         struct auth_cache *p = ran->ran_auth;
 261                         if (p->auth_state == NFS_AUTH_INVALID)
 262                                 nfsauth_free_node(p);
 263                         strfree(ran->ran_netid);
 264                         kmem_free(ran, sizeof (*ran));
 265                 }
 266 
 267                 list_destroy(&ren->ren_authlist);
 268                 exi_rele(ren->ren_exi);
 269                 kmem_free(ren, sizeof (*ren));
 270         }
 271 }
 272 
 273 void
 274 nfsauth_zone_fini(nfs_globals_t *ng)
 275 {
 276         nfsauth_globals_t *nag = ng->nfs_auth;
 277 
 278         ng->nfs_auth = NULL;
 279 
 280         list_destroy(&nag->refreshq_queue);
 281         cv_destroy(&nag->refreshq_cv);
 282         mutex_destroy(&nag->refreshq_lock);
 283         mutex_destroy(&nag->mountd_lock);
 284         /* Extra cleanup. */
 285         if (nag->mountd_dh != NULL)
 286                 door_ki_rele(nag->mountd_dh);
 287         kmem_free(nag, sizeof (*nag));
 288 }
 289 
 290 /*
 291  * Convert the address in a netbuf to
 292  * a hash index for the auth_cache table.
 293  */
 294 static int
 295 hash(struct netbuf *a)
 296 {
 297         int i, h = 0;
 298 
 299         for (i = 0; i < a->len; i++)
 
 862         struct netbuf           *taddrmask;
 863         struct netbuf           addr;   /* temporary copy of client's address */
 864         const struct netbuf     *claddr;
 865         avl_tree_t              *tree;
 866         struct auth_cache       ac;     /* used as a template for avl_find() */
 867         struct auth_cache_clnt  *c;
 868         struct auth_cache_clnt  acc;    /* used as a template for avl_find() */
 869         struct auth_cache       *p = NULL;
 870         int                     access;
 871 
 872         uid_t                   tmpuid;
 873         gid_t                   tmpgid;
 874         uint_t                  tmpngids;
 875         gid_t                   *tmpgids;
 876 
 877         avl_index_t             where;  /* used for avl_find()/avl_insert() */
 878 
 879         ASSERT(cr != NULL);
 880 
 881         ASSERT3P(curzone, ==, exi->exi_zone);
 882         nag = nfsauth_get_zg();
 883 
 884         /*
 885          * Now check whether this client already
 886          * has an entry for this flavor in the cache
 887          * for this export.
 888          * Get the caller's address, mask off the
 889          * parts of the address that do not identify
 890          * the host (port number, etc), and then hash
 891          * it to find the chain of cache entries.
 892          */
 893 
 894         claddr = svc_getrpccaller(req->rq_xprt);
 895         addr = *claddr;
 896         addr.buf = kmem_alloc(addr.maxlen, KM_SLEEP);
 897         bcopy(claddr->buf, addr.buf, claddr->len);
 898 
 899         SVC_GETADDRMASK(req->rq_xprt, SVC_TATTR_ADDRMASK, (void **)&taddrmask);
 900         ASSERT(taddrmask != NULL);
 901         addrmask(&addr, taddrmask);
 902 
 
 
1439 nfsauth_cache_free(struct exportinfo *exi)
1440 {
1441         int i;
1442 
1443         /*
1444          * The only way we got here was with an exi_rele, which means that no
1445          * auth cache entry is being refreshed.
1446          */
1447 
1448         for (i = 0; i < AUTH_TABLESIZE; i++) {
1449                 avl_tree_t *tree = exi->exi_cache[i];
1450                 void *cookie = NULL;
1451                 struct auth_cache_clnt *node;
1452 
1453                 while ((node = avl_destroy_nodes(tree, &cookie)) != NULL)
1454                         nfsauth_free_clnt_node(node);
1455         }
1456 }
1457 
1458 /*
1459  * Called by the kernel memory allocator when memory is low.
1460  * Free unused cache entries. If that's not enough, the VM system
1461  * will call again for some more.
1462  *
1463  * This needs to operate on all zones, so we take a reader lock
1464  * on the list of zones and walk the list.  This is OK here
1465  * becuase exi_cache_trim doesn't block or cause new objects
1466  * to be allocated (basically just frees lots of stuff).
1467  * Use care if nfssrv_globals_rwl is taken as reader in any
1468  * other cases because it will block nfs_server_zone_init
1469  * and nfs_server_zone_fini, which enter as writer.
1470  */
1471 /*ARGSUSED*/
1472 void
1473 exi_cache_reclaim(void *cdrarg)
1474 {
1475         nfs_globals_t *ng;
1476 
1477         rw_enter(&nfssrv_globals_rwl, RW_READER);
1478 
1479         ng = list_head(&nfssrv_globals_list);
1480         while (ng != NULL) {
1481                 exi_cache_reclaim_zone(ng);
1482                 ng = list_next(&nfssrv_globals_list, ng);
1483         }
1484 
1485         rw_exit(&nfssrv_globals_rwl);
1486 }
1487 
1488 static void
1489 exi_cache_reclaim_zone(nfs_globals_t *ng)
1490 {
1491         int i;
1492         struct exportinfo *exi;
1493         nfs_export_t *ne = ng->nfs_export;
1494 
1495         rw_enter(&ne->exported_lock, RW_READER);
1496 
1497         for (i = 0; i < EXPTABLESIZE; i++) {
1498                 for (exi = ne->exptable[i]; exi; exi = exi->fid_hash.next)
1499                         exi_cache_trim(exi);
1500         }
1501 
1502         rw_exit(&ne->exported_lock);
1503 
1504         atomic_inc_uint(&nfsauth_cache_reclaim);
1505 }
1506 
1507 static void
1508 exi_cache_trim(struct exportinfo *exi)
1509 {
1510         struct auth_cache_clnt *c;
1511         struct auth_cache_clnt *nextc;
1512         struct auth_cache *p;
1513         struct auth_cache *next;
1514         int i;
1515         time_t stale_time;
1516         avl_tree_t *tree;
1517 
1518         for (i = 0; i < AUTH_TABLESIZE; i++) {
1519                 tree = exi->exi_cache[i];
1520                 stale_time = gethrestime_sec() - NFSAUTH_CACHE_TRIM;
1521                 rw_enter(&exi->exi_cache_lock, RW_READER);
1522 
1523                 /*
1524                  * Free entries that have not been
1525                  * used for NFSAUTH_CACHE_TRIM seconds.
1526                  */
1527                 for (c = avl_first(tree); c != NULL; c = AVL_NEXT(tree, c)) {
 
 |