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

Last change on this file since 2958e70 was e494d7b, checked in by Miroslav Cimerman <mc@…>, 8 months ago

hr: initialize hotspare states to MISSING

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