xref: /titanic_41/usr/src/lib/libdiskmgt/common/inuse_svm.c (revision 82629e3015252bf18319ba3815c773df23e21436)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Creates and maintains a cache of slices used by SVM.
30  */
31 
32 #include <meta.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <libintl.h>
37 #include <synch.h>
38 #include <thread.h>
39 #include <dlfcn.h>
40 #include <link.h>
41 #include <libsysevent.h>
42 #include <syslog.h>
43 #include <sys/types.h>
44 #include <sys/sysevent/eventdefs.h>
45 
46 #include "libdiskmgt.h"
47 #include "disks_private.h"
48 
49 /*
50  * The list of SVM slices in use.
51  */
52 
53 struct svm_list {
54 	struct svm_list	*next;
55 	char		*slice;
56 	char		*name;
57 	char		*type;
58 };
59 
60 static struct svm_list	*svm_listp = NULL;
61 static rwlock_t		svm_lock = DEFAULTRWLOCK;
62 static int		initialized = 0;
63 static mutex_t		init_lock = DEFAULTMUTEX;
64 
65 static int	add_use_record(char *devname, char *type, char *mname);
66 static int	diskset_info(mdsetname_t *sp);
67 static int	drive_in_diskset(char *dpath, char *setname);
68 static void	event_handler();
69 static void	free_names(mdnamelist_t *nlp);
70 static void	free_svm(struct svm_list *listp);
71 static int	init_svm();
72 static int	load_svm();
73 static int	new_entry(char *sname, char *type, char *mname,
74 			    mdsetname_t *sp);
75 
76 /*
77  * Pointers to libmeta functions that we dynamically resolve.
78  */
79 static set_t		(*mdl_get_max_sets)(md_error_t *ep);
80 static void		(*mdl_mdclrerror)(md_error_t *ep);
81 static md_error_t	*mdl_mdnullerror;
82 static void		(*mdl_metaflushnames)(int flush_sr_cache);
83 static void		(*mdl_metaflushsetname)(mdsetname_t *sp);
84 static void		(*mdl_metafreenamelist)(mdnamelist_t *nlp);
85 static void		(*mdl_metafreereplicalist)(md_replicalist_t *rlp);
86 static md_drive_desc	*(*mdl_metaget_drivedesc)(mdsetname_t *sp, int flags,
87 			    md_error_t *ep);
88 static mdname_t		*(*mdl_metaname)(mdsetname_t **spp, char *uname,
89 			    meta_device_type_t uname_type, md_error_t *ep);
90 static int		(*mdl_metareplicalist)(mdsetname_t *sp, int flags,
91 			    md_replicalist_t **rlpp, md_error_t *ep);
92 static mdsetname_t	*(*mdl_metasetnosetname)(set_t setno, md_error_t *ep);
93 static int		(*mdl_meta_get_hotspare_names)(mdsetname_t *sp,
94 			    mdnamelist_t **nlpp, int options, md_error_t *ep);
95 static md_raid_t	*(*mdl_meta_get_raid)(mdsetname_t *sp, mdname_t *raidnp,
96 			    md_error_t *ep);
97 static int		(*mdl_meta_get_raid_names)(mdsetname_t *sp,
98 			    mdnamelist_t **nlpp, int options, md_error_t *ep);
99 static md_sp_t		*(*mdl_meta_get_sp)(mdsetname_t *sp, mdname_t *np,
100 			    md_error_t *ep);
101 static int		(*mdl_meta_get_sp_names)(mdsetname_t *sp,
102 			    mdnamelist_t **nlpp, int options, md_error_t *ep);
103 static md_stripe_t	*(*mdl_meta_get_stripe)(mdsetname_t *sp,
104 			    mdname_t *stripenp, md_error_t *ep);
105 static int		(*mdl_meta_get_stripe_names)(mdsetname_t *sp,
106 			    mdnamelist_t **nlpp, int options, md_error_t *ep);
107 static int		(*mdl_meta_get_trans_names)(mdsetname_t *sp,
108 			    mdnamelist_t **nlpp, int options, md_error_t *ep);
109 static void		(*mdl_meta_invalidate_name)(mdname_t *np);
110 static void		(*mdl_sdssc_bind_library)();
111 
112 /*
113  * Search the list of devices under SVM for the specified device.
114  */
115 int
116 inuse_svm(char *slice, nvlist_t *attrs, int *errp)
117 {
118 	struct svm_list	*listp;
119 	int		found = 0;
120 
121 	*errp = 0;
122 	if (slice == NULL) {
123 	    return (found);
124 	}
125 
126 	(void) mutex_lock(&init_lock);
127 	if (!initialized) {
128 		/* dynamically load libmeta */
129 		if (init_svm()) {
130 			/*
131 			 * need to initialize the cluster library to
132 			 * avoid seg faults
133 			 */
134 			(mdl_sdssc_bind_library)();
135 
136 			/* load the SVM cache */
137 			*errp = load_svm();
138 
139 			if (*errp == 0) {
140 				/* start a thread to monitor the svm config */
141 				sysevent_handle_t *shp;
142 				const char *subclass_list[1];
143 				/*
144 				 * Only start the svmevent thread if
145 				 * we are not doing an install
146 				 */
147 
148 				if (getenv("_LIBDISKMGT_INSTALL") == NULL) {
149 					shp = sysevent_bind_handle(
150 					    event_handler);
151 					if (shp != NULL) {
152 						subclass_list[0] = EC_SUB_ALL;
153 						if (sysevent_subscribe_event(
154 						    shp, EC_SVM_CONFIG,
155 						    subclass_list, 1) != 0) {
156 							*errp = errno;
157 						}
158 					} else {
159 						*errp = errno;
160 					}
161 					if (*errp) {
162 						/*
163 						 * If the sysevent thread fails,
164 						 * log the error but continue
165 						 * on. This failing to start
166 						 * is not catastrophic in
167 						 * particular for short lived
168 						 * consumers of libdiskmgt.
169 						 */
170 						syslog(LOG_WARNING,
171 						    dgettext(TEXT_DOMAIN,
172 						    "libdiskmgt: sysevent "
173 						    "thread for SVM failed "
174 						    "to start\n"));
175 						*errp = 0;
176 					}
177 				}
178 			}
179 		}
180 
181 		if (*errp == 0) {
182 			initialized = 1;
183 		}
184 	}
185 	(void) mutex_unlock(&init_lock);
186 
187 	(void) rw_rdlock(&svm_lock);
188 	listp = svm_listp;
189 	while (listp != NULL) {
190 	    if (strcmp(slice, listp->slice) == 0) {
191 		libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_SVM, errp);
192 		if (strcmp(listp->type, "mdb") == 0 ||
193 		    strcmp(listp->type, "hs") == 0) {
194 
195 		    libdiskmgt_add_str(attrs, DM_USED_NAME, listp->type, errp);
196 		} else {
197 		    char name[MAXPATHLEN];
198 		    (void) snprintf(name, MAXPATHLEN, "%s:%s", listp->type,
199 			listp->name);
200 		    libdiskmgt_add_str(attrs, DM_USED_NAME, name, errp);
201 		}
202 		found = 1;
203 		break;
204 	    }
205 	    listp = listp->next;
206 	}
207 	(void) rw_unlock(&svm_lock);
208 
209 	return (found);
210 }
211 
212 static int
213 add_use_record(char *devname, char *type, char *mname)
214 {
215 	struct svm_list *sp;
216 
217 	/* If prev. record is a dup, skip it. */
218 	if (svm_listp != NULL && strcmp(svm_listp->slice, devname) == 0 &&
219 	    strcmp(svm_listp->type, type) == 0) {
220 	    return (0);
221 	}
222 
223 	sp = (struct svm_list *)malloc(sizeof (struct svm_list));
224 	if (sp == NULL) {
225 	    return (ENOMEM);
226 	}
227 
228 	if ((sp->slice = strdup(devname)) == NULL) {
229 	    free(sp);
230 	    return (ENOMEM);
231 	}
232 
233 	if ((sp->name = strdup(mname)) == NULL) {
234 	    free(sp->slice);
235 	    free(sp);
236 	    return (ENOMEM);
237 	}
238 
239 	if ((sp->type = strdup(type)) == NULL) {
240 	    free(sp->slice);
241 	    free(sp->name);
242 	    free(sp);
243 	    return (ENOMEM);
244 	}
245 
246 	sp->next = svm_listp;
247 	svm_listp = sp;
248 
249 	return (0);
250 }
251 
252 static int
253 diskset_info(mdsetname_t *sp)
254 {
255 	md_error_t		error = *mdl_mdnullerror;
256 	md_replicalist_t	*replica_list = NULL;
257 	mdnamelist_t		*trans_list = NULL;
258 	mdnamelist_t		*raid_list = NULL;
259 	mdnamelist_t		*stripe_list = NULL;
260 	mdnamelist_t		*sp_list = NULL;
261 	mdnamelist_t		*spare_list = NULL;
262 
263 	if ((mdl_metareplicalist)(sp, MD_BASICNAME_OK, &replica_list, &error)
264 	    >= 0) {
265 	    md_replicalist_t	*nlp;
266 
267 	    for (nlp = replica_list; nlp != NULL; nlp = nlp->rl_next) {
268 		if (new_entry(nlp->rl_repp->r_namep->bname, "mdb",
269 		    nlp->rl_repp->r_namep->cname, sp)) {
270 		    (mdl_metafreereplicalist)(replica_list);
271 		    return (ENOMEM);
272 		}
273 	    }
274 	    (mdl_metafreereplicalist)(replica_list);
275 
276 	} else {
277 	    (mdl_mdclrerror)(&error);
278 	    /* there are no metadb's; that is ok, no need to check the rest */
279 	    return (0);
280 	}
281 	(mdl_mdclrerror)(&error);
282 
283 	if ((mdl_meta_get_trans_names)(sp, &trans_list, 0, &error) >= 0) {
284 	    mdnamelist_t *nlp;
285 
286 	    for (nlp = trans_list; nlp != NULL; nlp = nlp->next) {
287 		if (new_entry(nlp->namep->bname, "trans", nlp->namep->cname,
288 		    sp)) {
289 		    free_names(trans_list);
290 		    return (ENOMEM);
291 		}
292 	    }
293 
294 	    free_names(trans_list);
295 	}
296 	(mdl_mdclrerror)(&error);
297 
298 	if ((mdl_meta_get_raid_names)(sp, &raid_list, 0, &error) >= 0) {
299 	    mdnamelist_t *nlp;
300 
301 	    for (nlp = raid_list; nlp != NULL; nlp = nlp->next) {
302 		mdname_t	*mdn;
303 		md_raid_t	*raid;
304 
305 		mdn = (mdl_metaname)(&sp, nlp->namep->cname,
306 		    META_DEVICE, &error);
307 		(mdl_mdclrerror)(&error);
308 		if (mdn == NULL) {
309 		    continue;
310 		}
311 
312 		raid = (mdl_meta_get_raid)(sp, mdn, &error);
313 		(mdl_mdclrerror)(&error);
314 
315 		if (raid != NULL) {
316 		    int i;
317 
318 		    for (i = 0; i < raid->cols.cols_len; i++) {
319 			if (new_entry(raid->cols.cols_val[i].colnamep->bname,
320 			    "raid", nlp->namep->cname, sp)) {
321 			    free_names(raid_list);
322 			    return (ENOMEM);
323 			}
324 		    }
325 		}
326 	    }
327 
328 	    free_names(raid_list);
329 	}
330 	(mdl_mdclrerror)(&error);
331 
332 	if ((mdl_meta_get_stripe_names)(sp, &stripe_list, 0, &error) >= 0) {
333 	    mdnamelist_t *nlp;
334 
335 	    for (nlp = stripe_list; nlp != NULL; nlp = nlp->next) {
336 		mdname_t	*mdn;
337 		md_stripe_t	*stripe;
338 
339 		mdn = (mdl_metaname)(&sp, nlp->namep->cname,
340 		    META_DEVICE, &error);
341 		(mdl_mdclrerror)(&error);
342 		if (mdn == NULL) {
343 		    continue;
344 		}
345 
346 		stripe = (mdl_meta_get_stripe)(sp, mdn, &error);
347 		(mdl_mdclrerror)(&error);
348 
349 		if (stripe != NULL) {
350 		    int i;
351 
352 		    for (i = 0; i < stripe->rows.rows_len; i++) {
353 			md_row_t	*rowp;
354 			int		j;
355 
356 			rowp = &stripe->rows.rows_val[i];
357 
358 			for (j = 0; j < rowp->comps.comps_len; j++) {
359 			    md_comp_t	*component;
360 
361 			    component = &rowp->comps.comps_val[j];
362 			    if (new_entry(component->compnamep->bname, "stripe",
363 				nlp->namep->cname, sp)) {
364 				free_names(stripe_list);
365 				return (ENOMEM);
366 			    }
367 			}
368 		    }
369 		}
370 	    }
371 
372 	    free_names(stripe_list);
373 	}
374 	(mdl_mdclrerror)(&error);
375 
376 	if ((mdl_meta_get_sp_names)(sp, &sp_list, 0, &error) >= 0) {
377 	    mdnamelist_t *nlp;
378 
379 	    for (nlp = sp_list; nlp != NULL; nlp = nlp->next) {
380 		mdname_t	*mdn;
381 		md_sp_t		*soft_part;
382 
383 		mdn = (mdl_metaname)(&sp, nlp->namep->cname,
384 		    META_DEVICE, &error);
385 		(mdl_mdclrerror)(&error);
386 		if (mdn == NULL) {
387 		    continue;
388 		}
389 
390 		soft_part = (mdl_meta_get_sp)(sp, mdn, &error);
391 		(mdl_mdclrerror)(&error);
392 
393 		if (soft_part != NULL) {
394 		    if (new_entry(soft_part->compnamep->bname, "sp",
395 			nlp->namep->cname, sp)) {
396 			free_names(sp_list);
397 			return (ENOMEM);
398 		    }
399 		}
400 	    }
401 
402 	    free_names(sp_list);
403 	}
404 	(mdl_mdclrerror)(&error);
405 
406 	if ((mdl_meta_get_hotspare_names)(sp, &spare_list, 0, &error) >= 0) {
407 	    mdnamelist_t *nlp;
408 
409 	    for (nlp = spare_list; nlp != NULL; nlp = nlp->next) {
410 		if (new_entry(nlp->namep->bname, "hs", nlp->namep->cname, sp)) {
411 		    free_names(spare_list);
412 		    return (ENOMEM);
413 		}
414 	    }
415 
416 	    free_names(spare_list);
417 	}
418 
419 	(mdl_mdclrerror)(&error);
420 
421 	return (0);
422 }
423 
424 /*
425  * SVM uses "drive names" (ctd name without trailing slice) for drives
426  * in disksets.  Since it massages these names there is no direct correspondence
427  * with the slice device names in /dev.  So, we need to massage these names
428  * back to something we can match on when a slice comes in.  We create an
429  * entry for each possible slice since we don't know what slices actually
430  * exist.  Slice 0 & 7 are probably enough, but the user could have
431  * repartitioned the drive after they added it to the diskset and removed the
432  * mdb.
433  */
434 static int
435 drive_in_diskset(char *dpath, char *setname)
436 {
437 	int i;
438 	char path[MAXPATHLEN];
439 
440 	(void) strlcpy(path, dpath, sizeof (path));
441 	if (strncmp(path, "/dev/rdsk/", 10) == 0) {
442 	    /* change rdsk to dsk */
443 	    char *p;
444 
445 	    /* start p pointing to r in rdsk */
446 	    for (p = path + 5; *p; p++) {
447 		*p = *(p + 1);
448 	    }
449 	} else if (strncmp(path, "/dev/did/rdsk/", 14) == 0) {
450 	    /* change rdsk to dsk */
451 	    char *p;
452 
453 	    /* start p pointing to r in rdsk */
454 	    for (p = path + 9; *p; p++) {
455 		*p = *(p + 1);
456 	    }
457 	}
458 
459 	for (i = 0; i < 8; i++) {
460 	    char slice[MAXPATHLEN];
461 
462 	    (void) snprintf(slice, sizeof (slice), "%ss%d", path, i);
463 	    if (add_use_record(slice, "diskset", setname)) {
464 		return (ENOMEM);
465 	    }
466 	}
467 
468 	return (0);
469 }
470 
471 static void
472 event_handler()
473 {
474 	(void) rw_wrlock(&svm_lock);
475 	free_svm(svm_listp);
476 	svm_listp = NULL;
477 	(mdl_metaflushnames)(0);
478 	(void) load_svm();
479 	(void) rw_unlock(&svm_lock);
480 }
481 
482 static void
483 free_names(mdnamelist_t *nlp)
484 {
485 	mdnamelist_t *p;
486 
487 	for (p = nlp; p != NULL; p = p->next) {
488 	    (mdl_meta_invalidate_name)(p->namep);
489 	    p->namep = NULL;
490 	}
491 	(mdl_metafreenamelist)(nlp);
492 }
493 
494 /*
495  * Free the list of SVM entries.
496  */
497 static void
498 free_svm(struct svm_list *listp) {
499 
500 	struct svm_list	*nextp;
501 
502 	while (listp != NULL) {
503 	    nextp = listp->next;
504 	    free((void *)listp->slice);
505 	    free((void *)listp->name);
506 	    free((void *)listp->type);
507 	    free((void *)listp);
508 	    listp = nextp;
509 	}
510 }
511 
512 /*
513  * Try to dynamically link the libmeta functions we need.
514  */
515 static int
516 init_svm()
517 {
518 	void	*lh;
519 
520 	if ((lh = dlopen("/usr/lib/libmeta.so", RTLD_NOW)) == NULL) {
521 	    return (0);
522 	}
523 
524 	mdl_get_max_sets = (set_t (*)(md_error_t *))dlsym(lh, "get_max_sets");
525 
526 	mdl_mdclrerror = (void(*)(md_error_t *))dlsym(lh, "mdclrerror");
527 
528 	mdl_mdnullerror = (md_error_t *)dlsym(lh, "mdnullerror");
529 
530 	mdl_metaflushnames = (void (*)(int))dlsym(lh, "metaflushnames");
531 
532 	mdl_metaflushsetname = (void (*)(mdsetname_t *))dlsym(lh,
533 	    "metaflushsetname");
534 
535 	mdl_metafreenamelist = (void (*)(mdnamelist_t *))dlsym(lh,
536 	    "metafreenamelist");
537 
538 	mdl_metafreereplicalist = (void (*)(md_replicalist_t *))dlsym(lh,
539 	    "metafreereplicalist");
540 
541 	mdl_metaget_drivedesc = (md_drive_desc *(*)(mdsetname_t *, int,
542 	    md_error_t *))dlsym(lh, "metaget_drivedesc");
543 
544 	mdl_metaname = (mdname_t *(*)(mdsetname_t **, char *,
545 	    meta_device_type_t, md_error_t *))dlsym(lh, "metaname");
546 
547 	mdl_metareplicalist = (int (*)(mdsetname_t *, int, md_replicalist_t **,
548 	    md_error_t *))dlsym(lh, "metareplicalist");
549 
550 	mdl_metasetnosetname = (mdsetname_t *(*)(set_t, md_error_t *))dlsym(lh,
551 	    "metasetnosetname");
552 
553 	mdl_meta_get_hotspare_names = (int (*)(mdsetname_t *, mdnamelist_t **,
554 	    int, md_error_t *))dlsym(lh, "meta_get_hotspare_names");
555 
556 	mdl_meta_get_raid = (md_raid_t *(*)(mdsetname_t *, mdname_t *,
557 	    md_error_t *))dlsym(lh, "meta_get_raid");
558 
559 	mdl_meta_get_raid_names = (int (*)(mdsetname_t *, mdnamelist_t **,
560 	    int, md_error_t *))dlsym(lh, "meta_get_raid_names");
561 
562 	mdl_meta_get_sp = (md_sp_t *(*)(mdsetname_t *, mdname_t *,
563 	    md_error_t *))dlsym(lh, "meta_get_sp");
564 
565 	mdl_meta_get_sp_names = (int (*)(mdsetname_t *, mdnamelist_t **,
566 	    int, md_error_t *))dlsym(lh, "meta_get_sp_names");
567 
568 	mdl_meta_get_stripe = (md_stripe_t *(*)(mdsetname_t *, mdname_t *,
569 	    md_error_t *))dlsym(lh, "meta_get_stripe");
570 
571 	mdl_meta_get_stripe_names = (int (*)(mdsetname_t *, mdnamelist_t **,
572 	    int, md_error_t *))dlsym(lh, "meta_get_stripe_names");
573 
574 	mdl_meta_get_trans_names = (int (*)(mdsetname_t *, mdnamelist_t **,
575 	    int, md_error_t *))dlsym(lh, "meta_get_trans_names");
576 
577 	mdl_meta_invalidate_name = (void (*)(mdname_t *))dlsym(lh,
578 	    "meta_invalidate_name");
579 
580 	mdl_sdssc_bind_library = (void (*)())dlsym(lh, "sdssc_bind_library");
581 
582 	return (1);
583 }
584 
585 /*
586  * Create a list of SVM devices
587  */
588 static int
589 load_svm()
590 {
591 	int		max_sets;
592 	md_error_t	error = *mdl_mdnullerror;
593 	int		i;
594 
595 	if ((max_sets = (mdl_get_max_sets)(&error)) == 0) {
596 	    return (0);
597 	}
598 
599 	if (!mdisok(&error)) {
600 	    (mdl_mdclrerror)(&error);
601 	    return (0);
602 	}
603 
604 	/* for each possible set number, see if we really have a diskset */
605 	for (i = 0; i < max_sets; i++) {
606 	    mdsetname_t	*sp;
607 
608 	    if ((sp = (mdl_metasetnosetname)(i, &error)) == NULL) {
609 
610 		if (!mdisok(&error) &&
611 		    mdisrpcerror(&error, RPC_PROGNOTREGISTERED)) {
612 		    /* metad rpc program not registered - no metasets */
613 		    break;
614 		}
615 
616 		(mdl_mdclrerror)(&error);
617 		continue;
618 	    }
619 	    (mdl_mdclrerror)(&error);
620 
621 	    /* pick up drives in disksets with no mdbs/metadevices */
622 	    if (sp->setno != 0) {
623 		md_drive_desc	*dd;
624 
625 		dd = (mdl_metaget_drivedesc)(sp, MD_BASICNAME_OK | PRINT_FAST,
626 		    &error);
627 		(mdl_mdclrerror)(&error);
628 		for (; dd != NULL; dd = dd->dd_next) {
629 		    if (drive_in_diskset(dd->dd_dnp->rname, sp->setname)) {
630 			(mdl_metaflushsetname)(sp);
631 			return (ENOMEM);
632 		    }
633 		}
634 	    }
635 
636 	    if (diskset_info(sp)) {
637 		(mdl_metaflushsetname)(sp);
638 		return (ENOMEM);
639 	    }
640 
641 	    (mdl_metaflushsetname)(sp);
642 	}
643 
644 	(mdl_mdclrerror)(&error);
645 
646 	return (0);
647 }
648 
649 static int
650 new_entry(char *sname, char *type, char *mname, mdsetname_t *sp)
651 {
652 	mdname_t	*mdn;
653 	md_error_t	 error = *mdl_mdnullerror;
654 
655 	mdn = (mdl_metaname)(&sp, sname, UNKNOWN, &error);
656 	if (!mdisok(&error)) {
657 	    (mdl_mdclrerror)(&error);
658 	    return (0);
659 	}
660 
661 	if (mdn != NULL && (
662 	    mdn->drivenamep->type == MDT_ACCES ||
663 	    mdn->drivenamep->type == MDT_COMP ||
664 	    mdn->drivenamep->type == MDT_FAST_COMP)) {
665 
666 	    return (add_use_record(mdn->bname, type, mname));
667 	}
668 
669 	return (0);
670 }
671