source: mainline/uspace/srv/bd/hr/hr.c@ 78433bb

Last change on this file since 78433bb was 78433bb, checked in by Miroslav Cimerman <mc@…>, 7 weeks ago

hr: allocate in-memory metadata in format code

This will allow some formats to store in-memory
copy of metadata for each extent.

  • Property mode set to 100644
File size: 14.1 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 hr.c
34 * @brief HelenRAID server methods.
35 */
36
37#include <adt/list.h>
38#include <async.h>
39#include <bd_srv.h>
40#include <errno.h>
41#include <hr.h>
42#include <io/log.h>
43#include <ipc/hr.h>
44#include <ipc/services.h>
45#include <loc.h>
46#include <task.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <str.h>
50#include <str_error.h>
51#include <block.h>
52
53#include "util.h"
54#include "var.h"
55
56static void hr_assemble_srv(ipc_call_t *);
57static void hr_auto_assemble_srv(ipc_call_t *);
58static void hr_stop_srv(ipc_call_t *);
59static void hr_stop_all_srv(ipc_call_t *);
60static void hr_add_hotspare_srv(ipc_call_t *);
61static void hr_get_vol_states_srv(ipc_call_t *);
62static void hr_ctl_conn(ipc_call_t *);
63static void hr_client_conn(ipc_call_t *, void *);
64
65loc_srv_t *hr_srv;
66list_t hr_volumes;
67fibril_rwlock_t hr_volumes_lock;
68
69static service_id_t ctl_sid;
70
71/** Volume creation (server).
72 *
73 * Creates HelenRAID volume from parameters and
74 * devices specified in hr_config_t.
75 *
76 * @param icall hr_config_t
77 */
78static void hr_create_srv(ipc_call_t *icall)
79{
80 HR_DEBUG("%s()", __func__);
81
82 errno_t rc;
83 size_t i, size;
84 hr_config_t *cfg;
85 hr_volume_t *vol;
86 ipc_call_t call;
87
88 if (!async_data_write_receive(&call, &size)) {
89 async_answer_0(&call, EREFUSED);
90 async_answer_0(icall, EREFUSED);
91 return;
92 }
93
94 if (size != sizeof(hr_config_t)) {
95 async_answer_0(&call, EINVAL);
96 async_answer_0(icall, EINVAL);
97 return;
98 }
99
100 cfg = calloc(1, sizeof(hr_config_t));
101 if (cfg == NULL) {
102 async_answer_0(&call, ENOMEM);
103 async_answer_0(icall, ENOMEM);
104 return;
105 }
106
107 rc = async_data_write_finalize(&call, cfg, size);
108 if (rc != EOK) {
109 free(cfg);
110 async_answer_0(&call, rc);
111 async_answer_0(icall, rc);
112 return;
113 }
114
115 /*
116 * If there was a missing device provided
117 * for creation of a new volume, abort
118 */
119 for (i = 0; i < cfg->dev_no; i++) {
120 if (cfg->devs[i] == 0) {
121 /*
122 * XXX: own error codes, no need to log this...
123 * its user error not service error
124 */
125 HR_ERROR("missing device provided for volume "
126 "creation, aborting");
127 free(cfg);
128 async_answer_0(icall, EINVAL);
129 return;
130 }
131 }
132
133 rc = hr_create_vol_struct(&vol, cfg->level, cfg->devname,
134 HR_METADATA_NATIVE);
135 if (rc != EOK) {
136 free(cfg);
137 async_answer_0(icall, rc);
138 return;
139 }
140
141 rc = hr_init_extents_from_cfg(vol, cfg);
142 if (rc != EOK)
143 goto error;
144
145 vol->hr_ops.init(vol);
146 if (rc != EOK)
147 goto error;
148
149 rc = vol->meta_ops->init_vol2meta(vol);
150 if (rc != EOK)
151 goto error;
152
153 rc = vol->hr_ops.create(vol);
154 if (rc != EOK)
155 goto error;
156
157 vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
158
159 rc = hr_register_volume(vol);
160 if (rc != EOK)
161 goto error;
162
163 fibril_rwlock_write_lock(&hr_volumes_lock);
164 list_append(&vol->lvolumes, &hr_volumes);
165 fibril_rwlock_write_unlock(&hr_volumes_lock);
166
167 HR_NOTE("created volume \"%s\" (%" PRIun ")\n", vol->devname,
168 vol->svc_id);
169
170 free(cfg);
171 async_answer_0(icall, rc);
172 return;
173error:
174 free(cfg);
175 hr_destroy_vol_struct(vol);
176 async_answer_0(icall, rc);
177}
178
179/** Manual volume assembly (server).
180 *
181 * Tries to assemble a volume from devices in hr_config_t and
182 * sends the number of successful volumes assembled back to the
183 * client.
184 *
185 * @param icall hr_config_t
186 */
187static void hr_assemble_srv(ipc_call_t *icall)
188{
189 HR_DEBUG("%s()", __func__);
190
191 errno_t rc;
192 size_t size, assembled_cnt;
193 hr_config_t *cfg;
194 ipc_call_t call;
195
196 if (!async_data_write_receive(&call, &size)) {
197 async_answer_0(&call, EREFUSED);
198 async_answer_0(icall, EREFUSED);
199 return;
200 }
201
202 if (size != sizeof(hr_config_t)) {
203 async_answer_0(&call, EINVAL);
204 async_answer_0(icall, EINVAL);
205 return;
206 }
207
208 cfg = calloc(1, sizeof(hr_config_t));
209 if (cfg == NULL) {
210 async_answer_0(&call, ENOMEM);
211 async_answer_0(icall, ENOMEM);
212 return;
213 }
214
215 rc = async_data_write_finalize(&call, cfg, size);
216 if (rc != EOK)
217 goto error;
218
219 if (!async_data_read_receive(&call, &size)) {
220 async_answer_0(icall, EREFUSED);
221 return;
222 }
223
224 if (size != sizeof(size_t)) {
225 async_answer_0(icall, EINVAL);
226 return;
227 }
228
229 rc = hr_util_try_assemble(cfg, &assembled_cnt);
230 if (rc != EOK)
231 goto error;
232
233 rc = async_data_read_finalize(&call, &assembled_cnt, size);
234 if (rc != EOK)
235 goto error;
236
237 free(cfg);
238 async_answer_0(icall, EOK);
239 return;
240error:
241 free(cfg);
242 async_answer_0(&call, rc);
243 async_answer_0(icall, rc);
244}
245
246/** Automatic volume assembly (server).
247 *
248 * Tries to assemble a volume from devices in disk location
249 * category and sends the number of successful volumes assembled
250 * back to client.
251 */
252static void hr_auto_assemble_srv(ipc_call_t *icall)
253{
254 HR_DEBUG("%s()", __func__);
255
256 errno_t rc;
257 size_t size;
258 size_t assembled_cnt = 0;
259 ipc_call_t call;
260
261 if (!async_data_read_receive(&call, &size)) {
262 async_answer_0(icall, EREFUSED);
263 return;
264 }
265
266 if (size != sizeof(size_t)) {
267 async_answer_0(&call, EINVAL);
268 async_answer_0(icall, EINVAL);
269 return;
270 }
271
272 rc = hr_util_try_assemble(NULL, &assembled_cnt);
273 if (rc != EOK)
274 goto error;
275
276 rc = async_data_read_finalize(&call, &assembled_cnt, size);
277 if (rc != EOK)
278 goto error;
279
280 async_answer_0(icall, EOK);
281 return;
282error:
283 async_answer_0(&call, rc);
284 async_answer_0(icall, rc);
285}
286
287/** Volume deactivation (server).
288 *
289 * Deactivates/detaches specified volume.
290 */
291static void hr_stop_srv(ipc_call_t *icall)
292{
293 HR_DEBUG("%s()", __func__);
294
295 errno_t rc = EOK;
296 service_id_t svc_id;
297
298 svc_id = ipc_get_arg1(icall);
299
300 rc = hr_remove_volume(svc_id);
301
302 async_answer_0(icall, rc);
303}
304
305/** Automatic volume deactivation (server).
306 *
307 * Tries to deactivate/detach all volumes.
308 */
309static void hr_stop_all_srv(ipc_call_t *icall)
310{
311 HR_DEBUG("%s()", __func__);
312
313 service_id_t *vol_svcs = NULL;
314 errno_t rc = EOK;
315 size_t i, vol_cnt;
316
317 rc = hr_get_volume_svcs(&vol_cnt, &vol_svcs);
318 if (rc != EOK)
319 goto fail;
320
321 for (i = 0; i < vol_cnt; i++)
322 (void)hr_remove_volume(vol_svcs[i]);
323
324fail:
325 if (vol_svcs != NULL)
326 free(vol_svcs);
327 async_answer_0(icall, rc);
328}
329
330/** Simulate volume extent failure (server).
331 *
332 * Changes the specified extent's state to FAULTY.
333 * Other extents' metadata are marked as dirty, therefore
334 * it effectively invalides the specified extent as well
335 * for further uses.
336 */
337static void hr_fail_extent_srv(ipc_call_t *icall)
338{
339 HR_DEBUG("%s()", __func__);
340
341 service_id_t svc_id;
342 size_t extent_idx_to_fail;
343 hr_volume_t *vol;
344
345 svc_id = (service_id_t)ipc_get_arg1(icall);
346 extent_idx_to_fail = (size_t)ipc_get_arg2(icall);
347
348 vol = hr_get_volume(svc_id);
349 if (vol == NULL) {
350 async_answer_0(icall, ENOENT);
351 return;
352 }
353
354 fibril_rwlock_read_lock(&vol->extents_lock);
355 fibril_rwlock_write_lock(&vol->states_lock);
356
357 hr_extent_t *ext = &vol->extents[extent_idx_to_fail];
358
359 switch (ext->state) {
360 case HR_EXT_NONE:
361 case HR_EXT_MISSING:
362 case HR_EXT_FAILED:
363 fibril_rwlock_write_unlock(&vol->states_lock);
364 fibril_rwlock_read_unlock(&vol->extents_lock);
365 async_answer_0(icall, EINVAL);
366 return;
367 default:
368 hr_update_ext_state(vol, extent_idx_to_fail, HR_EXT_FAILED);
369 (void)vol->meta_ops->erase_block(ext->svc_id);
370 hr_mark_vol_state_dirty(vol);
371 }
372
373 fibril_rwlock_write_unlock(&vol->states_lock);
374 fibril_rwlock_read_unlock(&vol->extents_lock);
375
376 vol->hr_ops.vol_state_eval(vol);
377
378 async_answer_0(icall, EOK);
379}
380
381/** Add hotspare to volume (server).
382 *
383 * Adds hotspare to a volume.
384 */
385static void hr_add_hotspare_srv(ipc_call_t *icall)
386{
387 HR_DEBUG("%s()", __func__);
388
389 errno_t rc = EOK;
390 service_id_t vol_svc_id;
391 service_id_t hotspare;
392 hr_volume_t *vol;
393
394 vol_svc_id = ipc_get_arg1(icall);
395 hotspare = ipc_get_arg2(icall);
396
397 vol = hr_get_volume(vol_svc_id);
398 if (vol == NULL) {
399 async_answer_0(icall, ENOENT);
400 return;
401 }
402
403 if (vol->hr_ops.add_hotspare == NULL) {
404 HR_NOTE("hotspare not supported on RAID level = %d, "
405 "metadata type = %s\n", vol->level,
406 hr_get_metadata_type_str(vol->meta_ops->get_type()));
407 async_answer_0(icall, ENOTSUP);
408 return;
409 }
410
411 rc = vol->hr_ops.add_hotspare(vol, hotspare);
412
413 async_answer_0(icall, rc);
414}
415
416/** Send volume states.
417 *
418 * Sends the client pairs of (volume service_id, state).
419 */
420static void hr_get_vol_states_srv(ipc_call_t *icall)
421{
422 HR_DEBUG("%s()", __func__);
423
424 errno_t rc;
425 size_t vol_cnt = 0;
426 hr_pair_vol_state_t pair;
427 ipc_call_t call;
428 size_t size;
429
430 fibril_rwlock_read_lock(&hr_volumes_lock);
431
432 vol_cnt = list_count(&hr_volumes);
433
434 if (!async_data_read_receive(&call, &size)) {
435 rc = EREFUSED;
436 goto error;
437 }
438
439 if (size != sizeof(vol_cnt)) {
440 rc = EINVAL;
441 goto error;
442 }
443
444 rc = async_data_read_finalize(&call, &vol_cnt, size);
445 if (rc != EOK)
446 goto error;
447
448 list_foreach(hr_volumes, lvolumes, hr_volume_t, vol) {
449 pair.svc_id = vol->svc_id;
450 pair.state = vol->state;
451
452 if (!async_data_read_receive(&call, &size)) {
453 rc = EREFUSED;
454 goto error;
455 }
456
457 if (size != sizeof(pair)) {
458 rc = EINVAL;
459 goto error;
460 }
461
462 rc = async_data_read_finalize(&call, &pair, size);
463 if (rc != EOK)
464 goto error;
465 }
466
467 fibril_rwlock_read_unlock(&hr_volumes_lock);
468 async_answer_0(icall, EOK);
469 return;
470error:
471 fibril_rwlock_read_unlock(&hr_volumes_lock);
472 async_answer_0(&call, rc);
473 async_answer_0(icall, rc);
474}
475
476/** Send volume info.
477 *
478 * Sends the client volume info.
479 */
480static void hr_get_vol_info_srv(ipc_call_t *icall)
481{
482 HR_DEBUG("%s()", __func__);
483
484 errno_t rc;
485 size_t size;
486 ipc_call_t call;
487 service_id_t svc_id;
488 hr_vol_info_t info;
489 hr_volume_t *vol;
490
491 if (!async_data_write_receive(&call, &size)) {
492 rc = EREFUSED;
493 goto error;
494 }
495
496 if (size != sizeof(service_id_t)) {
497 rc = EINVAL;
498 goto error;
499 }
500
501 rc = async_data_write_finalize(&call, &svc_id, size);
502 if (rc != EOK)
503 goto error;
504
505 vol = hr_get_volume(svc_id);
506 if (vol == NULL) {
507 rc = ENOENT;
508 goto error;
509 }
510
511 memcpy(info.extents, vol->extents,
512 sizeof(hr_extent_t) * HR_MAX_EXTENTS);
513 memcpy(info.hotspares, vol->hotspares,
514 sizeof(hr_extent_t) * HR_MAX_HOTSPARES);
515 info.svc_id = vol->svc_id;
516 info.extent_no = vol->extent_no;
517 info.hotspare_no = vol->hotspare_no;
518 info.level = vol->level;
519 info.data_blkno = vol->data_blkno;
520 info.rebuild_blk = vol->rebuild_blk;
521 info.strip_size = vol->strip_size;
522 info.bsize = vol->bsize;
523 info.state = vol->state;
524 info.layout = vol->layout;
525 info.meta_type = vol->meta_ops->get_type();
526 memcpy(info.devname, vol->devname, HR_DEVNAME_LEN);
527
528 if (!async_data_read_receive(&call, &size)) {
529 rc = EREFUSED;
530 goto error;
531 }
532
533 if (size != sizeof(info)) {
534 rc = EINVAL;
535 goto error;
536 }
537
538 rc = async_data_read_finalize(&call, &info, size);
539 if (rc != EOK)
540 goto error;
541
542 async_answer_0(icall, EOK);
543 return;
544error:
545 async_answer_0(&call, rc);
546 async_answer_0(icall, rc);
547}
548
549/** HelenRAID server control IPC methods crossroad.
550 */
551static void hr_ctl_conn(ipc_call_t *icall)
552{
553 HR_DEBUG("%s()", __func__);
554
555 async_accept_0(icall);
556
557 while (true) {
558 ipc_call_t call;
559 async_get_call(&call);
560 sysarg_t method = ipc_get_imethod(&call);
561
562 if (!method) {
563 async_answer_0(&call, EOK);
564 return;
565 }
566
567 switch (method) {
568 case HR_CREATE:
569 hr_create_srv(&call);
570 break;
571 case HR_ASSEMBLE:
572 hr_assemble_srv(&call);
573 break;
574 case HR_AUTO_ASSEMBLE:
575 hr_auto_assemble_srv(&call);
576 break;
577 case HR_STOP:
578 hr_stop_srv(&call);
579 break;
580 case HR_STOP_ALL:
581 hr_stop_all_srv(&call);
582 break;
583 case HR_FAIL_EXTENT:
584 hr_fail_extent_srv(&call);
585 break;
586 case HR_ADD_HOTSPARE:
587 hr_add_hotspare_srv(&call);
588 break;
589 case HR_GET_VOL_STATES:
590 hr_get_vol_states_srv(&call);
591 break;
592 case HR_GET_VOL_INFO:
593 hr_get_vol_info_srv(&call);
594 break;
595 default:
596 async_answer_0(&call, EINVAL);
597 }
598 }
599}
600
601/** HelenRAID server IPC method crossroad.
602 *
603 * Distinguishes between control IPC and block device
604 * IPC calls.
605 */
606static void hr_client_conn(ipc_call_t *icall, void *arg)
607{
608 HR_DEBUG("%s()", __func__);
609
610 hr_volume_t *vol;
611
612 service_id_t svc_id = ipc_get_arg2(icall);
613
614 if (svc_id == ctl_sid) {
615 hr_ctl_conn(icall);
616 } else {
617 vol = hr_get_volume(svc_id);
618 if (vol == NULL)
619 async_answer_0(icall, ENOENT);
620 bd_conn(icall, &vol->hr_bds);
621 }
622}
623
624int main(int argc, char **argv)
625{
626 errno_t rc;
627
628 printf("%s: HelenRAID server\n", NAME);
629
630 rc = log_init(NAME);
631 if (rc != EOK) {
632 printf("%s: failed to initialize logging\n", NAME);
633 return 1;
634 }
635
636 fibril_rwlock_initialize(&hr_volumes_lock);
637 list_initialize(&hr_volumes);
638
639 async_set_fallback_port_handler(hr_client_conn, NULL);
640
641 rc = loc_server_register(NAME, &hr_srv);
642 if (rc != EOK) {
643 HR_ERROR("failed registering server: %s", str_error(rc));
644 return EEXIST;
645 }
646
647 rc = loc_service_register(hr_srv, SERVICE_NAME_HR, fallback_port_id,
648 &ctl_sid);
649 if (rc != EOK) {
650 HR_ERROR("failed registering service: %s", str_error(rc));
651 return EEXIST;
652 }
653
654 printf("%s: Trying automatic assembly.\n", NAME);
655 size_t assembled = 0;
656 (void)hr_util_try_assemble(NULL, &assembled);
657 printf("%s: Assembled %zu volume(s).\n", NAME, assembled);
658
659 printf("%s: Accepting connections.\n", NAME);
660 task_retval(0);
661 async_manager();
662
663 return 0;
664}
665
666/** @}
667 */
Note: See TracBrowser for help on using the repository browser.