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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include "iscsi.h" 27 #include "nvfile.h" 28 #include <sys/file.h> /* defines: FKIOCTL */ 29 30 #define NVF_GETF 16 31 static kmutex_t nvf_getf_lock; 32 static file_t *nvf_fd[NVF_GETF]; 33 34 /* 35 * file names 36 */ 37 #define NVF_FILENAME "/etc/iscsi/iscsi" 38 #define NVF_CURR_FILE_SUFFIX "dbc" 39 #define NVF_PREV_FILE_SUFFIX "dbp" 40 #define NVF_TMP_FILENAME "/etc/iscsi/iscsi.dbt" 41 #define NVF_MAX_FILENAME_LEN 40 42 43 /* 44 * file header definitions 45 */ 46 #define NVF_HDR_MAGIC 0x15C510DB /* iSCSI DB */ 47 #define NVF_HDR_VERSION 1 48 #define NVF_HDR_SIZE 128 49 50 51 /* 52 * file flag definitions 53 * 54 * These flags describe the current state of reading or writing 55 * the NVFILE/NVPAIR or the backing file. 56 * 57 * These flags are derived from a like NVPAIR/NVFILE implementation 58 * in usr/src/uts/common/sys/devctl_impl.h 59 */ 60 #define NVF_ACTIVE 0x01 /* nvlist/nvpair file is active */ 61 #define NVF_DIRTY 0x02 /* needs to be flushed */ 62 #define NVF_SCHED 0x04 /* flush thread is currently scheduled */ 63 #define NVF_FLUSHING 0x08 /* in process of being flushed */ 64 #define NVF_ERROR 0x10 /* most recent flush failed */ 65 66 #define NVF_IS_ACTIVE(flag) (flag & NVF_ACTIVE) 67 #define NVF_MARK_ACTIVE(flag) (flag |= NVF_ACTIVE) 68 #define NVF_CLEAR_ACTIVE(flag) (flag &= ~NVF_ACTIVE) 69 70 #define NVF_IS_DIRTY(flag) (flag & NVF_DIRTY) 71 #define NVF_MARK_DIRTY(flag) (flag |= NVF_DIRTY) 72 #define NVF_CLEAR_DIRTY(flag) (flag &= ~NVF_DIRTY) 73 74 #define NVF_IS_SCHED(flag) (flag & NVF_SCHED) 75 #define NVF_MARK_SCHED(flag) (flag |= NVF_SCHED) 76 #define NVF_CLEAR_SCHED(flag) (flag &= ~NVF_SCHED) 77 78 /* 79 * file flush time constants 80 */ 81 #define NVF_FLUSH_DELAY 10 /* number of ticks before flush */ 82 #define NVF_RESCHED_MIN_TICKS 5 /* min # of ticks to resched thread */ 83 #define NVF_FLUSH_BACKOFF_DELAY (SEC_TO_TICK(300)) /* re-try flush in 5 min */ 84 85 /* 86 * file access operations 87 */ 88 static file_t *nvf_getf(int fdes); 89 static void nvf_releasef(int fdes); 90 static int nvf_setf(file_t *fp); 91 92 static int nvf_open(char *path, int flags, int mode); 93 static int nvf_close(int fdes); 94 static int nvf_remove(char *filename); 95 static int nvf_rename(char *oldname, char *newname); 96 static ssize_t nvf_read(int fdes, void *cbuf, ssize_t count); 97 static ssize_t nvf_write(int fdes, void *cbuf, ssize_t count); 98 99 int nvf_errno; 100 101 /* 102 * file header data structure definition 103 * 104 * This data structure definition was derived from a like data structure 105 * (nvpf_hdr_t) in usr/src/uts/common/sys/devctl_impl.h 106 * 107 * This header is larger than need in order to support extensability in the 108 * future 109 * 110 */ 111 typedef struct nvf_hdr { 112 union { 113 struct hdr { 114 uint32_t h_magic; 115 int32_t h_ver; 116 int64_t h_size; 117 uint16_t h_hdrsum; 118 uint16_t h_datasum; 119 } h_info; 120 uchar_t h_pad[NVF_HDR_SIZE]; 121 } h_u; 122 } nvf_hdr_t; 123 124 #define nvfh_magic h_u.h_info.h_magic 125 #define nvfh_ver h_u.h_info.h_ver 126 #define nvfh_size h_u.h_info.h_size 127 #define nvfh_hdrsum h_u.h_info.h_hdrsum 128 #define nvfh_datasum h_u.h_info.h_datasum 129 130 131 /* 132 * Local Global Variables 133 */ 134 static nvlist_t *nvf_list; /* pointer to root nvlist */ 135 static uint32_t nvf_flags; /* nvfile state flags */ 136 static kmutex_t nvf_lock; /* lock for file */ 137 static krwlock_t nvf_list_lock; /* lock for nvlist access */ 138 static timeout_id_t nvf_thread_id; /* thread identifier */ 139 static clock_t nvf_thread_ticks; /* timeout tick value */ 140 static char nvf_curr_filename[NVF_MAX_FILENAME_LEN]; 141 static char nvf_prev_filename[NVF_MAX_FILENAME_LEN]; 142 static boolean_t nvf_written_once; /* File has been written once */ 143 /* 144 * Local Function Prototypes 145 */ 146 static uint16_t nvf_chksum(char *buf, int64_t buflen); 147 static void nvf_thread(void *arg); 148 static boolean_t nvf_flush(void); 149 static boolean_t nvf_parse(char *filename); 150 151 /* 152 * NVLIST/NVPAIR FILE External Interfaces 153 */ 154 155 void 156 nvf_init(void) 157 { 158 mutex_init(&nvf_getf_lock, NULL, MUTEX_DRIVER, NULL); 159 nvf_list = NULL; 160 nvf_flags = 0; 161 NVF_MARK_ACTIVE(nvf_flags); 162 nvf_thread_id = 0; 163 nvf_thread_ticks = 0; 164 nvf_written_once = B_FALSE; 165 (void) snprintf(nvf_curr_filename, NVF_MAX_FILENAME_LEN, "%s_v%d.%s", 166 NVF_FILENAME, NVF_HDR_VERSION, NVF_CURR_FILE_SUFFIX); 167 (void) snprintf(nvf_prev_filename, NVF_MAX_FILENAME_LEN, "%s_v%d.%s", 168 NVF_FILENAME, NVF_HDR_VERSION, NVF_PREV_FILE_SUFFIX); 169 mutex_init(&nvf_lock, NULL, MUTEX_DRIVER, NULL); 170 rw_init(&nvf_list_lock, NULL, RW_DRIVER, NULL); 171 } 172 173 void 174 nvf_fini(void) 175 { 176 mutex_enter(&nvf_lock); 177 NVF_CLEAR_ACTIVE(nvf_flags); 178 if (NVF_IS_SCHED(nvf_flags)) { 179 nvf_thread_ticks = 0; 180 mutex_exit(&nvf_lock); 181 (void) untimeout(nvf_thread_id); 182 mutex_enter(&nvf_lock); 183 } 184 185 rw_enter(&nvf_list_lock, RW_WRITER); 186 if (nvf_list) { 187 nvlist_free(nvf_list); 188 } 189 nvf_list = NULL; 190 rw_exit(&nvf_list_lock); 191 mutex_exit(&nvf_lock); 192 193 rw_destroy(&nvf_list_lock); 194 mutex_destroy(&nvf_lock); 195 } 196 197 /* 198 * nvf_load - load contents of NVLIST/NVPAIR file into memory. 199 */ 200 boolean_t 201 nvf_load(void) 202 { 203 char corrupt_filename[NVF_MAX_FILENAME_LEN]; 204 boolean_t rval; 205 206 mutex_enter(&nvf_lock); 207 208 /* 209 * try to load current file 210 */ 211 rval = nvf_parse(nvf_curr_filename); 212 if (rval == B_TRUE) { 213 mutex_exit(&nvf_lock); 214 return (rval); 215 } else { 216 /* 217 * Rename current file to add corrupted suffix 218 */ 219 (void) snprintf(corrupt_filename, NVF_MAX_FILENAME_LEN, 220 "%s.corrupt", nvf_curr_filename); 221 (void) nvf_rename(nvf_curr_filename, corrupt_filename); 222 } 223 224 /* 225 * try to load previous file 226 */ 227 rval = nvf_parse(nvf_prev_filename); 228 if (rval == B_TRUE) { 229 mutex_exit(&nvf_lock); 230 return (rval); 231 } else { 232 /* 233 * Rename previous file to add corrupted suffix 234 */ 235 (void) snprintf(corrupt_filename, NVF_MAX_FILENAME_LEN, 236 "%s.corrupt", nvf_prev_filename); 237 (void) nvf_rename(nvf_prev_filename, corrupt_filename); 238 } 239 240 /* 241 * non-existent or corrupted files are OK. We just create 242 * an empty root nvlist and then write to file when 243 * something added. However, ensure that any current root nvlist 244 * is deallocated before allocating a new one. 245 */ 246 rw_enter(&nvf_list_lock, RW_WRITER); 247 if (nvf_list != NULL) { 248 nvlist_free(nvf_list); 249 nvf_list = NULL; 250 } 251 rw_exit(&nvf_list_lock); 252 253 rval = nvlist_alloc(&nvf_list, NV_UNIQUE_NAME, KM_SLEEP); 254 if (rval != 0) { 255 cmn_err(CE_NOTE, "!iscsi persistent store failed to " 256 "allocate root list (%d)", rval); 257 } 258 259 mutex_exit(&nvf_lock); 260 return (rval == 0 ? B_TRUE : B_FALSE); 261 } 262 263 /* 264 * nvf_update - start process of updating the NVPAIR/NVLIST file. 265 * 266 * This function is derived from a like NVPAIR/NVFILE implementation 267 * in usr/src/uts/common/os/devctl.c 268 * 269 */ 270 void 271 nvf_update(void) 272 { 273 mutex_enter(&nvf_lock); 274 275 /* 276 * set dirty flag to indicate data flush is needed 277 */ 278 NVF_MARK_DIRTY(nvf_flags); 279 280 /* 281 * If thread is already started just update number of 282 * ticks before flush, otherwise start thread and set 283 * number of ticks. The thread will is responsible 284 * for starting the actual store to file. 285 * 286 * If update error occured previously, reschedule flush to 287 * occur almost immediately. If error still exists, the 288 * update thread will be backed off again 289 */ 290 if (!NVF_IS_SCHED(nvf_flags)) { 291 NVF_MARK_SCHED(nvf_flags); 292 mutex_exit(&nvf_lock); 293 nvf_thread_id = timeout(nvf_thread, NULL, NVF_FLUSH_DELAY); 294 } else { 295 nvf_thread_ticks = ddi_get_lbolt() + NVF_FLUSH_DELAY; 296 /* 297 * If update error occured previously, reschedule flush 298 * attempt to occur quickly. If an error still exists 299 * after a flush attempt, the update thread will be backed 300 * off again 301 */ 302 if (nvf_flags & NVF_ERROR) { 303 mutex_exit(&nvf_lock); 304 (void) untimeout(nvf_thread_id); 305 nvf_thread_id = timeout(nvf_thread, NULL, 306 NVF_FLUSH_DELAY); 307 } else { 308 mutex_exit(&nvf_lock); 309 } 310 } 311 } 312 313 /* 314 * nvf_data_check -- check if specified list exists 315 */ 316 boolean_t 317 nvf_list_check(char *id) 318 { 319 nvlist_t *list = NULL; 320 int rval; 321 322 /* 323 * find the specified list 324 */ 325 rw_enter(&nvf_list_lock, RW_READER); 326 rval = nvlist_lookup_nvlist(nvf_list, id, &list); 327 rw_exit(&nvf_list_lock); 328 329 return (rval == 0 ? B_TRUE : B_FALSE); 330 } 331 332 /* 333 * nvf_node_value_set - store value associated with node 334 */ 335 boolean_t 336 nvf_node_value_set(char *id, uint32_t value) 337 { 338 int rval; 339 340 ASSERT(id != NULL); 341 342 rw_enter(&nvf_list_lock, RW_WRITER); 343 344 if (nvf_list == NULL) { 345 rw_exit(&nvf_list_lock); 346 return (B_FALSE); 347 } 348 349 /* 350 * update value. If value already exists, it will be replaced 351 * by this update. 352 */ 353 rval = nvlist_add_uint32(nvf_list, id, value); 354 if (rval == 0) { 355 /* 356 * value was set, so update associated file 357 */ 358 nvf_update(); 359 } else { 360 cmn_err(CE_NOTE, "!iscsi persistent store failed to " 361 "store %s value (%d)", id, rval); 362 } 363 364 rw_exit(&nvf_list_lock); 365 return (rval == 0 ? B_TRUE : B_FALSE); 366 } 367 368 /* 369 * nvf_node_value_get - obtain value associated with node 370 */ 371 boolean_t 372 nvf_node_value_get(char *id, uint32_t *value) 373 { 374 boolean_t rval; 375 376 ASSERT(id != NULL); 377 ASSERT(value != NULL); 378 379 rw_enter(&nvf_list_lock, RW_READER); 380 381 if (nvf_list == NULL) { 382 rw_exit(&nvf_list_lock); 383 return (B_FALSE); 384 } 385 386 rval = nvlist_lookup_uint32(nvf_list, id, value); 387 388 rw_exit(&nvf_list_lock); 389 return (rval == 0 ? B_TRUE : B_FALSE); 390 } 391 392 /* 393 * nvf_node_name_set - store a specific type of name 394 */ 395 boolean_t 396 nvf_node_name_set(char *id, char *name) 397 { 398 boolean_t rval = B_TRUE; 399 400 rw_enter(&nvf_list_lock, RW_WRITER); 401 402 if (nvf_list == NULL) { 403 rw_exit(&nvf_list_lock); 404 return (B_FALSE); 405 } 406 407 rval = nvlist_add_string(nvf_list, id, name); 408 if (rval == 0) { 409 nvf_update(); 410 } else { 411 cmn_err(CE_NOTE, "!iscsi persistent store failed to " 412 "store %s name (%d)", id, rval); 413 } 414 415 rw_exit(&nvf_list_lock); 416 return (rval == 0 ? B_TRUE : B_FALSE); 417 } 418 419 /* 420 * nvf_node_name_get - return a specific type of name 421 */ 422 boolean_t 423 nvf_node_name_get(char *id, char *name, uint_t nsize) 424 { 425 boolean_t rval = B_FALSE; 426 char *tmpname; 427 428 rw_enter(&nvf_list_lock, RW_READER); 429 430 if (nvf_list == NULL) { 431 rw_exit(&nvf_list_lock); 432 return (B_FALSE); 433 } 434 435 rval = nvlist_lookup_string(nvf_list, id, &tmpname); 436 if (rval == 0) { 437 /* 438 * ensure name is able to fit into given buffer 439 */ 440 if (strlen(tmpname) < nsize) { 441 (void) strcpy(name, tmpname); 442 rval = B_TRUE; 443 } else { 444 cmn_err(CE_NOTE, "!iscsi persistent store " 445 "unable to fit %s node name into buffer %d %s", 446 tmpname, nsize, id); 447 rval = B_FALSE; 448 } 449 } else { 450 rval = B_FALSE; 451 } 452 453 rw_exit(&nvf_list_lock); 454 return (rval); 455 } 456 457 /* 458 * nvf_node_data_set -- store data element associated with node 459 */ 460 boolean_t 461 nvf_node_data_set(char *name, void *data, uint_t dsize) 462 { 463 int rval; 464 465 ASSERT(name != NULL); 466 ASSERT(data != NULL); 467 468 rw_enter(&nvf_list_lock, RW_WRITER); 469 470 if (nvf_list == NULL) { 471 rw_exit(&nvf_list_lock); 472 return (B_FALSE); 473 } 474 475 /* 476 * update the address configuration element in the specific 477 * list. If this element already exists, it will be 478 * replaced by this update. 479 */ 480 rval = nvlist_add_byte_array(nvf_list, name, (uchar_t *)data, dsize); 481 if (rval == 0) { 482 /* 483 * data was set, so update associated file 484 */ 485 nvf_update(); 486 } else { 487 cmn_err(CE_NOTE, "!iscsi persistent store failed " 488 "to store %s name (%d)", name, rval); 489 } 490 491 rw_exit(&nvf_list_lock); 492 return (rval == 0 ? B_TRUE : B_FALSE); 493 } 494 495 /* 496 * nvf_node_data_get -- obtain a data element associated with node 497 */ 498 iscsi_nvfile_status_t 499 nvf_node_data_get(char *name, void *data, uint_t dsize) 500 { 501 uchar_t *value = NULL; 502 uint_t vsize; 503 int rval = 0; 504 iscsi_nvfile_status_t status = ISCSI_NVFILE_SUCCESS; 505 506 ASSERT(name != NULL); 507 ASSERT(data != NULL); 508 509 rw_enter(&nvf_list_lock, RW_READER); 510 511 if (nvf_list == NULL) { 512 rw_exit(&nvf_list_lock); 513 return (ISCSI_NVFILE_NVF_LIST_NOT_FOUND); 514 } 515 516 rval = nvlist_lookup_byte_array(nvf_list, name, &value, &vsize); 517 if (rval == 0) { 518 /* 519 * ensure data is able to fit into given buffer 520 */ 521 if (vsize <= dsize) { 522 bcopy(value, data, vsize); 523 } else { 524 bcopy(value, data, dsize); 525 } 526 status = ISCSI_NVFILE_SUCCESS; 527 } else if (rval == ENOENT) { 528 status = ISCSI_NVFILE_NAMEVAL_NOT_FOUND; 529 } else { 530 status = ISCSI_NVFILE_FAILURE; 531 } 532 533 rw_exit(&nvf_list_lock); 534 return (status); 535 } 536 537 /* 538 * nvf_node_data_clear -- remove a data element associated with node 539 */ 540 boolean_t 541 nvf_node_data_clear(char *name) 542 { 543 int rval; 544 545 ASSERT(name != NULL); 546 547 rw_enter(&nvf_list_lock, RW_WRITER); 548 549 if (nvf_list == NULL) { 550 rw_exit(&nvf_list_lock); 551 return (B_FALSE); 552 } 553 554 /* 555 * remove the specified data element 556 */ 557 rval = nvlist_remove(nvf_list, name, DATA_TYPE_BYTE_ARRAY); 558 if (rval == 0) { 559 /* 560 * data was set, so update associated file 561 */ 562 nvf_update(); 563 } 564 565 rw_exit(&nvf_list_lock); 566 return (rval == 0 ? B_TRUE : B_FALSE); 567 } 568 569 /* 570 * nvf_data_set -- store a data element in the specified list 571 */ 572 boolean_t 573 nvf_data_set(char *id, char *name, void *data, uint_t dsize) 574 { 575 nvlist_t *list = NULL; 576 int rval; 577 boolean_t list_alloc = B_FALSE; 578 579 ASSERT(id != NULL); 580 ASSERT(name != NULL); 581 ASSERT(data != NULL); 582 583 rw_enter(&nvf_list_lock, RW_WRITER); 584 585 if (nvf_list == NULL) { 586 rw_exit(&nvf_list_lock); 587 return (B_FALSE); 588 } 589 590 /* 591 * find the specified list 592 */ 593 rval = nvlist_lookup_nvlist(nvf_list, id, &list); 594 if (rval != 0) { 595 rval = nvlist_alloc(&list, NV_UNIQUE_NAME, KM_SLEEP); 596 if (rval != 0) { 597 cmn_err(CE_NOTE, "!iscsi persistent store failed to " 598 "allocate %s list (%d)", id, rval); 599 rw_exit(&nvf_list_lock); 600 return (B_FALSE); 601 } 602 list_alloc = B_TRUE; 603 } 604 605 /* 606 * update the data element in the specified list. If this element 607 * already exists, it will be replaced by this update. 608 */ 609 rval = nvlist_add_byte_array(list, name, (uchar_t *)data, dsize); 610 if (rval == 0) { 611 rval = nvlist_add_nvlist(nvf_list, id, list); 612 if (rval != 0) { 613 cmn_err(CE_NOTE, "!iscsi persistent store failed " 614 "to add %s list to root (%d)", id, rval); 615 rw_exit(&nvf_list_lock); 616 return (B_FALSE); 617 } 618 /* 619 * data was set, so update file 620 */ 621 nvf_update(); 622 } else { 623 cmn_err(CE_NOTE, "!iscsi persistent store failed to " 624 "store %s in %s list (%d)", name, id, rval); 625 } 626 627 if (list_alloc) { 628 nvlist_free(list); 629 } 630 631 rw_exit(&nvf_list_lock); 632 return (rval == 0 ? B_TRUE : B_FALSE); 633 } 634 635 /* 636 * nvf_data_get -- get a data element from the specified list 637 */ 638 boolean_t 639 nvf_data_get(char *id, char *name, void *data, uint_t dsize) 640 { 641 nvlist_t *list = NULL; 642 uchar_t *value = NULL; 643 uint_t vsize; 644 int rval; 645 646 ASSERT(id != NULL); 647 ASSERT(name != NULL); 648 ASSERT(data != NULL); 649 650 rw_enter(&nvf_list_lock, RW_READER); 651 652 if (nvf_list == NULL) { 653 rw_exit(&nvf_list_lock); 654 return (B_FALSE); 655 } 656 657 /* 658 * find the specified list 659 */ 660 rval = nvlist_lookup_nvlist(nvf_list, id, &list); 661 if (rval != 0) { 662 rw_exit(&nvf_list_lock); 663 return (B_FALSE); 664 } 665 666 /* obtain data element from list */ 667 ASSERT(list != NULL); 668 rval = nvlist_lookup_byte_array(list, name, &value, &vsize); 669 if (rval == 0) { 670 /* 671 * ensure data is able to fit into given buffer 672 */ 673 if (vsize <= dsize) { 674 bcopy(value, data, vsize); 675 } else { 676 bcopy(value, data, dsize); 677 } 678 } 679 680 rw_exit(&nvf_list_lock); 681 return (rval == 0 ? B_TRUE : B_FALSE); 682 } 683 684 685 /* 686 * nvf_data_next -- get the next data element in the specified list 687 */ 688 boolean_t 689 nvf_data_next(char *id, void **v, char *name, void *data, uint_t dsize) 690 { 691 nvlist_t *list = NULL; 692 nvpair_t *pair = NULL; 693 uchar_t *value = NULL; 694 uint_t vsize; 695 int rval; 696 697 ASSERT(id != NULL); 698 ASSERT(v != NULL); 699 ASSERT(name != NULL); 700 ASSERT(data != NULL); 701 702 rw_enter(&nvf_list_lock, RW_READER); 703 if (nvf_list == NULL) { 704 rw_exit(&nvf_list_lock); 705 return (B_FALSE); 706 } 707 708 /* 709 * find the specified list 710 */ 711 rval = nvlist_lookup_nvlist(nvf_list, id, &list); 712 if (rval != 0) { 713 rw_exit(&nvf_list_lock); 714 return (B_FALSE); 715 } 716 717 /* 718 * get the next nvpair data item in the list 719 */ 720 pair = nvlist_next_nvpair(list, (nvpair_t *)*v); 721 *v = (void *)pair; 722 if (pair == NULL) { 723 rw_exit(&nvf_list_lock); 724 return (B_FALSE); 725 } 726 727 /* 728 * get the data bytes 729 */ 730 rval = nvpair_value_byte_array(pair, &value, &vsize); 731 if (rval != 0) { 732 rw_exit(&nvf_list_lock); 733 return (B_FALSE); 734 } 735 736 /* 737 * ensure data is able to fit into given buffer 738 */ 739 (void) strcpy(name, nvpair_name(pair)); 740 if (vsize <= dsize) { 741 bcopy(value, data, vsize); 742 } else { 743 bcopy(value, data, dsize); 744 } 745 746 rw_exit(&nvf_list_lock); 747 return (B_TRUE); 748 } 749 750 /* 751 * nvf_data_clear -- remove a data element from the specified list 752 */ 753 boolean_t 754 nvf_data_clear(char *id, char *name) 755 { 756 nvlist_t *list = NULL; 757 int rval = B_FALSE; 758 759 ASSERT(id != NULL); 760 ASSERT(name != NULL); 761 762 rw_enter(&nvf_list_lock, RW_WRITER); 763 764 if (nvf_list == NULL) { 765 rw_exit(&nvf_list_lock); 766 return (B_FALSE); 767 } 768 769 /* 770 * find the specified list 771 */ 772 rval = nvlist_lookup_nvlist(nvf_list, id, &list); 773 if (rval != 0) { 774 rw_exit(&nvf_list_lock); 775 return (B_FALSE); 776 } 777 778 /* 779 * remove the specified data element 780 */ 781 rval = nvlist_remove(list, name, DATA_TYPE_BYTE_ARRAY); 782 if (rval == 0) { 783 /* 784 * data was set, so update associated file 785 */ 786 nvf_update(); 787 } 788 789 rw_exit(&nvf_list_lock); 790 return (rval == 0 ? B_TRUE : B_FALSE); 791 } 792 793 /* 794 * +--------------------------------------------------------------------+ 795 * | Internal Helper Functions | 796 * +--------------------------------------------------------------------+ 797 */ 798 799 /* 800 * nvf_cksum - calculate checksum of given buffer. 801 * 802 * This function was derived from like function (nvp_cksum) in 803 * usr/src/uts/common/os/devctl.c 804 */ 805 static uint16_t 806 nvf_chksum(char *buf, int64_t buflen) 807 { 808 uint16_t cksum = 0; 809 uint16_t *p = (uint16_t *)buf; 810 int64_t n; 811 812 if ((buflen & 0x01) != 0) { 813 buflen--; 814 cksum = buf[buflen]; 815 } 816 n = buflen / 2; 817 while (n-- > 0) 818 cksum ^= *p++; 819 return (cksum); 820 } 821 822 823 /* 824 * nvf_thread - determines when writing of NVLIST/NVPAIR data to a file 825 * should occur. 826 */ 827 /* ARGSUSED */ 828 static void 829 nvf_thread(void *arg) 830 { 831 clock_t nticks; 832 boolean_t rval; 833 834 mutex_enter(&nvf_lock); 835 nticks = nvf_thread_ticks - ddi_get_lbolt(); 836 837 /* 838 * check whether its time to write to file. If not, reschedule self 839 */ 840 if (nticks > NVF_RESCHED_MIN_TICKS) { 841 if (NVF_IS_ACTIVE(nvf_flags)) { 842 mutex_exit(&nvf_lock); 843 nvf_thread_id = timeout(nvf_thread, NULL, nticks); 844 mutex_enter(&nvf_lock); 845 } 846 mutex_exit(&nvf_lock); 847 return; 848 } 849 850 /* 851 * flush NVLIST/NVPAIR data to file 852 */ 853 NVF_CLEAR_DIRTY(nvf_flags); 854 nvf_flags |= NVF_FLUSHING; 855 mutex_exit(&nvf_lock); 856 857 rval = nvf_flush(); 858 859 mutex_enter(&nvf_lock); 860 nvf_flags &= ~NVF_FLUSHING; 861 if (rval == B_FALSE) { 862 NVF_MARK_DIRTY(nvf_flags); 863 if ((nvf_flags & NVF_ERROR) == 0) { 864 if (nvf_written_once) { 865 cmn_err(CE_NOTE, 866 "!iscsi persistent store update " 867 "failed file:%s", nvf_curr_filename); 868 } 869 nvf_flags |= NVF_ERROR; 870 } 871 nvf_thread_ticks = NVF_FLUSH_BACKOFF_DELAY + ddi_get_lbolt(); 872 } else if (nvf_flags & NVF_ERROR) { 873 cmn_err(CE_NOTE, "!iscsi persistent store update ok now " 874 "filename:%s", nvf_curr_filename); 875 nvf_flags &= ~NVF_ERROR; 876 } 877 878 /* 879 * re-check whether data is dirty and reschedule if necessary 880 */ 881 if (NVF_IS_ACTIVE(nvf_flags) && NVF_IS_DIRTY(nvf_flags)) { 882 nticks = nvf_thread_ticks - ddi_get_lbolt(); 883 mutex_exit(&nvf_lock); 884 if (nticks > NVF_FLUSH_DELAY) { 885 nvf_thread_id = timeout(nvf_thread, NULL, nticks); 886 } else { 887 nvf_thread_id = timeout(nvf_thread, NULL, 888 NVF_FLUSH_DELAY); 889 } 890 } else { 891 NVF_CLEAR_SCHED(nvf_flags); 892 mutex_exit(&nvf_lock); 893 } 894 } 895 896 /* 897 * nvf_flush - write contents of NVLIST/NVPAIR to a backing file. 898 * 899 * This function is derived from a like NVPAIR/NVFILE implementation 900 * in usr/src/uts/common/os/devctl.c 901 */ 902 static boolean_t 903 nvf_flush(void) 904 { 905 int rval; 906 nvlist_t *tmpnvl; 907 char *nvfbuf; 908 char *nvlbuf; 909 size_t nvllen; 910 size_t nvflen; 911 int file; 912 int bytes_written; 913 914 /* 915 * duplicate data so access isn't blocked while writing to disk 916 */ 917 mutex_enter(&nvf_lock); 918 rval = nvlist_dup(nvf_list, &tmpnvl, KM_SLEEP); 919 if (rval != 0) { 920 cmn_err(CE_NOTE, "!iscsi persistent store failed to " 921 "duplicate nvf_list (%d)", rval); 922 mutex_exit(&nvf_lock); 923 return (B_FALSE); 924 } 925 mutex_exit(&nvf_lock); 926 927 /* 928 * pack duplicated list to get ready for file write 929 */ 930 nvlbuf = NULL; 931 rval = nvlist_pack(tmpnvl, &nvlbuf, &nvllen, NV_ENCODE_NATIVE, 0); 932 if (rval != 0) { 933 cmn_err(CE_NOTE, "!iscsi persistent store failed to pack " 934 "nvf_list (%d)", rval); 935 nvlist_free(tmpnvl); 936 return (B_FALSE); 937 } 938 939 /* 940 * allocate buffer to store both the header and the data. 941 */ 942 nvflen = nvllen + sizeof (nvf_hdr_t); 943 nvfbuf = kmem_zalloc(nvflen, KM_SLEEP); 944 945 /* 946 * fill buffer with contents of file header 947 */ 948 ((nvf_hdr_t *)nvfbuf)->nvfh_magic = NVF_HDR_MAGIC; 949 ((nvf_hdr_t *)nvfbuf)->nvfh_ver = NVF_HDR_VERSION; 950 ((nvf_hdr_t *)nvfbuf)->nvfh_size = nvllen; 951 ((nvf_hdr_t *)nvfbuf)->nvfh_datasum = nvf_chksum((char *)nvlbuf, 952 nvllen); 953 ((nvf_hdr_t *)nvfbuf)->nvfh_hdrsum = nvf_chksum((char *)nvfbuf, 954 sizeof (nvf_hdr_t)); 955 956 /* 957 * copy packed nvlist into buffer 958 */ 959 bcopy(nvlbuf, nvfbuf + sizeof (nvf_hdr_t), nvllen); 960 961 /* 962 * free memory used for packed nvlist 963 */ 964 nvlist_free(tmpnvl); 965 kmem_free(nvlbuf, nvllen); 966 967 /* 968 * To make it unlikely we suffer data loss, write 969 * data to the new temporary file. Once successful 970 * complete the transaction by renaming the new file 971 * to replace the previous. 972 */ 973 974 /* 975 * remove temporary file to ensure data content is written correctly 976 */ 977 rval = nvf_remove(NVF_TMP_FILENAME); 978 if (rval == -1) { 979 kmem_free(nvfbuf, nvflen); 980 return (B_FALSE); 981 } 982 983 /* 984 * create tempororary file 985 */ 986 file = nvf_open(NVF_TMP_FILENAME, O_RDWR | O_CREAT, 0600); 987 if (file == -1) { 988 mutex_enter(&nvf_lock); 989 if (nvf_written_once) { 990 cmn_err(CE_NOTE, 991 "!iscsi persistent store failed to create " 992 "%s (errno:%d)", NVF_TMP_FILENAME, nvf_errno); 993 } 994 mutex_exit(&nvf_lock); 995 kmem_free(nvfbuf, nvflen); 996 return (B_FALSE); 997 } 998 999 /* 1000 * write data to tempororary file 1001 */ 1002 bytes_written = nvf_write(file, nvfbuf, nvflen); 1003 kmem_free(nvfbuf, nvflen); 1004 if (bytes_written == -1) { 1005 cmn_err(CE_NOTE, "!iscsi persistent store failed to write " 1006 "%s (errno:%d)", NVF_TMP_FILENAME, nvf_errno); 1007 return (B_FALSE); 1008 } 1009 1010 if (bytes_written != nvflen) { 1011 cmn_err(CE_NOTE, "!iscsi persistent store failed to write " 1012 "%s (errno:%d)\n\tpartial write %d of %ld bytes\n", 1013 NVF_TMP_FILENAME, nvf_errno, bytes_written, nvflen); 1014 return (B_FALSE); 1015 } 1016 1017 /* 1018 * close tempororary file 1019 */ 1020 rval = nvf_close(file); 1021 if (rval == -1) { 1022 return (B_FALSE); 1023 } 1024 1025 mutex_enter(&nvf_lock); 1026 /* 1027 * File has been written. Set flag to allow the create and update 1028 * messages to be displayed in case of create or update failures. 1029 */ 1030 nvf_written_once = B_TRUE; 1031 1032 /* 1033 * rename current original file to previous original file 1034 */ 1035 rval = nvf_rename(nvf_curr_filename, nvf_prev_filename); 1036 if (rval == -1) { 1037 cmn_err(CE_NOTE, "!iscsi persistent store failed to " 1038 "rename %s (errno:%d)", nvf_curr_filename, nvf_errno); 1039 mutex_exit(&nvf_lock); 1040 return (B_FALSE); 1041 } 1042 1043 /* 1044 * rename temporary file to current original file 1045 */ 1046 rval = nvf_rename(NVF_TMP_FILENAME, nvf_curr_filename); 1047 if (rval == -1) { 1048 cmn_err(CE_NOTE, "!iscsi persistent store failed to " 1049 "rename %s (errno:%d)", NVF_TMP_FILENAME, nvf_errno); 1050 mutex_exit(&nvf_lock); 1051 return (B_FALSE); 1052 } 1053 1054 NVF_CLEAR_DIRTY(nvf_flags); 1055 1056 mutex_exit(&nvf_lock); 1057 return (B_TRUE); 1058 } 1059 1060 /* 1061 * nvf_parse - read contents of NVLIST/NVPAIR file. 1062 * 1063 * This function is derived from a like NVPAIR/NVFILE implementation 1064 * in usr/src/uts/common/os/devctl.c 1065 */ 1066 static boolean_t 1067 nvf_parse(char *filename) 1068 { 1069 int file; 1070 nvf_hdr_t hdr; 1071 int bytes_read; 1072 int rval; 1073 uint16_t chksum; 1074 uint16_t hdrsum; 1075 char *buf; 1076 char overfill; 1077 nvlist_t *nvl; 1078 nvlist_t *old_nvl; 1079 1080 1081 /* 1082 * open current file 1083 */ 1084 file = nvf_open(filename, O_RDONLY, 0600); 1085 if (file == -1) { 1086 return (B_FALSE); 1087 } 1088 1089 /* 1090 * read file header 1091 */ 1092 bytes_read = nvf_read(file, (char *)&hdr, sizeof (hdr)); 1093 if (bytes_read != sizeof (hdr)) { 1094 (void) nvf_close(file); 1095 return (B_FALSE); 1096 } 1097 1098 /* 1099 * calculate checksum over file header bytes 1100 */ 1101 chksum = hdr.nvfh_hdrsum; 1102 hdr.nvfh_hdrsum = 0; 1103 hdrsum = nvf_chksum((char *)&hdr, sizeof (hdr)); 1104 1105 /* 1106 * validate file header is as expected 1107 */ 1108 if ((hdr.nvfh_magic != NVF_HDR_MAGIC) || 1109 (hdr.nvfh_ver != NVF_HDR_VERSION) || 1110 (hdrsum != chksum)) { 1111 (void) nvf_close(file); 1112 if (hdrsum != chksum) { 1113 cmn_err(CE_NOTE, "!iscsi persistent store " 1114 "checksum error %s actual:0x%x expected:0x%x", 1115 filename, hdrsum, chksum); 1116 } 1117 cmn_err(CE_NOTE, "!iscsi persistent store %s has an " 1118 "incorrect header", filename); 1119 return (B_FALSE); 1120 } 1121 1122 ASSERT(hdr.nvfh_size >= 0); 1123 1124 /* 1125 * read expected remaining content of file 1126 */ 1127 buf = kmem_alloc(hdr.nvfh_size, KM_SLEEP); 1128 bytes_read = nvf_read(file, buf, hdr.nvfh_size); 1129 if (bytes_read != hdr.nvfh_size) { 1130 kmem_free(buf, hdr.nvfh_size); 1131 (void) nvf_close(file); 1132 if (bytes_read < 0) { 1133 cmn_err(CE_NOTE, "!iscsi persistent store failed " 1134 "to read %s bytes:%d", filename, bytes_read); 1135 } else { 1136 cmn_err(CE_NOTE, "!iscsi persistent store incomplete " 1137 "read %s bytes:%d/%lld", filename, 1138 bytes_read, (longlong_t)hdr.nvfh_size); 1139 } 1140 return (B_FALSE); 1141 } 1142 1143 /* 1144 * check whether file has anymore data. If so this is an error 1145 */ 1146 bytes_read = nvf_read(file, &overfill, 1); 1147 (void) nvf_close(file); 1148 if (bytes_read > 0) { 1149 kmem_free(buf, hdr.nvfh_size); 1150 cmn_err(CE_NOTE, "!iscsi persistent store file is larger " 1151 "than expected %s bytes:%lld", 1152 filename, (longlong_t)hdr.nvfh_size); 1153 return (B_FALSE); 1154 } 1155 1156 DTRACE_PROBE1(hdr, nvf_hdr_t *, &hdr); 1157 1158 /* 1159 * validate file data is as expected 1160 */ 1161 chksum = nvf_chksum(buf, hdr.nvfh_size); 1162 if (hdr.nvfh_datasum != chksum) { 1163 kmem_free(buf, hdr.nvfh_size); 1164 cmn_err(CE_NOTE, "!iscsi persistent store checksum error %s " 1165 "actual:0x%x expected:0x%x", filename, 1166 hdr.nvfh_datasum, chksum); 1167 return (B_FALSE); 1168 } 1169 1170 nvl = NULL; 1171 rval = nvlist_unpack(buf, hdr.nvfh_size, &nvl, 0); 1172 if (rval != 0) { 1173 kmem_free(buf, hdr.nvfh_size); 1174 cmn_err(CE_NOTE, "!iscsi persistent store failed unpacking " 1175 "nvlist %s (%d)", filename, rval); 1176 return (B_FALSE); 1177 } 1178 1179 kmem_free(buf, hdr.nvfh_size); 1180 1181 /* 1182 * activate nvlist 1183 */ 1184 rw_enter(&nvf_list_lock, RW_WRITER); 1185 old_nvl = nvf_list; 1186 nvf_list = nvl; 1187 rw_exit(&nvf_list_lock); 1188 1189 /* 1190 * free up old nvlist 1191 */ 1192 if (old_nvl) { 1193 nvlist_free(old_nvl); 1194 } 1195 1196 return (B_TRUE); 1197 } 1198 1199 /* 1200 * iscsid_getf -- given a file descriptor returns a file pointer 1201 */ 1202 static file_t * 1203 nvf_getf(int fdes) 1204 { 1205 file_t *fp = NULL; 1206 1207 mutex_enter(&nvf_getf_lock); 1208 if ((fdes >= 0) && (fdes < NVF_GETF)) { 1209 fp = nvf_fd[fdes]; 1210 if (fp != NULL) 1211 mutex_enter(&fp->f_tlock); 1212 } 1213 mutex_exit(&nvf_getf_lock); 1214 1215 return (fp); 1216 } 1217 1218 /* 1219 * nvf_releasef -- release lock on file pointer 1220 */ 1221 static void 1222 nvf_releasef(int fdes) 1223 { 1224 file_t *fp; 1225 1226 mutex_enter(&nvf_getf_lock); 1227 if ((fdes >= 0) && (fdes < NVF_GETF)) { 1228 fp = nvf_fd[fdes]; 1229 mutex_exit(&fp->f_tlock); 1230 } 1231 mutex_exit(&nvf_getf_lock); 1232 } 1233 1234 /* 1235 * nvf_setf -- stores the file pointer in an empty slot returning index 1236 */ 1237 static int 1238 nvf_setf(file_t *fp) 1239 { 1240 int i = -1; 1241 1242 mutex_enter(&nvf_getf_lock); 1243 for (i = 0; i < NVF_GETF; i++) { 1244 if (nvf_fd[i] == 0) { 1245 nvf_fd[i] = fp; 1246 break; 1247 } 1248 } 1249 mutex_exit(&nvf_getf_lock); 1250 return (i); 1251 } 1252 1253 /* 1254 * nvf_freef -- gets the file pointer based on index and releases memory. 1255 */ 1256 static void 1257 nvf_freef(int fdes) 1258 { 1259 file_t *fp; 1260 1261 mutex_enter(&nvf_getf_lock); 1262 if ((fdes >= 0) && (fdes < NVF_GETF)) { 1263 fp = nvf_fd[fdes]; 1264 unfalloc(fp); 1265 nvf_fd[fdes] = NULL; 1266 } 1267 mutex_exit(&nvf_getf_lock); 1268 } 1269 1270 /* 1271 * nvf_open -- acts like syscall open, but works for kernel 1272 * 1273 * Note: This works for regular files only. No umask is provided to 1274 * vn_open which means whatever mode is passed in will be used to 1275 * create a file. 1276 */ 1277 static int 1278 nvf_open(char *path, int flags, int mode) 1279 { 1280 file_t *fp = NULL; 1281 vnode_t *vp = NULL; 1282 int fdes = -1; 1283 int fflags; 1284 1285 /* 1286 * Need to convert from user mode flags to file system flags. 1287 * It's unfortunate that the kernel doesn't define a mask for 1288 * the read/write bits which would make this conversion easier. 1289 * Only O_RDONLY/O_WRONLY/O_RDWR are different than their FXXXXX 1290 * counterparts. If one was provided something like 1291 * fflags = ((flags & mask) + 1) | (flags & ~mask) 1292 * would work. But, that would only be true if the relationship 1293 * be O_XXX and FXXX was defined and it's not. So we have the 1294 * following. 1295 */ 1296 if (flags & O_WRONLY) 1297 fflags = FWRITE; 1298 else if (flags & O_RDWR) 1299 fflags = FWRITE | FREAD; 1300 else 1301 fflags = FREAD; 1302 1303 /* 1304 * Now that fflags has been initialized with the read/write bits 1305 * look at the other flags and OR them in. 1306 */ 1307 if (flags & O_CREAT) 1308 fflags |= FCREAT; 1309 if (flags & O_TRUNC) 1310 fflags |= FTRUNC; 1311 1312 if (nvf_errno = vn_open(path, UIO_SYSSPACE, fflags, 1313 mode & MODEMASK, &vp, CRCREAT, 0)) { 1314 return (-1); 1315 } 1316 1317 if (falloc(vp, fflags, &fp, NULL) != 0) { 1318 VN_RELE(vp); 1319 return (-1); 1320 } 1321 /* ---- falloc returns with f_tlock held on success ---- */ 1322 mutex_exit(&fp->f_tlock); 1323 1324 if ((fdes = nvf_setf(fp)) == -1) { 1325 VN_RELE(vp); 1326 } 1327 return (fdes); 1328 } 1329 1330 /* 1331 * nvf_close -- closes down the file by releasing locks and memory. 1332 */ 1333 static int 1334 nvf_close(int fdes) 1335 { 1336 file_t *fp; 1337 vnode_t *vp; 1338 1339 if ((fp = nvf_getf(fdes)) == NULL) 1340 return (-1); 1341 vp = fp->f_vnode; 1342 1343 (void) VOP_CLOSE(vp, fp->f_flag, 1, 0, kcred, NULL); 1344 VN_RELE(vp); 1345 /* 1346 * unfalloc which is called from here will do a mutex_exit 1347 * on t_lock in the fp. So don't call nvf_releasef() here. 1348 */ 1349 nvf_freef(fdes); 1350 1351 return (0); 1352 } 1353 1354 /* 1355 * nvf_remove -- remove file from filesystem 1356 */ 1357 static int 1358 nvf_remove(char *filename) 1359 { 1360 return (vn_remove(filename, UIO_SYSSPACE, RMFILE)); 1361 } 1362 1363 /* 1364 * nvf_rename -- rename file from one name to another 1365 */ 1366 static int 1367 nvf_rename(char *oldname, char *newname) 1368 { 1369 return (vn_rename(oldname, newname, UIO_SYSSPACE)); 1370 } 1371 1372 /* 1373 * nvf_rw -- common read/write code. Very simplistic. 1374 */ 1375 static ssize_t 1376 nvf_rw(int fdes, void *cbuf, ssize_t count, enum uio_rw rw) 1377 { 1378 file_t *fp; 1379 vnode_t *vp; 1380 ssize_t resid = 0; 1381 1382 if ((fp = nvf_getf(fdes)) == NULL) 1383 return (-1); 1384 vp = fp->f_vnode; 1385 1386 if (nvf_errno = vn_rdwr(rw, vp, (caddr_t)cbuf, count, fp->f_offset, 1387 UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, &resid)) { 1388 nvf_releasef(fdes); 1389 return (-1); 1390 } 1391 1392 if ((count - resid) > 0) 1393 fp->f_offset += count; 1394 1395 nvf_releasef(fdes); 1396 return (count - resid); 1397 } 1398 1399 /* 1400 * nvf_write -- kernel write function 1401 */ 1402 static ssize_t 1403 nvf_write(int fdes, void *cbuf, ssize_t count) 1404 { 1405 return (nvf_rw(fdes, cbuf, count, UIO_WRITE)); 1406 } 1407 1408 /* 1409 * nvf_read -- kernel read function 1410 */ 1411 static ssize_t 1412 nvf_read(int fdes, void *cbuf, ssize_t count) 1413 { 1414 return (nvf_rw(fdes, cbuf, count, UIO_READ)); 1415 } 1416