1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <sys/systm.h>
26 #include <sys/cmn_err.h>
27 #include <sys/kmem.h>
28 #include <sys/disp.h>
29 #include <sys/id_space.h>
30 #include <sys/atomic.h>
31 #include <rpc/rpc.h>
32 #include <nfs/nfs4.h>
33 #include <nfs/nfs4_db_impl.h>
34 #include <sys/sdt.h>
35
36 static int rfs4_reap_interval = RFS4_REAP_INTERVAL;
37
38 static void rfs4_dbe_reap(rfs4_table_t *, time_t, uint32_t);
39 static void rfs4_dbe_destroy(rfs4_dbe_t *);
40 static rfs4_dbe_t *rfs4_dbe_create(rfs4_table_t *, id_t, rfs4_entry_t);
41 static void rfs4_start_reaper(rfs4_table_t *);
42
43 /*
44 * t_lowat - integer percentage of table entries /etc/system only
232 /*
233 * Given a database that has been "shutdown" by the function above all
234 * of the table tables are destroyed and then the database itself
235 * freed.
236 */
237 void
238 rfs4_database_destroy(rfs4_database_t *db)
239 {
240 rfs4_table_t *next, *tmp;
241
242 for (next = db->db_tables; next; ) {
243 tmp = next;
244 next = tmp->dbt_tnext;
245 rfs4_table_destroy(db, tmp);
246 }
247
248 mutex_destroy(db->db_lock);
249 kmem_free(db, sizeof (rfs4_database_t));
250 }
251
252 rfs4_table_t *
253 rfs4_table_create(rfs4_database_t *db, char *tabname, time_t max_cache_time,
254 uint32_t idxcnt, bool_t (*create)(rfs4_entry_t, void *),
255 void (*destroy)(rfs4_entry_t),
256 bool_t (*expiry)(rfs4_entry_t),
257 uint32_t size, uint32_t hashsize,
258 uint32_t maxentries, id_t start)
259 {
260 rfs4_table_t *table;
261 int len;
262 char *cache_name;
263 char *id_name;
264
265 table = kmem_alloc(sizeof (rfs4_table_t), KM_SLEEP);
266 table->dbt_db = db;
267 rw_init(table->dbt_t_lock, NULL, RW_DEFAULT, NULL);
268 mutex_init(table->dbt_lock, NULL, MUTEX_DEFAULT, NULL);
269 mutex_init(&table->dbt_reaper_cv_lock, NULL, MUTEX_DEFAULT, NULL);
270 cv_init(&table->dbt_reaper_wait, NULL, CV_DEFAULT, NULL);
271
287
288 if (start >= 0) {
289 if (maxentries + (uint32_t)start > (uint32_t)INT32_MAX)
290 maxentries = INT32_MAX - start;
291 id_name = kmem_alloc(len + 9 /* "_id_space" */ + 1, KM_SLEEP);
292 (void) sprintf(id_name, "%s_id_space", table->dbt_name);
293 table->dbt_id_space = id_space_create(id_name, start,
294 maxentries + start);
295 kmem_free(id_name, len + 10);
296 }
297 ASSERT(t_lowat != 0);
298 table->dbt_id_lwat = (maxentries * t_lowat) / 100;
299 ASSERT(t_hiwat != 0);
300 table->dbt_id_hwat = (maxentries * t_hiwat) / 100;
301 table->dbt_id_reap = MIN(rfs4_reap_interval, max_cache_time);
302 table->dbt_maxentries = maxentries;
303 table->dbt_create = create;
304 table->dbt_destroy = destroy;
305 table->dbt_expiry = expiry;
306
307 table->dbt_mem_cache = kmem_cache_create(cache_name,
308 sizeof (rfs4_dbe_t) + idxcnt * sizeof (rfs4_link_t) + size,
309 0,
310 rfs4_dbe_kmem_constructor,
311 rfs4_dbe_kmem_destructor,
312 NULL,
313 table,
314 NULL,
315 0);
316 kmem_free(cache_name, len+13);
317
318 table->dbt_debug = db->db_debug_flags;
319
320 mutex_enter(db->db_lock);
321 table->dbt_tnext = db->db_tables;
322 db->db_tables = table;
323 mutex_exit(db->db_lock);
324
325 rfs4_start_reaper(table);
326
327 return (table);
328 }
329
330 void
331 rfs4_table_destroy(rfs4_database_t *db, rfs4_table_t *table)
332 {
333 rfs4_table_t *p;
334 rfs4_index_t *idx;
335
347 }
348 ASSERT(p != NULL);
349 }
350 mutex_exit(db->db_lock);
351
352 /* Destroy indices */
353 while (table->dbt_indices) {
354 idx = table->dbt_indices;
355 table->dbt_indices = idx->dbi_inext;
356 rfs4_index_destroy(idx);
357 }
358
359 rw_destroy(table->dbt_t_lock);
360 mutex_destroy(table->dbt_lock);
361 mutex_destroy(&table->dbt_reaper_cv_lock);
362 cv_destroy(&table->dbt_reaper_wait);
363
364 kmem_free(table->dbt_name, strlen(table->dbt_name) + 1);
365 if (table->dbt_id_space)
366 id_space_destroy(table->dbt_id_space);
367 kmem_cache_destroy(table->dbt_mem_cache);
368 kmem_free(table, sizeof (rfs4_table_t));
369 }
370
371 rfs4_index_t *
372 rfs4_index_create(rfs4_table_t *table, char *keyname,
373 uint32_t (*hash)(void *),
374 bool_t (compare)(rfs4_entry_t, void *),
375 void *(*mkkey)(rfs4_entry_t),
376 bool_t createable)
377 {
378 rfs4_index_t *idx;
379
380 ASSERT(table->dbt_idxcnt < table->dbt_maxcnt);
381
382 idx = kmem_alloc(sizeof (rfs4_index_t), KM_SLEEP);
383
384 idx->dbi_table = table;
385 idx->dbi_keyname = kmem_alloc(strlen(keyname) + 1, KM_SLEEP);
386 (void) strcpy(idx->dbi_keyname, keyname);
387 idx->dbi_hash = hash;
666 continue;
667 l = &entry->dbe_indices[ip->dbi_tblidx];
668 i = HASH(ip, ip->dbi_mkkey(entry->dbe_data));
669 ASSERT(i < ip->dbi_table->dbt_len);
670 bp = &ip->dbi_buckets[i];
671 ENQUEUE_IDX(bp, l);
672 }
673
674 NFS4_DEBUG(
675 table->dbt_debug & SEARCH_DEBUG || table->dbt_debug & CREATE_DEBUG,
676 (CE_NOTE, "Entry %p created for %s = %p in table %s",
677 (void*)entry, idx->dbi_keyname, (void*)key, table->dbt_name));
678
679 return (entry->dbe_data);
680 }
681
682 /*ARGSUSED*/
683 boolean_t
684 rfs4_cpr_callb(void *arg, int code)
685 {
686 rfs4_table_t *table = rfs4_client_tab;
687 rfs4_bucket_t *buckets, *bp;
688 rfs4_link_t *l;
689 rfs4_client_t *cp;
690 int i;
691
692 /*
693 * We get called for Suspend and Resume events.
694 * For the suspend case we simply don't care! Nor do we care if
695 * there are no clients.
696 */
697 if (code == CB_CODE_CPR_CHKPT || table == NULL) {
698 return (B_TRUE);
699 }
700
701 buckets = table->dbt_indices->dbi_buckets;
702
703 /*
704 * When we get this far we are in the process of
705 * resuming the system from a previous suspend.
706 *
707 * We are going to blast through and update the
708 * last_access time for all the clients and in
709 * doing so extend them by one lease period.
710 */
711 for (i = 0; i < table->dbt_len; i++) {
862 do {
863 CALLB_CPR_SAFE_BEGIN(&table->dbt_reaper_cpr_info);
864 rc = cv_reltimedwait_sig(&table->dbt_reaper_wait,
865 &table->dbt_reaper_cv_lock,
866 SEC_TO_TICK(table->dbt_id_reap), TR_CLOCK_TICK);
867 CALLB_CPR_SAFE_END(&table->dbt_reaper_cpr_info,
868 &table->dbt_reaper_cv_lock);
869 rfs4_dbe_reap(table, table->dbt_max_cache_time, 0);
870 } while (rc != 0 && table->dbt_reaper_shutdown == FALSE);
871
872 CALLB_CPR_EXIT(&table->dbt_reaper_cpr_info);
873
874 NFS4_DEBUG(table->dbt_debug,
875 (CE_NOTE, "rfs4_reaper_thread exiting for %s", table->dbt_name));
876
877 /* Notify the database shutdown processing that the table is shutdown */
878 mutex_enter(table->dbt_db->db_lock);
879 table->dbt_db->db_shutdown_count--;
880 cv_signal(&table->dbt_db->db_shutdown_wait);
881 mutex_exit(table->dbt_db->db_lock);
882 }
883
884 static void
885 rfs4_start_reaper(rfs4_table_t *table)
886 {
887 if (table->dbt_max_cache_time == 0)
888 return;
889
890 (void) thread_create(NULL, 0, reaper_thread, table, 0, &p0, TS_RUN,
891 minclsyspri);
892 }
893
894 #ifdef DEBUG
895 void
896 rfs4_dbe_debug(rfs4_dbe_t *entry)
897 {
898 cmn_err(CE_NOTE, "Entry %p from table %s",
899 (void *)entry, entry->dbe_table->dbt_name);
900 cmn_err(CE_CONT, "\trefcnt = %d id = %d",
901 entry->dbe_refcnt, entry->dbe_id);
902 }
903 #endif
|
1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * Copyright 2018 Nexenta Systems, Inc.
28 */
29
30 #include <sys/systm.h>
31 #include <sys/cmn_err.h>
32 #include <sys/kmem.h>
33 #include <sys/disp.h>
34 #include <sys/id_space.h>
35 #include <sys/atomic.h>
36 #include <rpc/rpc.h>
37 #include <nfs/nfs4.h>
38 #include <nfs/nfs4_db_impl.h>
39 #include <sys/sdt.h>
40
41 static int rfs4_reap_interval = RFS4_REAP_INTERVAL;
42
43 static void rfs4_dbe_reap(rfs4_table_t *, time_t, uint32_t);
44 static void rfs4_dbe_destroy(rfs4_dbe_t *);
45 static rfs4_dbe_t *rfs4_dbe_create(rfs4_table_t *, id_t, rfs4_entry_t);
46 static void rfs4_start_reaper(rfs4_table_t *);
47
48 /*
49 * t_lowat - integer percentage of table entries /etc/system only
237 /*
238 * Given a database that has been "shutdown" by the function above all
239 * of the table tables are destroyed and then the database itself
240 * freed.
241 */
242 void
243 rfs4_database_destroy(rfs4_database_t *db)
244 {
245 rfs4_table_t *next, *tmp;
246
247 for (next = db->db_tables; next; ) {
248 tmp = next;
249 next = tmp->dbt_tnext;
250 rfs4_table_destroy(db, tmp);
251 }
252
253 mutex_destroy(db->db_lock);
254 kmem_free(db, sizeof (rfs4_database_t));
255 }
256
257 /*
258 * Used to get the correct kmem_cache database for the state table being
259 * created.
260 * Helper function for rfs4_table_create
261 */
262 static kmem_cache_t *
263 get_db_mem_cache(char *name)
264 {
265 int i;
266
267 for (i = 0; i < RFS4_DB_MEM_CACHE_NUM; i++) {
268 if (strcmp(name, rfs4_db_mem_cache_table[i].r_db_name) == 0)
269 return (rfs4_db_mem_cache_table[i].r_db_mem_cache);
270 }
271 /*
272 * There is no associated kmem cache for this NFS4 server state
273 * table name
274 */
275 return (NULL);
276 }
277
278 /*
279 * Used to initialize the global NFSv4 server state database.
280 * Helper funtion for rfs4_state_g_init and called when module is loaded.
281 */
282 kmem_cache_t *
283 /* CSTYLED */
284 nfs4_init_mem_cache(char *cache_name, uint32_t idxcnt, uint32_t size, uint32_t idx)
285 {
286 kmem_cache_t *mem_cache = kmem_cache_create(cache_name,
287 sizeof (rfs4_dbe_t) + idxcnt * sizeof (rfs4_link_t) + size,
288 0,
289 rfs4_dbe_kmem_constructor,
290 rfs4_dbe_kmem_destructor,
291 NULL,
292 NULL,
293 NULL,
294 0);
295 (void) strlcpy(rfs4_db_mem_cache_table[idx].r_db_name, cache_name,
296 strlen(cache_name) + 1);
297 rfs4_db_mem_cache_table[idx].r_db_mem_cache = mem_cache;
298 return (mem_cache);
299 }
300
301 rfs4_table_t *
302 rfs4_table_create(rfs4_database_t *db, char *tabname, time_t max_cache_time,
303 uint32_t idxcnt, bool_t (*create)(rfs4_entry_t, void *),
304 void (*destroy)(rfs4_entry_t),
305 bool_t (*expiry)(rfs4_entry_t),
306 uint32_t size, uint32_t hashsize,
307 uint32_t maxentries, id_t start)
308 {
309 rfs4_table_t *table;
310 int len;
311 char *cache_name;
312 char *id_name;
313
314 table = kmem_alloc(sizeof (rfs4_table_t), KM_SLEEP);
315 table->dbt_db = db;
316 rw_init(table->dbt_t_lock, NULL, RW_DEFAULT, NULL);
317 mutex_init(table->dbt_lock, NULL, MUTEX_DEFAULT, NULL);
318 mutex_init(&table->dbt_reaper_cv_lock, NULL, MUTEX_DEFAULT, NULL);
319 cv_init(&table->dbt_reaper_wait, NULL, CV_DEFAULT, NULL);
320
336
337 if (start >= 0) {
338 if (maxentries + (uint32_t)start > (uint32_t)INT32_MAX)
339 maxentries = INT32_MAX - start;
340 id_name = kmem_alloc(len + 9 /* "_id_space" */ + 1, KM_SLEEP);
341 (void) sprintf(id_name, "%s_id_space", table->dbt_name);
342 table->dbt_id_space = id_space_create(id_name, start,
343 maxentries + start);
344 kmem_free(id_name, len + 10);
345 }
346 ASSERT(t_lowat != 0);
347 table->dbt_id_lwat = (maxentries * t_lowat) / 100;
348 ASSERT(t_hiwat != 0);
349 table->dbt_id_hwat = (maxentries * t_hiwat) / 100;
350 table->dbt_id_reap = MIN(rfs4_reap_interval, max_cache_time);
351 table->dbt_maxentries = maxentries;
352 table->dbt_create = create;
353 table->dbt_destroy = destroy;
354 table->dbt_expiry = expiry;
355
356 /*
357 * get the correct kmem_cache for this table type based on the name.
358 */
359 table->dbt_mem_cache = get_db_mem_cache(cache_name);
360
361 kmem_free(cache_name, len+13);
362
363 table->dbt_debug = db->db_debug_flags;
364
365 mutex_enter(db->db_lock);
366 table->dbt_tnext = db->db_tables;
367 db->db_tables = table;
368 mutex_exit(db->db_lock);
369
370 rfs4_start_reaper(table);
371
372 return (table);
373 }
374
375 void
376 rfs4_table_destroy(rfs4_database_t *db, rfs4_table_t *table)
377 {
378 rfs4_table_t *p;
379 rfs4_index_t *idx;
380
392 }
393 ASSERT(p != NULL);
394 }
395 mutex_exit(db->db_lock);
396
397 /* Destroy indices */
398 while (table->dbt_indices) {
399 idx = table->dbt_indices;
400 table->dbt_indices = idx->dbi_inext;
401 rfs4_index_destroy(idx);
402 }
403
404 rw_destroy(table->dbt_t_lock);
405 mutex_destroy(table->dbt_lock);
406 mutex_destroy(&table->dbt_reaper_cv_lock);
407 cv_destroy(&table->dbt_reaper_wait);
408
409 kmem_free(table->dbt_name, strlen(table->dbt_name) + 1);
410 if (table->dbt_id_space)
411 id_space_destroy(table->dbt_id_space);
412 table->dbt_mem_cache = NULL;
413 kmem_free(table, sizeof (rfs4_table_t));
414 }
415
416 rfs4_index_t *
417 rfs4_index_create(rfs4_table_t *table, char *keyname,
418 uint32_t (*hash)(void *),
419 bool_t (compare)(rfs4_entry_t, void *),
420 void *(*mkkey)(rfs4_entry_t),
421 bool_t createable)
422 {
423 rfs4_index_t *idx;
424
425 ASSERT(table->dbt_idxcnt < table->dbt_maxcnt);
426
427 idx = kmem_alloc(sizeof (rfs4_index_t), KM_SLEEP);
428
429 idx->dbi_table = table;
430 idx->dbi_keyname = kmem_alloc(strlen(keyname) + 1, KM_SLEEP);
431 (void) strcpy(idx->dbi_keyname, keyname);
432 idx->dbi_hash = hash;
711 continue;
712 l = &entry->dbe_indices[ip->dbi_tblidx];
713 i = HASH(ip, ip->dbi_mkkey(entry->dbe_data));
714 ASSERT(i < ip->dbi_table->dbt_len);
715 bp = &ip->dbi_buckets[i];
716 ENQUEUE_IDX(bp, l);
717 }
718
719 NFS4_DEBUG(
720 table->dbt_debug & SEARCH_DEBUG || table->dbt_debug & CREATE_DEBUG,
721 (CE_NOTE, "Entry %p created for %s = %p in table %s",
722 (void*)entry, idx->dbi_keyname, (void*)key, table->dbt_name));
723
724 return (entry->dbe_data);
725 }
726
727 /*ARGSUSED*/
728 boolean_t
729 rfs4_cpr_callb(void *arg, int code)
730 {
731 rfs4_bucket_t *buckets, *bp;
732 rfs4_link_t *l;
733 rfs4_client_t *cp;
734 int i;
735
736 nfs4_srv_t *nsrv4 = nfs4_get_srv();
737 rfs4_table_t *table = nsrv4->rfs4_client_tab;
738
739 /*
740 * We get called for Suspend and Resume events.
741 * For the suspend case we simply don't care! Nor do we care if
742 * there are no clients.
743 */
744 if (code == CB_CODE_CPR_CHKPT || table == NULL) {
745 return (B_TRUE);
746 }
747
748 buckets = table->dbt_indices->dbi_buckets;
749
750 /*
751 * When we get this far we are in the process of
752 * resuming the system from a previous suspend.
753 *
754 * We are going to blast through and update the
755 * last_access time for all the clients and in
756 * doing so extend them by one lease period.
757 */
758 for (i = 0; i < table->dbt_len; i++) {
909 do {
910 CALLB_CPR_SAFE_BEGIN(&table->dbt_reaper_cpr_info);
911 rc = cv_reltimedwait_sig(&table->dbt_reaper_wait,
912 &table->dbt_reaper_cv_lock,
913 SEC_TO_TICK(table->dbt_id_reap), TR_CLOCK_TICK);
914 CALLB_CPR_SAFE_END(&table->dbt_reaper_cpr_info,
915 &table->dbt_reaper_cv_lock);
916 rfs4_dbe_reap(table, table->dbt_max_cache_time, 0);
917 } while (rc != 0 && table->dbt_reaper_shutdown == FALSE);
918
919 CALLB_CPR_EXIT(&table->dbt_reaper_cpr_info);
920
921 NFS4_DEBUG(table->dbt_debug,
922 (CE_NOTE, "rfs4_reaper_thread exiting for %s", table->dbt_name));
923
924 /* Notify the database shutdown processing that the table is shutdown */
925 mutex_enter(table->dbt_db->db_lock);
926 table->dbt_db->db_shutdown_count--;
927 cv_signal(&table->dbt_db->db_shutdown_wait);
928 mutex_exit(table->dbt_db->db_lock);
929 zthread_exit();
930 }
931
932 static void
933 rfs4_start_reaper(rfs4_table_t *table)
934 {
935 if (table->dbt_max_cache_time == 0)
936 return;
937
938 (void) zthread_create(NULL, 0, reaper_thread, table, 0,
939 minclsyspri);
940 }
941
942 #ifdef DEBUG
943 void
944 rfs4_dbe_debug(rfs4_dbe_t *entry)
945 {
946 cmn_err(CE_NOTE, "Entry %p from table %s",
947 (void *)entry, entry->dbe_table->dbt_name);
948 cmn_err(CE_CONT, "\trefcnt = %d id = %d",
949 entry->dbe_refcnt, entry->dbe_id);
950 }
951 #endif
|