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