source: mainline/uspace/srv/bd/hr/hr.c@ 15e51b05

Last change on this file since 15e51b05 was 15e51b05, checked in by Miroslav Cimerman <mc@…>, 6 weeks ago

hr: check extent number

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