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
RevLine 
[da5c257]1/*
[36661772]2 * Copyright (c) 2025 Miroslav Cimerman
[da5c257]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
[38e3c0a7]36#include <adt/list.h>
[da5c257]37#include <block.h>
38#include <errno.h>
[38e3c0a7]39#include <fibril_synch.h>
[da5c257]40#include <hr.h>
41#include <io/log.h>
42#include <loc.h>
[d6fe2a1]43#include <stdatomic.h>
[44ea48e]44#include <stdlib.h>
45#include <stdio.h>
[da5c257]46#include <str_error.h>
47
48#include "util.h"
[b0f1366]49#include "var.h"
[da5c257]50
[38e3c0a7]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
[da5c257]57extern loc_srv_t *hr_srv;
58
59errno_t hr_init_devs(hr_volume_t *vol)
60{
[d199a6f]61 HR_DEBUG("hr_init_devs()\n");
[da5c257]62
63 errno_t rc;
64 size_t i;
[5d96f427]65 hr_extent_t *extent;
[da5c257]66
[65706f1]67 for (i = 0; i < vol->extent_no; i++) {
[5d96f427]68 extent = &vol->extents[i];
69 if (extent->svc_id == 0) {
70 extent->status = HR_EXT_MISSING;
[e47a032]71 continue;
72 }
[5d96f427]73
[d199a6f]74 HR_DEBUG("hr_init_devs(): block_init() on (%lu)\n",
[5d96f427]75 extent->svc_id);
76 rc = block_init(extent->svc_id);
77 extent->status = HR_EXT_ONLINE;
78
[da5c257]79 if (rc != EOK) {
[d199a6f]80 HR_ERROR("hr_init_devs(): initing (%lu) failed, "
[5d96f427]81 "aborting\n", extent->svc_id);
[da5c257]82 break;
83 }
84 }
85
86 return rc;
87}
88
89void hr_fini_devs(hr_volume_t *vol)
90{
[d199a6f]91 HR_DEBUG("hr_fini_devs()\n");
[da5c257]92
93 size_t i;
94
[65706f1]95 for (i = 0; i < vol->extent_no; i++) {
[36661772]96 if (vol->extents[i].svc_id != 0) {
[d199a6f]97 HR_DEBUG("hr_fini_devs(): block_fini() on (%lu)\n",
[5d96f427]98 vol->extents[i].svc_id);
[e47a032]99 block_fini(vol->extents[i].svc_id);
[5d96f427]100 }
101 }
[da5c257]102}
103
[5d96f427]104errno_t hr_register_volume(hr_volume_t *vol)
[da5c257]105{
[d199a6f]106 HR_DEBUG("hr_register_volume()\n");
[b0f1366]107
[da5c257]108 errno_t rc;
109 service_id_t new_id;
110 category_id_t cat_id;
[44ea48e]111 char *fullname = NULL;
[5d96f427]112 char *devname = vol->devname;
[da5c257]113
[5d96f427]114 if (asprintf(&fullname, "devices/%s", devname) < 0)
[44ea48e]115 return ENOMEM;
116
117 rc = loc_service_register(hr_srv, fullname, &new_id);
[da5c257]118 if (rc != EOK) {
[d199a6f]119 HR_ERROR("unable to register device \"%s\": %s\n",
[5d96f427]120 fullname, str_error(rc));
[da5c257]121 goto error;
122 }
123
124 rc = loc_category_get_id("raid", &cat_id, IPC_FLAG_BLOCKING);
125 if (rc != EOK) {
[d199a6f]126 HR_ERROR("failed resolving category \"raid\": %s\n",
[5d96f427]127 str_error(rc));
[da5c257]128 goto error;
129 }
130
131 rc = loc_service_add_to_cat(hr_srv, new_id, cat_id);
132 if (rc != EOK) {
[d199a6f]133 HR_ERROR("failed adding \"%s\" to category \"raid\": %s\n",
[5d96f427]134 fullname, str_error(rc));
[da5c257]135 goto error;
136 }
137
[5d96f427]138 vol->svc_id = new_id;
[da5c257]139error:
[44ea48e]140 free(fullname);
[da5c257]141 return rc;
142}
143
[6b8e89b0]144errno_t hr_check_devs(hr_volume_t *vol, uint64_t *rblkno, size_t *rbsize)
[b0f1366]145{
[d199a6f]146 HR_DEBUG("hr_check_devs()\n");
[b0f1366]147
148 errno_t rc;
[e47a032]149 size_t i, bsize;
150 uint64_t nblocks;
151 size_t last_bsize = 0;
152 uint64_t last_nblocks = 0;
[b0f1366]153 uint64_t total_blocks = 0;
[5d96f427]154 hr_extent_t *extent;
[b0f1366]155
[65706f1]156 for (i = 0; i < vol->extent_no; i++) {
[5d96f427]157 extent = &vol->extents[i];
158 if (extent->status == HR_EXT_MISSING)
[e47a032]159 continue;
[5d96f427]160 rc = block_get_nblocks(extent->svc_id, &nblocks);
[b0f1366]161 if (rc != EOK)
162 goto error;
[e47a032]163 if (last_nblocks != 0 && nblocks != last_nblocks) {
[d199a6f]164 HR_ERROR("number of blocks differs\n");
[b0f1366]165 rc = EINVAL;
166 goto error;
167 }
[5d96f427]168
[b0f1366]169 total_blocks += nblocks;
170 last_nblocks = nblocks;
171 }
172
[65706f1]173 for (i = 0; i < vol->extent_no; i++) {
[5d96f427]174 extent = &vol->extents[i];
175 if (extent->status == HR_EXT_MISSING)
[e47a032]176 continue;
[5d96f427]177 rc = block_get_bsize(extent->svc_id, &bsize);
[b0f1366]178 if (rc != EOK)
179 goto error;
[e47a032]180 if (last_bsize != 0 && bsize != last_bsize) {
[d199a6f]181 HR_ERROR("block sizes differ\n");
[b0f1366]182 rc = EINVAL;
183 goto error;
184 }
[5d96f427]185
[b0f1366]186 last_bsize = bsize;
187 }
188
[1cfa162]189 if ((bsize % 512) != 0) {
[d199a6f]190 HR_ERROR("block size not multiple of 512\n");
[1cfa162]191 return EINVAL;
192 }
193
[6b8e89b0]194 if (rblkno != NULL)
195 *rblkno = total_blocks;
196 if (rbsize != NULL)
197 *rbsize = bsize;
[b0f1366]198error:
199 return rc;
200}
201
[4a2a6b8b]202errno_t hr_check_ba_range(hr_volume_t *vol, size_t cnt, uint64_t ba)
[b0f1366]203{
[4a2a6b8b]204 if (ba + cnt > vol->data_blkno)
[b0f1366]205 return ERANGE;
[4a2a6b8b]206 return EOK;
207}
[b0f1366]208
[4a2a6b8b]209void hr_add_ba_offset(hr_volume_t *vol, uint64_t *ba)
210{
[b0f1366]211 *ba = *ba + vol->data_offset;
212}
213
[a0c3080]214void hr_update_ext_status(hr_volume_t *vol, size_t extent, hr_ext_status_t s)
[e47a032]215{
[36661772]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
[7a3529a8]221 assert(extent < vol->extent_no);
222
[a0c3080]223 hr_ext_status_t old = vol->extents[extent].status;
[37ffa4d]224 HR_WARN("\"%s\": changing extent %lu state: %s -> %s\n",
[a0c3080]225 vol->devname, extent, hr_get_ext_status_msg(old),
226 hr_get_ext_status_msg(s));
[e47a032]227 vol->extents[extent].status = s;
228}
229
[a0c3080]230void hr_update_hotspare_status(hr_volume_t *vol, size_t hs, hr_ext_status_t s)
231{
[36661772]232 assert(fibril_mutex_is_locked(&vol->hotspare_lock));
233
[7a3529a8]234 assert(hs < vol->hotspare_no);
235
[a0c3080]236 hr_ext_status_t old = vol->hotspares[hs].status;
[37ffa4d]237 HR_WARN("\"%s\": changing hotspare %lu state: %s -> %s\n",
[a0c3080]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
[edc89bd8]243void hr_update_vol_status(hr_volume_t *vol, hr_vol_status_t new)
[a0c3080]244{
[36661772]245 assert(fibril_rwlock_is_write_locked(&vol->states_lock));
246
[37ffa4d]247 HR_WARN("\"%s\": changing volume state: %s -> %s\n", vol->devname,
[edc89bd8]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;
[a0c3080]275}
276
[52af125]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);
[65706f1]289 for (size_t i = 0; i < vol->extent_no; i++) {
[52af125]290 if (vol->extents[i].status != HR_EXT_ONLINE)
291 continue;
292 rc = block_sync_cache(vol->extents[i].svc_id, 0, 0);
[36661772]293 if (rc == ENOMEM || rc == ENOTSUP)
294 continue;
295 if (rc != EOK) {
[52af125]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
[e76e12d8]305size_t hr_count_extents(hr_volume_t *vol, hr_ext_status_t status)
306{
[36661772]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
[e76e12d8]311 size_t count = 0;
[38e3c0a7]312 for (size_t i = 0; i < vol->extent_no; i++)
[e76e12d8]313 if (vol->extents[i].status == status)
314 count++;
315
316 return count;
317}
318
[38e3c0a7]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{
[36661772]382 if (rl == NULL)
383 return;
384
[38e3c0a7]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
[d6fe2a1]413void hr_mark_vol_state_dirty(hr_volume_t *vol)
414{
[d2da1be]415 atomic_store(&vol->state_dirty, true);
[d6fe2a1]416}
417
[da5c257]418/** @}
419 */
Note: See TracBrowser for help on using the repository browser.