xref: /titanic_41/usr/src/cmd/lvm/metassist/layout/layout_device_cache.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <assert.h>
30 #include <libintl.h>
31 
32 #include <string.h>
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <errno.h>
36 #include <search.h>
37 
38 #include "volume_dlist.h"
39 #include "volume_error.h"
40 #include "volume_output.h"
41 
42 #include "layout_device_cache.h"
43 #include "layout_dlist_util.h"
44 #include "layout_request.h"
45 
46 /*
47  * Implementation note:
48  * The current caches are implemented as linked lists of data
49  * structures described below. Cached object lookup uses hsearch()
50  * where possible to minimize the inefficiency of linear search.
51  */
52 
53 /*
54  * The name and attribute maps use hesarch() for faster lookup
55  */
56 static const uint32_t	MAX_CACHED_OBJECTS = 50000;
57 
58 /*
59  * The attribute cache is maintained as a list of these
60  * structs which map a device name to attributes.  The
61  * device name is the unique device name returned from
62  * the device library, typically a devfs path.  It should
63  * not be confused with the "display" name of the device
64  * which is typically a CTD or DID name.
65  */
66 typedef struct {
67 	char		*name;
68 	nvlist_t	*attrs;
69 } attr_cache_t;
70 
71 static dlist_t	*_attr_cache = NULL;
72 
73 /*
74  * The name cache is maintained via a list of these structs
75  * which map a descriptor to its name.
76  * The descriptor is saved as a string for hsearch()
77  */
78 typedef struct {
79 	char		*desc;
80 	char		*name;
81 } name_cache_t;
82 static dlist_t *_name_cache = NULL;
83 
84 /*
85  * The desc cache is maintained as a list of these
86  * structs which map a device display name (CTD or DID)
87  * or alias to a descriptor.
88  */
89 typedef struct {
90 	char		*name;
91 	dm_descriptor_t desc;
92 } desc_cache_t;
93 
94 static dlist_t	*_desc_cache = NULL;
95 
96 /*
97  * Since each of the lookup caches shares the same hsearch()
98  * hash table, the names used as lookup keys for the desc_cache_t
99  * and attr_cache_t may cause collisions.
100  *
101  * The desc_cache_t map alters the device name by prepending
102  * this string to avoid collisions.
103  */
104 static const char *DESC_CACHE_KEY_PREFIX = "desc_cache";
105 
106 /*
107  * The set of descriptors to be returned to libdiskmgt is
108  * maintained via a list of dm_descriptor_t handles.
109  * descriptors are added by new_descriptor() and
110  * cache_descriptor_to_free().
111  */
112 typedef struct {
113 	dm_descriptor_t desc;
114 	boolean_t	virtual;
115 } desc_free_t;
116 static dlist_t	*_desc_to_free = NULL;
117 
118 static char	*find_cached_name(dm_descriptor_t desc);
119 static nvlist_t *find_cached_attrs(char *name);
120 
121 static int	add_descriptor_to_free(dm_descriptor_t desc);
122 
123 static void	release_name_cache();
124 static void	release_desc_to_free_cache();
125 static void	release_attribute_cache();
126 static void	release_descriptor_cache();
127 
128 static uint32_t interal_name_count = 0;
129 
130 /*
131  * FUNCTION:	create_device_caches()
132  *
133  * PURPOSE:	Helper which initializes the module's private data
134  *		structures.
135  */
136 int
create_device_caches()137 create_device_caches()
138 {
139 	if (hcreate(MAX_CACHED_OBJECTS) == 0) {
140 	    return (ENOMEM);
141 	}
142 
143 	return (0);
144 }
145 
146 /*
147  * FUNCTION:	release_device_caches()
148  *
149  * PURPOSE:	Helper which cleans up memory allocated to the module's
150  *		private data structures.
151  */
152 int
release_device_caches()153 release_device_caches()
154 {
155 	release_name_cache();
156 	release_desc_to_free_cache();
157 	release_attribute_cache();
158 	release_descriptor_cache();
159 
160 	return (0);
161 }
162 
163 /*
164  * FUNCTION:	free_desc_cache_object(void *obj)
165  *
166  * INPUT:	obj	- opaque pointer
167  *
168  * PURPOSE:	Frees memory associated with an entry in the
169  *		desc cache.
170  *
171  *		Assumes that the input object is a pointer
172  *		to a desc_cache_t struct.
173  */
174 static void
free_desc_cache_object(void * obj)175 free_desc_cache_object(
176 	void	*obj)
177 {
178 	if (obj == NULL) {
179 	    return;
180 	}
181 
182 	free(((desc_cache_t *)obj)->name);
183 	free(obj);
184 }
185 /*
186  * FUNCTION:	release_descriptor_cache()
187  *
188  * RETURNS:	int	- 0 on success
189  *			 !0 otherwise
190  *
191  * PURPOSE:	Frees all entries in the name cache.
192  */
193 static void
release_descriptor_cache()194 release_descriptor_cache()
195 {
196 	oprintf(OUTPUT_DEBUG,
197 		gettext("  destroying descriptor cache (%d items)\n"),
198 		dlist_length(_desc_cache));
199 
200 	dlist_free_items(_desc_cache, free_desc_cache_object);
201 	_desc_cache = NULL;
202 }
203 
204 /*
205  * FUNCTION:	add_cached_descriptor(char *name, dm_descriptor_t desc)
206  *
207  * INPUT:	name	- a device name
208  *		desc	- a dm_descriptor_t handle
209  *
210  * RETURNS:	int	- 0 on success
211  *			 !0 otherwise
212  *
213  * PURPOSE:	Adds an entry to the descriptor cache using the input
214  *		descriptor and name.
215  *
216  * 		Note that all of the lookup caches shares the same hsearch()
217  *		hash table and that the names used as lookup keys for the
218  *		desc_cache_t and attr_cache_t cause collisions.
219  *
220  *		The desc_cache_t map alters the device name to avoid collisions.
221  */
222 int
add_cached_descriptor(char * name,dm_descriptor_t desc)223 add_cached_descriptor(
224 	char		*name,
225 	dm_descriptor_t	desc)
226 {
227 	desc_cache_t	*dcp;
228 	char		buf[MAXNAMELEN+1];
229 	dlist_t		*item;
230 	ENTRY		entry;
231 
232 	if ((dcp = (desc_cache_t *)
233 	    calloc(1, sizeof (desc_cache_t))) == NULL) {
234 	    return (ENOMEM);
235 	}
236 
237 	dcp->desc = desc;
238 
239 	(void) snprintf(buf, MAXNAMELEN, "%s-%s", DESC_CACHE_KEY_PREFIX, name);
240 	dcp->name = strdup(buf);
241 	if (dcp->name == NULL) {
242 	    free(dcp);
243 	    return (ENOMEM);
244 	}
245 
246 	/*
247 	 * insert into the hashtable... ignore the return from hsearch(),
248 	 * there is no existing entry corresponding to desc since the
249 	 * map was already searched just before this function is called,
250 	 * see get_name() below
251 	 */
252 	entry.key  = dcp->name;
253 	entry.data = (void *)dcp;
254 	(void) hsearch(entry, ENTER);
255 
256 	/* insert into the list cache... */
257 	if ((item = dlist_new_item((void *)dcp)) == NULL) {
258 	    free(dcp);
259 	    return (ENOMEM);
260 	}
261 
262 	_desc_cache = dlist_append(item, _desc_cache, AT_HEAD);
263 
264 	return (0);
265 }
266 
267 /*
268  * FUNCTION:	dm_descriptor_t find_cached_descriptor(char *name)
269  *
270  * INPUT:	char * - pointer to a name or alias.
271  *
272  * RETURNS:	dm_descriptor_t - dm_descriptor_t handle cached under the
273  *			input name if a match is found.  A null descriptor
274  *			is returned if no match is found.
275  *
276  * PURPOSE:	Searches for the desc that has been cached for
277  *		the input device name.
278  *
279  * 		Note that all of the lookup caches shares the same hsearch()
280  *		hash table and that the names used as lookup keys for the
281  *		desc_cache_t and attr_cache_t cause collisions.
282  *
283  *		The desc_cache_t map alters the device name to avoid collisions.
284  */
285 dm_descriptor_t
find_cached_descriptor(char * name)286 find_cached_descriptor(
287 	char		*name)
288 {
289 	ENTRY		item;
290 	ENTRY		*cached_item = NULL;
291 	char		buf[MAXNAMELEN+1];
292 	dm_descriptor_t	desc = (dm_descriptor_t)0;
293 
294 	(void) snprintf(buf, MAXNAMELEN, "%s-%s", DESC_CACHE_KEY_PREFIX, name);
295 	item.key = buf;
296 
297 	/* get descriptor associated with this name */
298 	if ((cached_item = hsearch(item, FIND)) != NULL) {
299 	    /* LINTED */
300 	    desc = ((desc_cache_t *)cached_item->data)->desc;
301 	}
302 
303 	return (desc);
304 }
305 
306 /*
307  * FUNCTION:	free_name_cache_object(void *obj)
308  *
309  * INPUT:	obj	- opaque pointer
310  *
311  * PURPOSE:	Frees memory associated with an entry in the
312  *		name cache.
313  *
314  *		Assumes that the input object is a pointer
315  *		to a name_cache_t struct.
316  */
317 static void
free_name_cache_object(void * obj)318 free_name_cache_object(
319 	void	*obj)
320 {
321 	if (obj == NULL) {
322 	    return;
323 	}
324 
325 	free(((name_cache_t *)obj)->desc);
326 	free(((name_cache_t *)obj)->name);
327 	free(obj);
328 }
329 
330 /*
331  * FUNCTION:	release_name_cache()
332  *
333  * RETURNS:	int	- 0 on success
334  *			 !0 otherwise
335  *
336  * PURPOSE:	Frees all entries in the name cache.
337  */
338 static void
release_name_cache()339 release_name_cache()
340 {
341 	oprintf(OUTPUT_DEBUG,
342 		gettext("  destroying name cache (%d items)\n"),
343 		dlist_length(_name_cache));
344 
345 	dlist_free_items(_name_cache, free_name_cache_object);
346 	_name_cache = NULL;
347 }
348 
349 /*
350  * FUNCTION:	add_cached_name(dm_descriptor_t desc, char *name)
351  *
352  * INPUT:	desc	- a dm_descriptor_t handle
353  *		name	- a device name
354  *
355  * RETURNS:	int	- 0 on success
356  *			 !0 otherwise
357  *
358  * PURPOSE:	Adds an entry to the name cache using the input
359  *		descriptor and name.
360  */
361 int
add_cached_name(dm_descriptor_t desc,char * name)362 add_cached_name(
363 	dm_descriptor_t	desc,
364 	char		*name)
365 {
366 	name_cache_t	*ncp;
367 	char		buf[MAXNAMELEN+1];
368 	dlist_t		*item;
369 	ENTRY		entry;
370 
371 	if ((ncp = (name_cache_t *)
372 	    calloc(1, sizeof (name_cache_t))) == NULL) {
373 	    return (ENOMEM);
374 	}
375 
376 	(void) snprintf(buf, MAXNAMELEN, "%llu", desc);
377 	ncp->desc = strdup(buf);
378 	if (ncp->desc == NULL) {
379 	    free(ncp);
380 	    return (ENOMEM);
381 	}
382 
383 	ncp->name = strdup(name);
384 	if (ncp->name == NULL) {
385 	    free(ncp->desc);
386 	    free(ncp);
387 	    return (ENOMEM);
388 	}
389 
390 	/*
391 	 * insert into the hashtable... ignore the return from hsearch(),
392 	 * there is no existing entry corresponding to desc since the
393 	 * map was already searched just before this function is called,
394 	 * see get_name() below
395 	 */
396 	entry.key  = ncp->desc;
397 	entry.data = (void *)ncp;
398 	(void) hsearch(entry, ENTER);
399 
400 	/* insert into the list cache... */
401 	if ((item = dlist_new_item((void *)ncp)) == NULL) {
402 	    free(ncp->desc);
403 	    free(ncp->name);
404 	    free(ncp);
405 	    return (ENOMEM);
406 	}
407 
408 	_name_cache = dlist_append(item, _name_cache, AT_HEAD);
409 
410 	return (0);
411 }
412 
413 /*
414  * FUNCTION:	char *find_cached_name(dm_descriptor_t desc)
415  *
416  * INPUT:	desc	- a dm_descriptor_t handle
417  *
418  * RETURNS:	char * - pointer to the name cached for the descriptor.
419  *			 Null otherwise.
420  *
421  * PURPOSE:	Searches for the name that has been cached for
422  *		the input dm_descriptor_t.
423  *
424  *		Search linked list.
425  */
426 static char *
find_cached_name(dm_descriptor_t desc)427 find_cached_name(
428 	dm_descriptor_t	desc)
429 {
430 	char		buf[MAXNAMELEN+1];
431 	ENTRY		item;
432 	ENTRY		*cached_item = NULL;
433 	char		*name = NULL;
434 
435 	(void) snprintf(buf, MAXNAMELEN, "%llu", desc);
436 	item.key = buf;
437 
438 	/* get name associated with this descriptor */
439 	if ((cached_item = hsearch(item, FIND)) != NULL) {
440 	    /* LINTED */
441 	    name = ((name_cache_t *)cached_item->data)->name;
442 	}
443 
444 	return (name);
445 }
446 
447 /*
448  * FUNCTION:	get_name(dm_descriptor_t desc,
449  *			char_t **name)
450  *
451  * INPUT:	desc	- a dm_descriptor_t handle
452  *
453  * OUTPUT:	name	- pointer to char * to hold the name
454  *
455  * RETURNS:	int	- 0 on success
456  *			 !0 otherwise
457  *
458  * PURPOSE:	Searches for the name that has been cached for the
459  *		input dm_descriptor_t.
460  *
461  *		Names are cached using the dm_descriptor.
462  *		If no name has yet been cached, it is retrieved from
463  *		libdiskmgt and added to the cache.
464  *
465  *		Names are cached so that all name strings obtained from
466  *		libdiskmgt will get properly released when layout completes.
467  */
468 int
get_name(dm_descriptor_t desc,char ** name)469 get_name(
470 	dm_descriptor_t	desc,
471 	char		**name)
472 {
473 
474 	int		dm_free = 1;
475 	int		error = 0;
476 
477 	if ((desc != (dm_descriptor_t)0) &&
478 	    (*name = find_cached_name(desc)) == NULL) {
479 
480 	    /* not in descriptor->name cache/map, add it */
481 
482 	    if (is_virtual_slice(desc) != B_TRUE) {
483 
484 		dm_desc_type_t	type;
485 
486 		*name = dm_get_name(desc, &error);
487 		if (error != 0) {
488 		    volume_set_error(
489 			    gettext("failed to get name for descriptor: %d\n"),
490 			    error);
491 		    return (-1);
492 		}
493 
494 		/*
495 		 * some devices can be unnamed...
496 		 * assign a unique internal name if necessary
497 		 */
498 		if (*name == NULL) {
499 		    char buf[MAXNAMELEN];
500 
501 		    dm_free = 0;
502 		    (void) snprintf(buf, MAXNAMELEN-1, "temp-name-%lu",
503 			    interal_name_count++);
504 		    *name = strdup(buf);
505 		    if (*name == NULL) {
506 			volume_set_error(
507 			    gettext("failed to get name for descriptor: %d\n"),
508 			    errno);
509 			return (-1);
510 		    }
511 		    oprintf(OUTPUT_DEBUG,
512 			    gettext("unnamed descriptor %llu assigned %s\n"),
513 			    desc, *name);
514 		}
515 
516 		/*
517 		 * media can have the same name as the associated drive
518 		 * which hoses the attribute caching scheme, so unique-ify
519 		 */
520 		if ((type = dm_get_type(desc)) == DM_MEDIA) {
521 		    char buf[MAXNAMELEN];
522 		    (void) snprintf(buf, MAXNAMELEN-1, "%s-%d", *name, type);
523 		    error = add_cached_name(desc, buf);
524 		} else {
525 		    error = add_cached_name(desc, *name);
526 		}
527 		if (dm_free)
528 		    dm_free_name(*name);
529 		else
530 		    free(*name);
531 
532 		if (error == 0) {
533 		    /* return copied name */
534 		    *name = find_cached_name(desc);
535 		} else {
536 		    *name = NULL;
537 		}
538 	    }
539 	}
540 
541 	return (error);
542 }
543 
544 /*
545  * FUNCTION:	free_attr_cache_object(void *obj)
546  *
547  * INPUT:	obj	- opaque pointer
548  *
549  * PURPOSE:	Frees memory associated with an entry in the
550  *		attribute cache.
551  *
552  *		Assumes that the input object is a pointer
553  *		to a attr_cache_t struct.
554  */
555 static void
free_attr_cache_object(void * obj)556 free_attr_cache_object(
557 	void		*obj)
558 {
559 	if (obj == NULL) {
560 	    return;
561 	}
562 
563 	nvlist_free(((attr_cache_t *)obj)->attrs);
564 	free(obj);
565 }
566 
567 /*
568  * FUNCTION:	release_attribute_cache()
569  *
570  * RETURNS:	int	- 0 on success
571  *			 !0 otherwise
572  *
573  * PURPOSE:	Frees all entries in the attribute cache.
574  */
575 void
release_attribute_cache()576 release_attribute_cache()
577 {
578 	oprintf(OUTPUT_DEBUG,
579 		gettext("  destroying attribute cache (%d items)\n"),
580 		dlist_length(_attr_cache));
581 
582 	dlist_free_items(_attr_cache, free_attr_cache_object);
583 	_attr_cache = NULL;
584 
585 	/* cleanup attribute cache lookup hashtable */
586 	hdestroy();
587 }
588 
589 /*
590  * FUNCTION:	add_cached_attributes(char *name, nvlist_t *attrs)
591  *
592  * INPUT:	name	- a device name
593  *		attrs	- pointer to an nvlist_t attribute structure
594  *
595  * RETURNS:	int	- 0 on success
596  *			 !0 otherwise
597  *
598  * PURPOSE:	Adds an entry to the attribute cache using the input
599  *		name and attributes.
600  *
601  *		Uses a linked list to cache attributes.
602  *		Keeps a parallel hash table for faster lookup.
603  */
604 int
add_cached_attributes(char * name,nvlist_t * attrs)605 add_cached_attributes(
606 	char		*name,
607 	nvlist_t 	*attrs)
608 {
609 	attr_cache_t	*acp = NULL;
610 	dlist_t		*item = NULL;
611 	ENTRY		*exist = NULL;
612 	ENTRY		entry;
613 
614 	/* insert into the hashtable... */
615 	entry.key  = name;
616 	entry.data = (void *)attrs;
617 
618 	if ((exist = hsearch(entry, ENTER)) != NULL) {
619 	    /* replace the existing attrs entry */
620 	    exist->data = (void *)attrs;
621 	}
622 
623 	if ((acp = (attr_cache_t *)calloc(1, sizeof (attr_cache_t))) == NULL) {
624 	    return (ENOMEM);
625 	}
626 
627 	acp->name = name;
628 	acp->attrs = attrs;
629 
630 	/* and cache of attr structs to be freed */
631 	if ((item = dlist_new_item((void *)acp)) == NULL) {
632 	    free(acp);
633 	    return (ENOMEM);
634 	}
635 
636 	_attr_cache = dlist_append(item, _attr_cache, AT_HEAD);
637 
638 	return (0);
639 }
640 
641 /*
642  * FUNCTION:	nvlist_t *find_cached_attrs(char *name)
643  *
644  * INPUT:	name	- a device name
645  *
646  * RETURNS:	nvlist_t * - pointer to an nvlist_t attribute structure
647  *			cached under 'name'.  Null otherwise.
648  *
649  * PURPOSE:	Searches for the nvlist attributes that have been
650  *		cached for the input name.
651  */
652 static nvlist_t *
find_cached_attrs(char * name)653 find_cached_attrs(
654 	char		*name)
655 {
656 	ENTRY		item;
657 	ENTRY		*cached_item = NULL;
658 	nvlist_t	*attrs = NULL;
659 
660 	item.key = name;
661 
662 	/* get attributes cached under this name */
663 	if ((cached_item = hsearch(item, FIND)) != NULL) {
664 	    /* LINTED */
665 	    attrs = (nvlist_t *)cached_item->data;
666 	}
667 
668 	return (attrs);
669 }
670 
671 /*
672  * FUNCTION:	get_cached_attributes(dm_descriptor_t desc,
673  *			nvlist_t **attrs)
674  *
675  * INPUT:	desc	- a dm_descriptor_t handle
676  *
677  * OUTPUT:	attrs	- pointer to an nvlist_t attribute structure
678  *
679  * RETURNS:	int	- 0 on success
680  *			 !0 otherwise
681  *
682  * PURPOSE:	Searches for the nvlist attributes that have been
683  *		cached for the input dm_descriptor_t.
684  *
685  *		Attributes are cached using the name associated with
686  *		the descriptor.  If no attributes have yet been cached
687  *		they are retrieved from libdiskmgt and added to the
688  *		cache.
689  *
690  *		Attributes are cached so that layout may store transient
691  *		data relevant to the layout process.
692  */
693 int
get_cached_attributes(dm_descriptor_t desc,nvlist_t ** attrs)694 get_cached_attributes(
695 	dm_descriptor_t	desc,
696 	nvlist_t 	**attrs)
697 {
698 	int		error = 0;
699 	char		*name = NULL;
700 
701 	if ((desc != (dm_descriptor_t)0) &&
702 	    (error = get_name(desc, &name)) == 0) {
703 
704 	    if ((*attrs = find_cached_attrs(name)) == NULL) {
705 		/* get attrs and cache them */
706 		*attrs = dm_get_attributes(desc, &error);
707 		if (error == 0) {
708 		    error = add_cached_attributes(name, *attrs);
709 		}
710 	    }
711 	}
712 
713 	return (error);
714 }
715 
716 /*
717  * FUNCTION:	new_descriptor(dm_descriptor_t *desc)
718  *
719  * INPUT:	desc	- a pointer to a dm_descriptor_t to hold
720  *				the result.
721  *
722  * RETURNS:	int	- 0 on success
723  *			 !0 otherwise
724  *
725  * PURPOSE:	Allocates a new dm_descriptor_t handle.
726  *
727  *		This is necessary because the process may have to
728  *		create "virtual" objects to represent devices that
729  *		do not yet exist on the system and hence are unknown
730  *		to libdiskmgt and diskmgtd.
731  *
732  *		A unique handle is created for such objects and may
733  *		be used by layout to access the virtual devices as
734  *		if they were obtained from libdiskmgt.
735  */
736 int
new_descriptor(dm_descriptor_t * desc)737 new_descriptor(
738 	dm_descriptor_t	*desc)
739 {
740 	desc_free_t	*dfp;
741 	dlist_t		*item;
742 
743 	*desc = NULL;
744 
745 	if ((dfp = (desc_free_t *)
746 	    calloc(1, sizeof (desc_free_t))) == NULL) {
747 	    return (ENOMEM);
748 	}
749 
750 	dfp->desc = (uintptr_t)dfp;
751 	dfp->virtual = B_TRUE;
752 
753 	if ((item = dlist_new_item((void *)dfp)) == NULL) {
754 	    free(dfp);
755 	    return (ENOMEM);
756 	}
757 
758 	_desc_to_free = dlist_append(item, _desc_to_free, AT_HEAD);
759 
760 	*desc = (uintptr_t)dfp;
761 
762 	return (0);
763 }
764 
765 /*
766  * FUNCTION:	add_descriptors_to_free(dm_descriptor_t *desc)
767  *
768  * INPUT:	desc	- an array of dm_descriptor_t handles from
769  *				libdiskmgt
770  *
771  * RETURNS:	int	- 0 on success
772  *			 !0 otherwise
773  *
774  * PURPOSE:	Function which accepts an array of dm_descriptor_t handles
775  *		that need to be returned to libdiskmgt.
776  *
777  *		The array is iterated and each handle is passed to
778  *		add_descriptor_to_free.
779  */
780 int
add_descriptors_to_free(dm_descriptor_t * desc_list)781 add_descriptors_to_free(
782 	dm_descriptor_t *desc_list)
783 {
784 	int i = 0;
785 
786 	if (desc_list != NULL) {
787 	    for (i = 0; desc_list[i] != NULL; i++) {
788 		(void) add_descriptor_to_free(desc_list[i]);
789 	    }
790 	}
791 
792 	return (0);
793 }
794 
795 /*
796  * FUNCTION:	add_descriptor_to_free(dm_descriptor_t desc)
797  *
798  * INPUT:	desc	- dm_descriptor_t handle from libdiskmgt
799  *
800  * RETURNS:	int	- 0 on success
801  *			 !0 otherwise
802  *
803  * PURPOSE:	Remembers a dm_descriptor_t handle which needs to be
804  *		returned to libdiskmgt. These handles represent memory
805  *		allocated by the the diskmgtd and must be returned in
806  *		order for that memory to be released.
807  *
808  *		The handles are cached for the duration of layout
809  *	        processing so that layout is guaranteed to have
810  *		unique handles for all objects received from
811  *		libdiskmgt.
812  *
813  *		The caching is accomplished by adding the handle to
814  *		a list of desc_free_t structs.
815  */
816 static int
add_descriptor_to_free(dm_descriptor_t desc)817 add_descriptor_to_free(
818 	dm_descriptor_t desc)
819 {
820 	desc_free_t	*dfp = NULL;
821 	dlist_t		*item = NULL;
822 
823 	if (desc == (dm_descriptor_t)0) {
824 	    return (0);
825 	}
826 
827 	if (is_virtual_slice(desc) == B_TRUE) {
828 	    /* don't return virtual slice descriptors to libdiskmgt */
829 	    return (0);
830 	}
831 
832 	if ((dfp = calloc(1, sizeof (desc_free_t))) == NULL) {
833 	    return (ENOMEM);
834 	}
835 
836 	dfp->desc = desc;
837 	dfp->virtual = B_FALSE;
838 
839 	if ((item = dlist_new_item((void *)dfp)) == NULL) {
840 	    free(dfp);
841 	    return (ENOMEM);
842 	}
843 
844 	_desc_to_free = dlist_append(item, _desc_to_free, AT_HEAD);
845 
846 	return (0);
847 }
848 
849 /*
850  * FUNCTION:	release_desc_to_free_cache()
851  *
852  * PURPOSE:	Frees all entries in the desc_to_free cache.
853  *
854  *		Iterates the _desc_to_free list and builds an
855  *		array with all dm_descriptor_t handles that were
856  *		obtained from libdiskmgt.  Passing this array to
857  *		dm_free_descriptors() is faster than calling
858  *		dm_free_descriptor() to free individual	handles.
859  */
860 void
release_desc_to_free_cache()861 release_desc_to_free_cache()
862 {
863 	dlist_t *iter;
864 	dm_descriptor_t *array;
865 	int i = 0;
866 
867 	oprintf(OUTPUT_DEBUG,
868 		gettext("  destroying desc_to_free cache (%d items)\n"),
869 		dlist_length(_desc_to_free));
870 
871 	array = (dm_descriptor_t *)calloc(
872 		dlist_length(_desc_to_free) + 1, sizeof (dm_descriptor_t));
873 
874 	if (array != NULL) {
875 	    for (iter = _desc_to_free; iter != NULL; iter = iter->next) {
876 		desc_free_t *dfp = (desc_free_t *)iter->obj;
877 		if (dfp->virtual == B_FALSE) {
878 		    array[i++] = dfp->desc;
879 		}
880 	    }
881 	    array[i] = (dm_descriptor_t)0;
882 	    dm_free_descriptors(array);
883 	}
884 
885 	/*
886 	 * If the calloc failed, the descriptors aren't explicitly freed,
887 	 * but the libdiskmgt daemon will eventually reclaim them after
888 	 * a period of inactivity.
889 	 */
890 	dlist_free_items(_desc_to_free, free);
891 
892 	_desc_to_free = NULL;
893 }
894