1 | /* $NetBSD: dm_pdev.c,v 1.8 2010/12/23 14:58:13 mlelstv 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/fcntl.h> |
37 | #include <sys/kmem.h> |
38 | #include <sys/namei.h> |
39 | #include <sys/vnode.h> |
40 | |
41 | #include <dev/dkvar.h> |
42 | |
43 | #include "dm.h" |
44 | |
45 | SLIST_HEAD(dm_pdevs, dm_pdev) dm_pdev_list; |
46 | |
47 | kmutex_t dm_pdev_mutex; |
48 | |
49 | static dm_pdev_t *dm_pdev_alloc(const char *); |
50 | static int dm_pdev_rem(dm_pdev_t *); |
51 | static dm_pdev_t *dm_pdev_lookup_name(const char *); |
52 | |
53 | /* |
54 | * Find used pdev with name == dm_pdev_name. |
55 | */ |
56 | dm_pdev_t * |
57 | dm_pdev_lookup_name(const char *dm_pdev_name) |
58 | { |
59 | dm_pdev_t *dm_pdev; |
60 | int dlen; |
61 | int slen; |
62 | |
63 | KASSERT(dm_pdev_name != NULL); |
64 | |
65 | slen = strlen(dm_pdev_name); |
66 | |
67 | SLIST_FOREACH(dm_pdev, &dm_pdev_list, next_pdev) { |
68 | dlen = strlen(dm_pdev->name); |
69 | |
70 | if (slen != dlen) |
71 | continue; |
72 | |
73 | if (strncmp(dm_pdev_name, dm_pdev->name, slen) == 0) |
74 | return dm_pdev; |
75 | } |
76 | |
77 | return NULL; |
78 | } |
79 | /* |
80 | * Create entry for device with name dev_name and open vnode for it. |
81 | * If entry already exists in global SLIST I will only increment |
82 | * reference counter. |
83 | */ |
84 | dm_pdev_t * |
85 | dm_pdev_insert(const char *dev_name) |
86 | { |
87 | struct pathbuf *dev_pb; |
88 | dm_pdev_t *dmp; |
89 | int error; |
90 | |
91 | KASSERT(dev_name != NULL); |
92 | |
93 | mutex_enter(&dm_pdev_mutex); |
94 | dmp = dm_pdev_lookup_name(dev_name); |
95 | |
96 | if (dmp != NULL) { |
97 | dmp->ref_cnt++; |
98 | aprint_debug("dmp_pdev_insert pdev %s already in tree\n" , dev_name); |
99 | mutex_exit(&dm_pdev_mutex); |
100 | return dmp; |
101 | } |
102 | mutex_exit(&dm_pdev_mutex); |
103 | |
104 | if ((dmp = dm_pdev_alloc(dev_name)) == NULL) |
105 | return NULL; |
106 | |
107 | dev_pb = pathbuf_create(dev_name); |
108 | if (dev_pb == NULL) { |
109 | aprint_debug("pathbuf_create on device: %s failed!\n" , |
110 | dev_name); |
111 | kmem_free(dmp, sizeof(dm_pdev_t)); |
112 | return NULL; |
113 | } |
114 | error = dk_lookup(dev_pb, curlwp, &dmp->pdev_vnode); |
115 | pathbuf_destroy(dev_pb); |
116 | if (error) { |
117 | aprint_debug("dk_lookup on device: %s failed with error %d!\n" , |
118 | dev_name, error); |
119 | kmem_free(dmp, sizeof(dm_pdev_t)); |
120 | return NULL; |
121 | } |
122 | getdisksize(dmp->pdev_vnode, &dmp->pdev_numsec, &dmp->pdev_secsize); |
123 | dmp->ref_cnt = 1; |
124 | |
125 | mutex_enter(&dm_pdev_mutex); |
126 | SLIST_INSERT_HEAD(&dm_pdev_list, dmp, next_pdev); |
127 | mutex_exit(&dm_pdev_mutex); |
128 | |
129 | return dmp; |
130 | } |
131 | /* |
132 | * Initialize pdev subsystem. |
133 | */ |
134 | int |
135 | dm_pdev_init(void) |
136 | { |
137 | SLIST_INIT(&dm_pdev_list); /* initialize global pdev list */ |
138 | mutex_init(&dm_pdev_mutex, MUTEX_DEFAULT, IPL_NONE); |
139 | |
140 | return 0; |
141 | } |
142 | /* |
143 | * Allocat new pdev structure if is not already present and |
144 | * set name. |
145 | */ |
146 | static dm_pdev_t * |
147 | dm_pdev_alloc(const char *name) |
148 | { |
149 | dm_pdev_t *dmp; |
150 | |
151 | if ((dmp = kmem_zalloc(sizeof(dm_pdev_t), KM_SLEEP)) == NULL) |
152 | return NULL; |
153 | |
154 | strlcpy(dmp->name, name, MAX_DEV_NAME); |
155 | |
156 | dmp->ref_cnt = 0; |
157 | dmp->pdev_vnode = NULL; |
158 | |
159 | return dmp; |
160 | } |
161 | /* |
162 | * Destroy allocated dm_pdev. |
163 | */ |
164 | static int |
165 | dm_pdev_rem(dm_pdev_t * dmp) |
166 | { |
167 | int err; |
168 | |
169 | KASSERT(dmp != NULL); |
170 | |
171 | if (dmp->pdev_vnode != NULL) { |
172 | err = vn_close(dmp->pdev_vnode, FREAD | FWRITE, FSCRED); |
173 | if (err != 0) |
174 | return err; |
175 | } |
176 | kmem_free(dmp, sizeof(*dmp)); |
177 | dmp = NULL; |
178 | |
179 | return 0; |
180 | } |
181 | /* |
182 | * Destroy all existing pdev's in device-mapper. |
183 | */ |
184 | int |
185 | dm_pdev_destroy(void) |
186 | { |
187 | dm_pdev_t *dm_pdev; |
188 | |
189 | mutex_enter(&dm_pdev_mutex); |
190 | while (!SLIST_EMPTY(&dm_pdev_list)) { /* List Deletion. */ |
191 | |
192 | dm_pdev = SLIST_FIRST(&dm_pdev_list); |
193 | |
194 | SLIST_REMOVE_HEAD(&dm_pdev_list, next_pdev); |
195 | |
196 | dm_pdev_rem(dm_pdev); |
197 | } |
198 | mutex_exit(&dm_pdev_mutex); |
199 | |
200 | mutex_destroy(&dm_pdev_mutex); |
201 | return 0; |
202 | } |
203 | /* |
204 | * This funcion is called from dm_dev_remove_ioctl. |
205 | * When I'm removing device from list, I have to decrement |
206 | * reference counter. If reference counter is 0 I will remove |
207 | * dmp from global list and from device list to. And I will CLOSE |
208 | * dmp vnode too. |
209 | */ |
210 | |
211 | /* |
212 | * Decrement pdev reference counter if 0 remove it. |
213 | */ |
214 | int |
215 | dm_pdev_decr(dm_pdev_t * dmp) |
216 | { |
217 | KASSERT(dmp != NULL); |
218 | /* |
219 | * If this was last reference remove dmp from |
220 | * global list also. |
221 | */ |
222 | mutex_enter(&dm_pdev_mutex); |
223 | |
224 | if (--dmp->ref_cnt == 0) { |
225 | SLIST_REMOVE(&dm_pdev_list, dmp, dm_pdev, next_pdev); |
226 | mutex_exit(&dm_pdev_mutex); |
227 | dm_pdev_rem(dmp); |
228 | return 0; |
229 | } |
230 | mutex_exit(&dm_pdev_mutex); |
231 | return 0; |
232 | } |
233 | /*static int |
234 | dm_pdev_dump_list(void) |
235 | { |
236 | dm_pdev_t *dmp; |
237 | |
238 | aprint_verbose("Dumping dm_pdev_list \n"); |
239 | |
240 | SLIST_FOREACH(dmp, &dm_pdev_list, next_pdev) { |
241 | aprint_verbose("dm_pdev_name %s ref_cnt %d list_rf_cnt %d\n", |
242 | dmp->name, dmp->ref_cnt, dmp->list_ref_cnt); |
243 | } |
244 | |
245 | return 0; |
246 | |
247 | }*/ |
248 | |