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)) {
|