xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/iscsi/nvfile.c (revision 64d1d4ab72834b7483c7962efc738b568ca8792e)
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