source: mainline/uspace/srv/bd/hr/util.c@ 401b9e42

Last change on this file since 401b9e42 was edc89bd8, checked in by Miroslav Cimerman <mc@…>, 5 months ago

hr: util: add hr_update_{ext,hotspare}_svc_id()

Provides easy way to assert write-ownership of needed
locks.

  • Property mode set to 100644
File size: 9.7 KB
Line 
1/*
2 * Copyright (c) 2025 Miroslav Cimerman
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup hr
30 * @{
31 */
32/**
33 * @file
34 */
35
36#include <adt/list.h>
37#include <block.h>
38#include <errno.h>
39#include <fibril_synch.h>
40#include <hr.h>
41#include <io/log.h>
42#include <loc.h>
43#include <stdlib.h>
44#include <stdio.h>
45#include <str_error.h>
46
47#include "util.h"
48#include "var.h"
49
50#define HR_RL_LIST_LOCK(vol) (fibril_mutex_lock(&vol->range_lock_list_lock))
51#define HR_RL_LIST_UNLOCK(vol) \
52 (fibril_mutex_unlock(&vol->range_lock_list_lock))
53
54static bool hr_range_lock_overlap(hr_range_lock_t *, hr_range_lock_t *);
55
56extern loc_srv_t *hr_srv;
57
58errno_t hr_init_devs(hr_volume_t *vol)
59{
60 HR_DEBUG("hr_init_devs()\n");
61
62 errno_t rc;
63 size_t i;
64 hr_extent_t *extent;
65
66 for (i = 0; i < vol->extent_no; i++) {
67 extent = &vol->extents[i];
68 if (extent->svc_id == 0) {
69 extent->status = HR_EXT_MISSING;
70 continue;
71 }
72
73 HR_DEBUG("hr_init_devs(): block_init() on (%lu)\n",
74 extent->svc_id);
75 rc = block_init(extent->svc_id);
76 extent->status = HR_EXT_ONLINE;
77
78 if (rc != EOK) {
79 HR_ERROR("hr_init_devs(): initing (%lu) failed, "
80 "aborting\n", extent->svc_id);
81 break;
82 }
83 }
84
85 return rc;
86}
87
88void hr_fini_devs(hr_volume_t *vol)
89{
90 HR_DEBUG("hr_fini_devs()\n");
91
92 size_t i;
93
94 for (i = 0; i < vol->extent_no; i++) {
95 if (vol->extents[i].svc_id != 0) {
96 HR_DEBUG("hr_fini_devs(): block_fini() on (%lu)\n",
97 vol->extents[i].svc_id);
98 block_fini(vol->extents[i].svc_id);
99 }
100 }
101}
102
103errno_t hr_register_volume(hr_volume_t *vol)
104{
105 HR_DEBUG("hr_register_volume()\n");
106
107 errno_t rc;
108 service_id_t new_id;
109 category_id_t cat_id;
110 char *fullname = NULL;
111 char *devname = vol->devname;
112
113 if (asprintf(&fullname, "devices/%s", devname) < 0)
114 return ENOMEM;
115
116 rc = loc_service_register(hr_srv, fullname, &new_id);
117 if (rc != EOK) {
118 HR_ERROR("unable to register device \"%s\": %s\n",
119 fullname, str_error(rc));
120 goto error;
121 }
122
123 rc = loc_category_get_id("raid", &cat_id, IPC_FLAG_BLOCKING);
124 if (rc != EOK) {
125 HR_ERROR("failed resolving category \"raid\": %s\n",
126 str_error(rc));
127 goto error;
128 }
129
130 rc = loc_service_add_to_cat(hr_srv, new_id, cat_id);
131 if (rc != EOK) {
132 HR_ERROR("failed adding \"%s\" to category \"raid\": %s\n",
133 fullname, str_error(rc));
134 goto error;
135 }
136
137 vol->svc_id = new_id;
138error:
139 free(fullname);
140 return rc;
141}
142
143errno_t hr_check_devs(hr_volume_t *vol, uint64_t *rblkno, size_t *rbsize)
144{
145 HR_DEBUG("hr_check_devs()\n");
146
147 errno_t rc;
148 size_t i, bsize;
149 uint64_t nblocks;
150 size_t last_bsize = 0;
151 uint64_t last_nblocks = 0;
152 uint64_t total_blocks = 0;
153 hr_extent_t *extent;
154
155 for (i = 0; i < vol->extent_no; i++) {
156 extent = &vol->extents[i];
157 if (extent->status == HR_EXT_MISSING)
158 continue;
159 rc = block_get_nblocks(extent->svc_id, &nblocks);
160 if (rc != EOK)
161 goto error;
162 if (last_nblocks != 0 && nblocks != last_nblocks) {
163 HR_ERROR("number of blocks differs\n");
164 rc = EINVAL;
165 goto error;
166 }
167
168 total_blocks += nblocks;
169 last_nblocks = nblocks;
170 }
171
172 for (i = 0; i < vol->extent_no; i++) {
173 extent = &vol->extents[i];
174 if (extent->status == HR_EXT_MISSING)
175 continue;
176 rc = block_get_bsize(extent->svc_id, &bsize);
177 if (rc != EOK)
178 goto error;
179 if (last_bsize != 0 && bsize != last_bsize) {
180 HR_ERROR("block sizes differ\n");
181 rc = EINVAL;
182 goto error;
183 }
184
185 last_bsize = bsize;
186 }
187
188 if ((bsize % 512) != 0) {
189 HR_ERROR("block size not multiple of 512\n");
190 return EINVAL;
191 }
192
193 if (rblkno != NULL)
194 *rblkno = total_blocks;
195 if (rbsize != NULL)
196 *rbsize = bsize;
197error:
198 return rc;
199}
200
201errno_t hr_check_ba_range(hr_volume_t *vol, size_t cnt, uint64_t ba)
202{
203 if (ba + cnt > vol->data_blkno)
204 return ERANGE;
205 return EOK;
206}
207
208void hr_add_ba_offset(hr_volume_t *vol, uint64_t *ba)
209{
210 *ba = *ba + vol->data_offset;
211}
212
213void hr_update_ext_status(hr_volume_t *vol, size_t extent, hr_ext_status_t s)
214{
215 if (vol->level != HR_LVL_0)
216 assert(fibril_rwlock_is_locked(&vol->extents_lock));
217
218 assert(fibril_rwlock_is_write_locked(&vol->states_lock));
219
220 assert(extent < vol->extent_no);
221
222 hr_ext_status_t old = vol->extents[extent].status;
223 HR_WARN("\"%s\": changing extent %lu state: %s -> %s\n",
224 vol->devname, extent, hr_get_ext_status_msg(old),
225 hr_get_ext_status_msg(s));
226 vol->extents[extent].status = s;
227}
228
229void hr_update_hotspare_status(hr_volume_t *vol, size_t hs, hr_ext_status_t s)
230{
231 assert(fibril_mutex_is_locked(&vol->hotspare_lock));
232
233 assert(hs < vol->hotspare_no);
234
235 hr_ext_status_t old = vol->hotspares[hs].status;
236 HR_WARN("\"%s\": changing hotspare %lu state: %s -> %s\n",
237 vol->devname, hs, hr_get_ext_status_msg(old),
238 hr_get_ext_status_msg(s));
239 vol->hotspares[hs].status = s;
240}
241
242void hr_update_vol_status(hr_volume_t *vol, hr_vol_status_t new)
243{
244 assert(fibril_rwlock_is_write_locked(&vol->states_lock));
245
246 HR_WARN("\"%s\": changing volume state: %s -> %s\n", vol->devname,
247 hr_get_vol_status_msg(vol->status), hr_get_vol_status_msg(new));
248 vol->status = new;
249}
250
251void hr_update_ext_svc_id(hr_volume_t *vol, size_t extent, service_id_t new)
252{
253 if (vol->level != HR_LVL_0)
254 assert(fibril_rwlock_is_write_locked(&vol->extents_lock));
255
256 assert(extent < vol->extent_no);
257
258 service_id_t old = vol->extents[extent].svc_id;
259 HR_WARN("\"%s\": changing extent no. %lu svc_id: (%lu) -> (%lu)\n",
260 vol->devname, extent, old, new);
261 vol->extents[extent].svc_id = new;
262}
263
264void hr_update_hotspare_svc_id(hr_volume_t *vol, size_t hs, service_id_t new)
265{
266 assert(fibril_mutex_is_locked(&vol->hotspare_lock));
267
268 assert(hs < vol->hotspare_no);
269
270 service_id_t old = vol->hotspares[hs].svc_id;
271 HR_WARN("\"%s\": changing hotspare no. %lu svc_id: (%lu) -> (%lu)\n",
272 vol->devname, hs, old, new);
273 vol->hotspares[hs].svc_id = new;
274}
275
276/*
277 * Do a whole sync (ba = 0, cnt = 0) across all extents,
278 * and update extent status. *For now*, the caller has to
279 * update volume status after the syncs.
280 *
281 * TODO: add update_vol_status fcn ptr for each raid
282 */
283void hr_sync_all_extents(hr_volume_t *vol)
284{
285 errno_t rc;
286
287 fibril_mutex_lock(&vol->lock);
288 for (size_t i = 0; i < vol->extent_no; i++) {
289 if (vol->extents[i].status != HR_EXT_ONLINE)
290 continue;
291 rc = block_sync_cache(vol->extents[i].svc_id, 0, 0);
292 if (rc == ENOMEM || rc == ENOTSUP)
293 continue;
294 if (rc != EOK) {
295 if (rc == ENOENT)
296 hr_update_ext_status(vol, i, HR_EXT_MISSING);
297 else if (rc != EOK)
298 hr_update_ext_status(vol, i, HR_EXT_FAILED);
299 }
300 }
301 fibril_mutex_unlock(&vol->lock);
302}
303
304size_t hr_count_extents(hr_volume_t *vol, hr_ext_status_t status)
305{
306 if (vol->level != HR_LVL_0)
307 assert(fibril_rwlock_is_locked(&vol->extents_lock));
308 assert(fibril_rwlock_is_locked(&vol->states_lock));
309
310 size_t count = 0;
311 for (size_t i = 0; i < vol->extent_no; i++)
312 if (vol->extents[i].status == status)
313 count++;
314
315 return count;
316}
317
318hr_range_lock_t *hr_range_lock_acquire(hr_volume_t *vol, uint64_t ba,
319 uint64_t cnt)
320{
321 hr_range_lock_t *rl = malloc(sizeof(hr_range_lock_t));
322 if (rl == NULL)
323 return NULL;
324
325 rl->vol = vol;
326 rl->off = ba;
327 rl->len = cnt;
328
329 rl->pending = 1;
330 rl->ignore = false;
331
332 link_initialize(&rl->link);
333 fibril_mutex_initialize(&rl->lock);
334
335 fibril_mutex_lock(&rl->lock);
336
337again:
338 HR_RL_LIST_LOCK(vol);
339 list_foreach(vol->range_lock_list, link, hr_range_lock_t, rlp) {
340 if (rlp->ignore)
341 continue;
342 if (hr_range_lock_overlap(rlp, rl)) {
343 rlp->pending++;
344
345 HR_RL_LIST_UNLOCK(vol);
346
347 fibril_mutex_lock(&rlp->lock);
348
349 HR_RL_LIST_LOCK(vol);
350
351 rlp->pending--;
352
353 /*
354 * when ignore is set, after HR_RL_LIST_UNLOCK(),
355 * noone new is going to be able to start sleeping
356 * on the ignored range lock, only already waiting
357 * IOs will come through here
358 */
359 rlp->ignore = true;
360
361 fibril_mutex_unlock(&rlp->lock);
362
363 if (rlp->pending == 0) {
364 list_remove(&rlp->link);
365 free(rlp);
366 }
367
368 HR_RL_LIST_UNLOCK(vol);
369 goto again;
370 }
371 }
372
373 list_append(&rl->link, &vol->range_lock_list);
374
375 HR_RL_LIST_UNLOCK(vol);
376 return rl;
377}
378
379void hr_range_lock_release(hr_range_lock_t *rl)
380{
381 if (rl == NULL)
382 return;
383
384 HR_RL_LIST_LOCK(rl->vol);
385
386 rl->pending--;
387
388 fibril_mutex_unlock(&rl->lock);
389
390 if (rl->pending == 0) {
391 list_remove(&rl->link);
392 free(rl);
393 }
394
395 HR_RL_LIST_UNLOCK(rl->vol);
396}
397
398static bool hr_range_lock_overlap(hr_range_lock_t *rl1, hr_range_lock_t *rl2)
399{
400 uint64_t rl1_start = rl1->off;
401 uint64_t rl1_end = rl1->off + rl1->len - 1;
402 uint64_t rl2_start = rl2->off;
403 uint64_t rl2_end = rl2->off + rl2->len - 1;
404
405 /* one ends before the other starts */
406 if (rl1_end < rl2_start || rl2_end < rl1_start)
407 return false;
408
409 return true;
410}
411
412/** @}
413 */
Note: See TracBrowser for help on using the repository browser.