xref: /netbsd-src/sys/dev/dm/dm_dev.c (revision df7f595ecd6efe54ea7c11083e2dbf711cad4b31)
1 /*        $NetBSD: dm_dev.c,v 1.4 2009/03/18 10:22:39 cegger 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/disk.h>
36 #include <sys/disklabel.h>
37 #include <sys/ioctl.h>
38 #include <sys/ioccom.h>
39 #include <sys/kmem.h>
40 
41 #include "netbsd-dm.h"
42 #include "dm.h"
43 
44 static dm_dev_t* dm_dev_lookup_name(const char *);
45 static dm_dev_t* dm_dev_lookup_uuid(const char *);
46 static dm_dev_t* dm_dev_lookup_minor(int);
47 
48 static struct dm_dev_head dm_dev_list =
49 TAILQ_HEAD_INITIALIZER(dm_dev_list);
50 
51 kmutex_t dm_dev_mutex;
52 
53 __inline static void
54 disable_dev(dm_dev_t *dmv)
55 {
56 		TAILQ_REMOVE(&dm_dev_list, dmv, next_devlist);
57                 mutex_enter(&dmv->dev_mtx);
58                 mutex_exit(&dm_dev_mutex);
59                 while(dmv->ref_cnt != 0)
60 	              cv_wait(&dmv->dev_cv, &dmv->dev_mtx);
61                 mutex_exit(&dmv->dev_mtx);
62 }
63 
64 /*
65  * Generic function used to lookup dm_dev_t. Calling with dm_dev_name
66  * and dm_dev_uuid NULL is allowed.
67  */
68 dm_dev_t*
69 dm_dev_lookup(const char *dm_dev_name, const char *dm_dev_uuid,
70  	int dm_dev_minor)
71 {
72 	dm_dev_t *dmv;
73 
74 	dmv = NULL;
75 	mutex_enter(&dm_dev_mutex);
76 
77 	/* KASSERT(dm_dev_name != NULL && dm_dev_uuid != NULL && dm_dev_minor > 0); */
78 	if (dm_dev_minor > 0)
79 		if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL){
80 			dm_dev_busy(dmv);
81 			mutex_exit(&dm_dev_mutex);
82 			return dmv;
83 		}
84 
85 	if (dm_dev_name != NULL)
86 		if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL){
87 			dm_dev_busy(dmv);
88 			mutex_exit(&dm_dev_mutex);
89 			return dmv;
90 		}
91 
92 	if (dm_dev_uuid != NULL)
93 		if ((dmv = dm_dev_lookup_uuid(dm_dev_uuid)) != NULL){
94 			dm_dev_busy(dmv);
95 			mutex_exit(&dm_dev_mutex);
96 			return dmv;
97 		}
98 	mutex_exit(&dm_dev_mutex);
99 	return NULL;
100 }
101 
102 
103 /*
104  * Lookup device with its minor number.
105  */
106 static dm_dev_t*
107 dm_dev_lookup_minor(int dm_dev_minor)
108 {
109 	dm_dev_t *dmv;
110 
111 	TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){
112 		if (dm_dev_minor == dmv->minor)
113 			return dmv;
114 	}
115 
116 	return NULL;
117 }
118 
119 /*
120  * Lookup device with it's device name.
121  */
122 static dm_dev_t*
123 dm_dev_lookup_name(const char *dm_dev_name)
124 {
125 	dm_dev_t *dmv;
126 	int dlen; int slen;
127 
128 	slen = strlen(dm_dev_name);
129 
130 	if (slen == 0)
131 		return NULL;
132 
133 	TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){
134 
135 		dlen = strlen(dmv->name);
136 
137 		if(slen != dlen)
138 			continue;
139 
140 		if (strncmp(dm_dev_name, dmv->name, slen) == 0)
141 			return dmv;
142 	}
143 
144 	return NULL;
145 }
146 
147 /*
148  * Lookup device with it's device uuid. Used mostly by LVM2tools.
149  */
150 static dm_dev_t*
151 dm_dev_lookup_uuid(const char *dm_dev_uuid)
152 {
153 	dm_dev_t *dmv;
154 	size_t len;
155 
156 	len = 0;
157 	len = strlen(dm_dev_uuid);
158 
159 	if (len == 0)
160 		return NULL;
161 
162 	TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){
163 
164 		if (strlen(dmv->uuid) != len)
165 			continue;
166 
167 		if (strncmp(dm_dev_uuid, dmv->uuid, strlen(dmv->uuid)) == 0)
168 			return dmv;
169 	}
170 
171 	return NULL;
172 }
173 
174 /*
175  * Insert new device to the global list of devices.
176  */
177 int
178 dm_dev_insert(dm_dev_t *dev)
179 {
180 	dm_dev_t *dmv;
181 	int r;
182 
183 	dmv = NULL;
184 	r = 0;
185 
186 	KASSERT(dev != NULL);
187 	mutex_enter(&dm_dev_mutex);
188 	if (((dmv = dm_dev_lookup_uuid(dev->uuid)) == NULL) &&
189 	    ((dmv = dm_dev_lookup_name(dev->name)) == NULL) &&
190 	    ((dmv = dm_dev_lookup_minor(dev->minor)) == NULL)){
191 
192 		TAILQ_INSERT_TAIL(&dm_dev_list, dev, next_devlist);
193 
194 	} else
195 		r = EEXIST;
196 
197 	mutex_exit(&dm_dev_mutex);
198 	return r;
199 }
200 
201 #ifdef notyet
202 /*
203  * Lookup device with its minor number.
204  */
205 int
206 dm_dev_test_minor(int dm_dev_minor)
207 {
208 	dm_dev_t *dmv;
209 
210 	mutex_enter(&dm_dev_mutex);
211 	TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){
212 		if (dm_dev_minor == dmv->minor){
213 			mutex_exit(&dm_dev_mutex);
214 			return 1;
215 		}
216 	}
217 	mutex_exit(&dm_dev_mutex);
218 
219 	return 0;
220 }
221 #endif
222 
223 /*
224  * Remove device selected with dm_dev from global list of devices.
225  */
226 dm_dev_t*
227 dm_dev_rem(const char *dm_dev_name, const char *dm_dev_uuid,
228  	int dm_dev_minor)
229 {
230 	dm_dev_t *dmv;
231 	dmv = NULL;
232 
233 	mutex_enter(&dm_dev_mutex);
234 
235 	if (dm_dev_minor > 0)
236 		if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL){
237 			disable_dev(dmv);
238 			return dmv;
239 		}
240 
241 	if (dm_dev_name != NULL)
242 		if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL){
243 			disable_dev(dmv);
244 			return dmv;
245 		}
246 
247 	if (dm_dev_uuid != NULL)
248 		if ((dmv = dm_dev_lookup_name(dm_dev_uuid)) != NULL){
249 			disable_dev(dmv);
250 			return dmv;
251 		}
252 	mutex_exit(&dm_dev_mutex);
253 
254 	return NULL;
255 }
256 
257 /*
258  * Destroy all devices created in device-mapper. Remove all tables
259  * free all allocated memmory.
260  */
261 int
262 dm_dev_destroy(void)
263 {
264 	dm_dev_t *dmv;
265 	mutex_enter(&dm_dev_mutex);
266 
267 	while (TAILQ_FIRST(&dm_dev_list) != NULL){
268 
269 		dmv = TAILQ_FIRST(&dm_dev_list);
270 
271 		TAILQ_REMOVE(&dm_dev_list, TAILQ_FIRST(&dm_dev_list),
272 		    next_devlist);
273 
274 		mutex_enter(&dmv->dev_mtx);
275 
276 		while (dmv->ref_cnt != 0)
277 			cv_wait(&dmv->dev_cv, &dmv->dev_mtx);
278 
279 		/* Destroy active table first.  */
280 		dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE);
281 
282 		/* Destroy inactive table if exits, too. */
283 		dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
284 
285 		dm_table_head_destroy(&dmv->table_head);
286 
287 		mutex_exit(&dmv->dev_mtx);
288 		mutex_destroy(&dmv->dev_mtx);
289 		cv_destroy(&dmv->dev_cv);
290 
291 		(void)kmem_free(dmv, sizeof(dm_dev_t));
292 	}
293 	mutex_exit(&dm_dev_mutex);
294 
295 	mutex_destroy(&dm_dev_mutex);
296        	return 0;
297 }
298 
299 /*
300  * Allocate new device entry.
301  */
302 dm_dev_t*
303 dm_dev_alloc(void)
304 {
305 	dm_dev_t *dmv;
306 
307 	dmv = kmem_zalloc(sizeof(dm_dev_t), KM_NOSLEEP);
308 
309 	if(dmv != NULL)
310 		dmv->diskp = kmem_zalloc(sizeof(struct disk), KM_NOSLEEP);
311 
312 	return dmv;
313 }
314 
315 /*
316  * Freed device entry.
317  */
318 int
319 dm_dev_free(dm_dev_t *dmv)
320 {
321 	KASSERT(dmv != NULL);
322 
323 	if(dmv->diskp != NULL)
324 		(void)kmem_free(dmv->diskp, sizeof(struct disk));
325 
326 	(void)kmem_free(dmv, sizeof(dm_dev_t));
327 
328 	return 0;
329 }
330 
331 void
332 dm_dev_busy(dm_dev_t *dmv)
333 {
334 	mutex_enter(&dmv->dev_mtx);
335 	dmv->ref_cnt++;
336 	mutex_exit(&dmv->dev_mtx);
337 }
338 
339 void
340 dm_dev_unbusy(dm_dev_t *dmv)
341 {
342 	KASSERT(dmv->ref_cnt != 0);
343 
344 	mutex_enter(&dmv->dev_mtx);
345 	if (--dmv->ref_cnt == 0)
346 		cv_broadcast(&dmv->dev_cv);
347 	mutex_exit(&dmv->dev_mtx);
348 }
349 
350 /*
351  * Return prop_array of dm_targer_list dictionaries.
352  */
353 prop_array_t
354 dm_dev_prop_list(void)
355 {
356 	dm_dev_t *dmv;
357 	prop_array_t dev_array;
358 	prop_dictionary_t dev_dict;
359 
360 	dev_array = prop_array_create();
361 
362 	mutex_enter(&dm_dev_mutex);
363 
364 	TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
365 		dev_dict  = prop_dictionary_create();
366 
367 		prop_dictionary_set_cstring(dev_dict, DM_DEV_NAME, dmv->name);
368 		prop_dictionary_set_uint32(dev_dict, DM_DEV_DEV, dmv->minor);
369 
370 		prop_array_add(dev_array, dev_dict);
371 		prop_object_release(dev_dict);
372 	}
373 
374 	mutex_exit(&dm_dev_mutex);
375 	return dev_array;
376 }
377 
378 /*
379  * Initialize global device mutex.
380  */
381 int
382 dm_dev_init(void)
383 {
384 	TAILQ_INIT(&dm_dev_list); /* initialize global dev list */
385 	mutex_init(&dm_dev_mutex, MUTEX_DEFAULT, IPL_NONE);
386 	return 0;
387 }
388