xref: /netbsd-src/sys/dev/dm/dm_dev.c (revision 466a16a118933bd295a8a104f095714fadf9cf68)
1 /*        $NetBSD: dm_dev.c,v 1.2 2008/12/19 15:24:03 haad Exp $      */
2 
3 /*
4  * Copyright (c) 2008 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Adam Hamsik.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/types.h>
33 #include <sys/param.h>
34 
35 #include <sys/disklabel.h>
36 #include <sys/ioctl.h>
37 #include <sys/ioccom.h>
38 #include <sys/kmem.h>
39 
40 #include "netbsd-dm.h"
41 #include "dm.h"
42 
43 static dm_dev_t* dm_dev_lookup_name(const char *);
44 static dm_dev_t* dm_dev_lookup_uuid(const char *);
45 static dm_dev_t* dm_dev_lookup_minor(int);
46 
47 static struct dm_dev_head dm_dev_list =
48 TAILQ_HEAD_INITIALIZER(dm_dev_list);
49 
50 kmutex_t dm_dev_mutex;
51 
52 __inline static void
53 disable_dev(dm_dev_t *dmv)
54 {
55 		TAILQ_REMOVE(&dm_dev_list, dmv, next_devlist);
56                 mutex_enter(&dmv->dev_mtx);
57                 mutex_exit(&dm_dev_mutex);
58                 while(dmv->ref_cnt != 0)
59 	              cv_wait(&dmv->dev_cv, &dmv->dev_mtx);
60                 mutex_exit(&dmv->dev_mtx);
61 }
62 
63 /*
64  * Generic function used to lookup dm_dev_t. Calling with dm_dev_name
65  * and dm_dev_uuid NULL is allowed.
66  */
67 dm_dev_t*
68 dm_dev_lookup(const char *dm_dev_name, const char *dm_dev_uuid,
69  	int dm_dev_minor)
70 {
71 	dm_dev_t *dmv;
72 
73 	dmv = NULL;
74 	mutex_enter(&dm_dev_mutex);
75 
76 	/* KASSERT(dm_dev_name != NULL && dm_dev_uuid != NULL && dm_dev_minor > 0); */
77 	if (dm_dev_minor > 0)
78 		if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL){
79 			dm_dev_busy(dmv);
80 			mutex_exit(&dm_dev_mutex);
81 			return dmv;
82 		}
83 
84 	if (dm_dev_name != NULL)
85 		if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL){
86 			dm_dev_busy(dmv);
87 			mutex_exit(&dm_dev_mutex);
88 			return dmv;
89 		}
90 
91 	if (dm_dev_uuid != NULL)
92 		if ((dmv = dm_dev_lookup_name(dm_dev_uuid)) != NULL){
93 			dm_dev_busy(dmv);
94 			mutex_exit(&dm_dev_mutex);
95 			return dmv;
96 		}
97 	mutex_exit(&dm_dev_mutex);
98 	return NULL;
99 }
100 
101 
102 /*
103  * Lookup device with its minor number.
104  */
105 static dm_dev_t*
106 dm_dev_lookup_minor(int dm_dev_minor)
107 {
108 	dm_dev_t *dmv;
109 
110 	TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){
111 		if (dm_dev_minor == dmv->minor)
112 			return dmv;
113 	}
114 
115 	return NULL;
116 }
117 
118 /*
119  * Lookup device with it's device name.
120  */
121 static dm_dev_t*
122 dm_dev_lookup_name(const char *dm_dev_name)
123 {
124 	dm_dev_t *dmv;
125 	int dlen; int slen;
126 
127 	slen = strlen(dm_dev_name);
128 
129 	if (slen == 0)
130 		return NULL;
131 
132 	TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){
133 
134 		dlen = strlen(dmv->name);
135 
136 		if(slen != dlen)
137 			continue;
138 
139 		if (strncmp(dm_dev_name, dmv->name, slen) == 0)
140 			return dmv;
141 	}
142 
143 	return NULL;
144 }
145 
146 /*
147  * Lookup device with it's device uuid. Used mostly by LVM2tools.
148  */
149 static dm_dev_t*
150 dm_dev_lookup_uuid(const char *dm_dev_uuid)
151 {
152 	dm_dev_t *dmv;
153 	size_t len;
154 
155 	len = 0;
156 	len = strlen(dm_dev_uuid);
157 
158 	if (len == 0)
159 		return NULL;
160 
161 	TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){
162 
163 		if (strlen(dmv->uuid) != len)
164 			continue;
165 
166 		if (strncmp(dm_dev_uuid, dmv->uuid, strlen(dmv->uuid)) == 0)
167 			return dmv;
168 	}
169 
170 	return NULL;
171 }
172 
173 /*
174  * Insert new device to the global list of devices.
175  */
176 int
177 dm_dev_insert(dm_dev_t *dev)
178 {
179 	dm_dev_t *dmv;
180 	int r;
181 
182 	dmv = NULL;
183 	r = 0;
184 
185 	KASSERT(dev != NULL);
186 	mutex_enter(&dm_dev_mutex);
187 	if (((dmv = dm_dev_lookup_uuid(dev->uuid)) == NULL) &&
188 	    ((dmv = dm_dev_lookup_name(dev->name)) == NULL) &&
189 	    ((dmv = dm_dev_lookup_minor(dev->minor)) == NULL)){
190 
191 		TAILQ_INSERT_TAIL(&dm_dev_list, dev, next_devlist);
192 
193 	} else
194 		r = EEXIST;
195 
196 	mutex_exit(&dm_dev_mutex);
197 	return r;
198 }
199 
200 #ifdef notyet
201 /*
202  * Lookup device with its minor number.
203  */
204 int
205 dm_dev_test_minor(int dm_dev_minor)
206 {
207 	dm_dev_t *dmv;
208 
209 	mutex_enter(&dm_dev_mutex);
210 	TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){
211 		if (dm_dev_minor == dmv->minor){
212 			mutex_exit(&dm_dev_mutex);
213 			return 1;
214 		}
215 	}
216 	mutex_exit(&dm_dev_mutex);
217 
218 	return 0;
219 }
220 #endif
221 
222 /*
223  * Remove device selected with dm_dev from global list of devices.
224  */
225 dm_dev_t*
226 dm_dev_rem(const char *dm_dev_name, const char *dm_dev_uuid,
227  	int dm_dev_minor)
228 {
229 	dm_dev_t *dmv;
230 	dmv = NULL;
231 
232 	mutex_enter(&dm_dev_mutex);
233 
234 	if (dm_dev_minor > 0)
235 		if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL){
236 			disable_dev(dmv);
237 			return dmv;
238 		}
239 
240 	if (dm_dev_name != NULL)
241 		if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL){
242 			disable_dev(dmv);
243 			return dmv;
244 		}
245 
246 	if (dm_dev_uuid != NULL)
247 		if ((dmv = dm_dev_lookup_name(dm_dev_uuid)) != NULL){
248 			disable_dev(dmv);
249 			return dmv;
250 		}
251 	mutex_exit(&dm_dev_mutex);
252 
253 	return NULL;
254 }
255 
256 /*
257  * Destroy all devices created in device-mapper. Remove all tables
258  * free all allocated memmory.
259  */
260 int
261 dm_dev_destroy(void)
262 {
263 	dm_dev_t *dmv;
264 	mutex_enter(&dm_dev_mutex);
265 
266 	while (TAILQ_FIRST(&dm_dev_list) != NULL){
267 
268 		dmv = TAILQ_FIRST(&dm_dev_list);
269 
270 		TAILQ_REMOVE(&dm_dev_list, TAILQ_FIRST(&dm_dev_list),
271 		    next_devlist);
272 
273 		mutex_enter(&dmv->dev_mtx);
274 
275 		while (dmv->ref_cnt != 0)
276 			cv_wait(&dmv->dev_cv, &dmv->dev_mtx);
277 
278 		/* Destroy active table first.  */
279 		dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE);
280 
281 		/* Destroy inactive table if exits, too. */
282 		dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
283 
284 		dm_table_head_destroy(&dmv->table_head);
285 
286 		mutex_exit(&dmv->dev_mtx);
287 		mutex_destroy(&dmv->dev_mtx);
288 		cv_destroy(&dmv->dev_cv);
289 
290 		(void)kmem_free(dmv, sizeof(dm_dev_t));
291 	}
292 	mutex_exit(&dm_dev_mutex);
293 
294 	mutex_destroy(&dm_dev_mutex);
295        	return 0;
296 }
297 
298 /*
299  * Allocate new device entry.
300  */
301 dm_dev_t*
302 dm_dev_alloc()
303 {
304 	dm_dev_t *dmv;
305 
306 	dmv = kmem_zalloc(sizeof(dm_dev_t), KM_NOSLEEP);
307 	dmv->dk_label = kmem_zalloc(sizeof(struct disklabel), KM_NOSLEEP);
308 
309 	return dmv;
310 }
311 
312 /*
313  * Freed device entry.
314  */
315 int
316 dm_dev_free(dm_dev_t *dmv)
317 {
318 	KASSERT(dmv != NULL);
319 
320 	if (dmv->dk_label != NULL)
321 		(void)kmem_free(dmv->dk_label, sizeof(struct disklabel));
322 
323 	(void)kmem_free(dmv, sizeof(dm_dev_t));
324 
325 	return 0;
326 }
327 
328 void
329 dm_dev_busy(dm_dev_t *dmv)
330 {
331 	mutex_enter(&dmv->dev_mtx);
332 	dmv->ref_cnt++;
333 	mutex_exit(&dmv->dev_mtx);
334 }
335 
336 void
337 dm_dev_unbusy(dm_dev_t *dmv)
338 {
339 	KASSERT(dmv->ref_cnt != 0);
340 
341 	mutex_enter(&dmv->dev_mtx);
342 	if (--dmv->ref_cnt == 0)
343 		cv_broadcast(&dmv->dev_cv);
344 	mutex_exit(&dmv->dev_mtx);
345 }
346 
347 /*
348  * Return prop_array of dm_targer_list dictionaries.
349  */
350 prop_array_t
351 dm_dev_prop_list(void)
352 {
353 	dm_dev_t *dmv;
354 	prop_array_t dev_array;
355 	prop_dictionary_t dev_dict;
356 
357 	dev_array = prop_array_create();
358 
359 	mutex_enter(&dm_dev_mutex);
360 
361 	TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
362 		dev_dict  = prop_dictionary_create();
363 
364 		prop_dictionary_set_cstring(dev_dict, DM_DEV_NAME, dmv->name);
365 		prop_dictionary_set_uint32(dev_dict, DM_DEV_DEV, dmv->minor);
366 
367 		prop_array_add(dev_array, dev_dict);
368 		prop_object_release(dev_dict);
369 	}
370 
371 	mutex_exit(&dm_dev_mutex);
372 	return dev_array;
373 }
374 
375 /*
376  * Initialize global device mutex.
377  */
378 int
379 dm_dev_init()
380 {
381 	TAILQ_INIT(&dm_dev_list); /* initialize global dev list */
382 	mutex_init(&dm_dev_mutex, MUTEX_DEFAULT, IPL_NONE);
383 	return 0;
384 }
385