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

Last change on this file since d2da1be was d2da1be, checked in by Miroslav Cimerman <mc@…>, 4 months ago

hr: rename vol→state_changed → vol→state_dirty

  • Property mode set to 100644
File size: 9.8 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 <stdatomic.h>
44#include <stdlib.h>
45#include <stdio.h>
46#include <str_error.h>
47
48#include "util.h"
49#include "var.h"
50
51#define HR_RL_LIST_LOCK(vol) (fibril_mutex_lock(&vol->range_lock_list_lock))
52#define HR_RL_LIST_UNLOCK(vol) \
53 (fibril_mutex_unlock(&vol->range_lock_list_lock))
54
55static bool hr_range_lock_overlap(hr_range_lock_t *, hr_range_lock_t *);
56
57extern loc_srv_t *hr_srv;
58
59errno_t hr_init_devs(hr_volume_t *vol)
60{
61 HR_DEBUG("hr_init_devs()\n");
62
63 errno_t rc;
64 size_t i;
65 hr_extent_t *extent;
66
67 for (i = 0; i < vol->extent_no; i++) {
68 extent = &vol->extents[i];
69 if (extent->svc_id == 0) {
70 extent->status = HR_EXT_MISSING;
71 continue;
72 }
73
74 HR_DEBUG("hr_init_devs(): block_init() on (%lu)\n",
75 extent->svc_id);
76 rc = block_init(extent->svc_id);
77 extent->status = HR_EXT_ONLINE;
78
79 if (rc != EOK) {
80 HR_ERROR("hr_init_devs(): initing (%lu) failed, "
81 "aborting\n", extent->svc_id);
82 break;
83 }
84 }
85
86 return rc;
87}
88
89void hr_fini_devs(hr_volume_t *vol)
90{
91 HR_DEBUG("hr_fini_devs()\n");
92
93 size_t i;
94
95 for (i = 0; i < vol->extent_no; i++) {
96 if (vol->extents[i].svc_id != 0) {
97 HR_DEBUG("hr_fini_devs(): block_fini() on (%lu)\n",
98 vol->extents[i].svc_id);
99 block_fini(vol->extents[i].svc_id);
100 }
101 }
102}
103
104errno_t hr_register_volume(hr_volume_t *vol)
105{
106 HR_DEBUG("hr_register_volume()\n");
107
108 errno_t rc;
109 service_id_t new_id;
110 category_id_t cat_id;
111 char *fullname = NULL;
112 char *devname = vol->devname;
113
114 if (asprintf(&fullname, "devices/%s", devname) < 0)
115 return ENOMEM;
116
117 rc = loc_service_register(hr_srv, fullname, &new_id);
118 if (rc != EOK) {
119 HR_ERROR("unable to register device \"%s\": %s\n",
120 fullname, str_error(rc));
121 goto error;
122 }
123
124 rc = loc_category_get_id("raid", &cat_id, IPC_FLAG_BLOCKING);
125 if (rc != EOK) {
126 HR_ERROR("failed resolving category \"raid\": %s\n",
127 str_error(rc));
128 goto error;
129 }
130
131 rc = loc_service_add_to_cat(hr_srv, new_id, cat_id);
132 if (rc != EOK) {
133 HR_ERROR("failed adding \"%s\" to category \"raid\": %s\n",
134 fullname, str_error(rc));
135 goto error;
136 }
137
138 vol->svc_id = new_id;
139error:
140 free(fullname);
141 return rc;
142}
143
144errno_t hr_check_devs(hr_volume_t *vol, uint64_t *rblkno, size_t *rbsize)
145{
146 HR_DEBUG("hr_check_devs()\n");
147
148 errno_t rc;
149 size_t i, bsize;
150 uint64_t nblocks;
151 size_t last_bsize = 0;
152 uint64_t last_nblocks = 0;
153 uint64_t total_blocks = 0;
154 hr_extent_t *extent;
155
156 for (i = 0; i < vol->extent_no; i++) {
157 extent = &vol->extents[i];
158 if (extent->status == HR_EXT_MISSING)
159 continue;
160 rc = block_get_nblocks(extent->svc_id, &nblocks);
161 if (rc != EOK)
162 goto error;
163 if (last_nblocks != 0 && nblocks != last_nblocks) {
164 HR_ERROR("number of blocks differs\n");
165 rc = EINVAL;
166 goto error;
167 }
168
169 total_blocks += nblocks;
170 last_nblocks = nblocks;
171 }
172
173 for (i = 0; i < vol->extent_no; i++) {
174 extent = &vol->extents[i];
175 if (extent->status == HR_EXT_MISSING)
176 continue;
177 rc = block_get_bsize(extent->svc_id, &bsize);
178 if (rc != EOK)
179 goto error;
180 if (last_bsize != 0 && bsize != last_bsize) {
181 HR_ERROR("block sizes differ\n");
182 rc = EINVAL;
183 goto error;
184 }
185
186 last_bsize = bsize;
187 }
188
189 if ((bsize % 512) != 0) {
190 HR_ERROR("block size not multiple of 512\n");
191 return EINVAL;
192 }
193
194 if (rblkno != NULL)
195 *rblkno = total_blocks;
196 if (rbsize != NULL)
197 *rbsize = bsize;
198error:
199 return rc;
200}
201
202errno_t hr_check_ba_range(hr_volume_t *vol, size_t cnt, uint64_t ba)
203{
204 if (ba + cnt > vol->data_blkno)
205 return ERANGE;
206 return EOK;
207}
208
209void hr_add_ba_offset(hr_volume_t *vol, uint64_t *ba)
210{
211 *ba = *ba + vol->data_offset;
212}
213
214void hr_update_ext_status(hr_volume_t *vol, size_t extent, hr_ext_status_t s)
215{
216 if (vol->level != HR_LVL_0)
217 assert(fibril_rwlock_is_locked(&vol->extents_lock));
218
219 assert(fibril_rwlock_is_write_locked(&vol->states_lock));
220
221 assert(extent < vol->extent_no);
222
223 hr_ext_status_t old = vol->extents[extent].status;
224 HR_WARN("\"%s\": changing extent %lu state: %s -> %s\n",
225 vol->devname, extent, hr_get_ext_status_msg(old),
226 hr_get_ext_status_msg(s));
227 vol->extents[extent].status = s;
228}
229
230void hr_update_hotspare_status(hr_volume_t *vol, size_t hs, hr_ext_status_t s)
231{
232 assert(fibril_mutex_is_locked(&vol->hotspare_lock));
233
234 assert(hs < vol->hotspare_no);
235
236 hr_ext_status_t old = vol->hotspares[hs].status;
237 HR_WARN("\"%s\": changing hotspare %lu state: %s -> %s\n",
238 vol->devname, hs, hr_get_ext_status_msg(old),
239 hr_get_ext_status_msg(s));
240 vol->hotspares[hs].status = s;
241}
242
243void hr_update_vol_status(hr_volume_t *vol, hr_vol_status_t new)
244{
245 assert(fibril_rwlock_is_write_locked(&vol->states_lock));
246
247 HR_WARN("\"%s\": changing volume state: %s -> %s\n", vol->devname,
248 hr_get_vol_status_msg(vol->status), hr_get_vol_status_msg(new));
249 vol->status = new;
250}
251
252void hr_update_ext_svc_id(hr_volume_t *vol, size_t extent, service_id_t new)
253{
254 if (vol->level != HR_LVL_0)
255 assert(fibril_rwlock_is_write_locked(&vol->extents_lock));
256
257 assert(extent < vol->extent_no);
258
259 service_id_t old = vol->extents[extent].svc_id;
260 HR_WARN("\"%s\": changing extent no. %lu svc_id: (%lu) -> (%lu)\n",
261 vol->devname, extent, old, new);
262 vol->extents[extent].svc_id = new;
263}
264
265void hr_update_hotspare_svc_id(hr_volume_t *vol, size_t hs, service_id_t new)
266{
267 assert(fibril_mutex_is_locked(&vol->hotspare_lock));
268
269 assert(hs < vol->hotspare_no);
270
271 service_id_t old = vol->hotspares[hs].svc_id;
272 HR_WARN("\"%s\": changing hotspare no. %lu svc_id: (%lu) -> (%lu)\n",
273 vol->devname, hs, old, new);
274 vol->hotspares[hs].svc_id = new;
275}
276
277/*
278 * Do a whole sync (ba = 0, cnt = 0) across all extents,
279 * and update extent status. *For now*, the caller has to
280 * update volume status after the syncs.
281 *
282 * TODO: add update_vol_status fcn ptr for each raid
283 */
284void hr_sync_all_extents(hr_volume_t *vol)
285{
286 errno_t rc;
287
288 fibril_mutex_lock(&vol->lock);
289 for (size_t i = 0; i < vol->extent_no; i++) {
290 if (vol->extents[i].status != HR_EXT_ONLINE)
291 continue;
292 rc = block_sync_cache(vol->extents[i].svc_id, 0, 0);
293 if (rc == ENOMEM || rc == ENOTSUP)
294 continue;
295 if (rc != EOK) {
296 if (rc == ENOENT)
297 hr_update_ext_status(vol, i, HR_EXT_MISSING);
298 else if (rc != EOK)
299 hr_update_ext_status(vol, i, HR_EXT_FAILED);
300 }
301 }
302 fibril_mutex_unlock(&vol->lock);
303}
304
305size_t hr_count_extents(hr_volume_t *vol, hr_ext_status_t status)
306{
307 if (vol->level != HR_LVL_0)
308 assert(fibril_rwlock_is_locked(&vol->extents_lock));
309 assert(fibril_rwlock_is_locked(&vol->states_lock));
310
311 size_t count = 0;
312 for (size_t i = 0; i < vol->extent_no; i++)
313 if (vol->extents[i].status == status)
314 count++;
315
316 return count;
317}
318
319hr_range_lock_t *hr_range_lock_acquire(hr_volume_t *vol, uint64_t ba,
320 uint64_t cnt)
321{
322 hr_range_lock_t *rl = malloc(sizeof(hr_range_lock_t));
323 if (rl == NULL)
324 return NULL;
325
326 rl->vol = vol;
327 rl->off = ba;
328 rl->len = cnt;
329
330 rl->pending = 1;
331 rl->ignore = false;
332
333 link_initialize(&rl->link);
334 fibril_mutex_initialize(&rl->lock);
335
336 fibril_mutex_lock(&rl->lock);
337
338again:
339 HR_RL_LIST_LOCK(vol);
340 list_foreach(vol->range_lock_list, link, hr_range_lock_t, rlp) {
341 if (rlp->ignore)
342 continue;
343 if (hr_range_lock_overlap(rlp, rl)) {
344 rlp->pending++;
345
346 HR_RL_LIST_UNLOCK(vol);
347
348 fibril_mutex_lock(&rlp->lock);
349
350 HR_RL_LIST_LOCK(vol);
351
352 rlp->pending--;
353
354 /*
355 * when ignore is set, after HR_RL_LIST_UNLOCK(),
356 * noone new is going to be able to start sleeping
357 * on the ignored range lock, only already waiting
358 * IOs will come through here
359 */
360 rlp->ignore = true;
361
362 fibril_mutex_unlock(&rlp->lock);
363
364 if (rlp->pending == 0) {
365 list_remove(&rlp->link);
366 free(rlp);
367 }
368
369 HR_RL_LIST_UNLOCK(vol);
370 goto again;
371 }
372 }
373
374 list_append(&rl->link, &vol->range_lock_list);
375
376 HR_RL_LIST_UNLOCK(vol);
377 return rl;
378}
379
380void hr_range_lock_release(hr_range_lock_t *rl)
381{
382 if (rl == NULL)
383 return;
384
385 HR_RL_LIST_LOCK(rl->vol);
386
387 rl->pending--;
388
389 fibril_mutex_unlock(&rl->lock);
390
391 if (rl->pending == 0) {
392 list_remove(&rl->link);
393 free(rl);
394 }
395
396 HR_RL_LIST_UNLOCK(rl->vol);
397}
398
399static bool hr_range_lock_overlap(hr_range_lock_t *rl1, hr_range_lock_t *rl2)
400{
401 uint64_t rl1_start = rl1->off;
402 uint64_t rl1_end = rl1->off + rl1->len - 1;
403 uint64_t rl2_start = rl2->off;
404 uint64_t rl2_end = rl2->off + rl2->len - 1;
405
406 /* one ends before the other starts */
407 if (rl1_end < rl2_start || rl2_end < rl1_start)
408 return false;
409
410 return true;
411}
412
413void hr_mark_vol_state_dirty(hr_volume_t *vol)
414{
415 atomic_store(&vol->state_dirty, true);
416}
417
418/** @}
419 */
Note: See TracBrowser for help on using the repository browser.