source: mainline/uspace/srv/bd/hr/hr.c@ e0695ce

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

hr: save REBUILD position

  • Property mode set to 100644
File size: 14.1 KB
RevLine 
[94d84a0]1/*
[bc3d695]2 * Copyright (c) 2025 Miroslav Cimerman
[94d84a0]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/**
[31eb568]33 * @file hr.c
34 * @brief HelenRAID server methods.
[94d84a0]35 */
36
[b04f7af]37#include <adt/list.h>
[94d84a0]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>
[68e357e]49#include <str.h>
[94d84a0]50#include <str_error.h>
[b04f7af]51#include <block.h>
[94d84a0]52
[b0f1366]53#include "util.h"
[94d84a0]54#include "var.h"
55
[6d0fc11]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 *);
[d1d355f]59static void hr_stop_all_srv(ipc_call_t *);
[6d0fc11]60static void hr_add_hotspare_srv(ipc_call_t *);
[e0bbecb]61static void hr_get_vol_states_srv(ipc_call_t *);
[31eb568]62static void hr_ctl_conn(ipc_call_t *);
[6d0fc11]63static void hr_client_conn(ipc_call_t *, void *);
64
[94d84a0]65loc_srv_t *hr_srv;
[8b51009]66list_t hr_volumes;
67fibril_rwlock_t hr_volumes_lock;
[94d84a0]68
69static service_id_t ctl_sid;
70
[31eb568]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 */
[6d0fc11]78static void hr_create_srv(ipc_call_t *icall)
[a19d7fc4]79{
[8b51009]80 HR_DEBUG("%s()", __func__);
[a19d7fc4]81
[8b51009]82 errno_t rc;
[6d0fc11]83 size_t i, size;
84 hr_config_t *cfg;
[50603405]85 hr_volume_t *vol;
[8b51009]86 ipc_call_t call;
87
[6d0fc11]88 if (!async_data_write_receive(&call, &size)) {
89 async_answer_0(&call, EREFUSED);
[8b51009]90 async_answer_0(icall, EREFUSED);
91 return;
[a19d7fc4]92 }
93
[6d0fc11]94 if (size != sizeof(hr_config_t)) {
[d082801]95 async_answer_0(&call, EINVAL);
[8b51009]96 async_answer_0(icall, EINVAL);
97 return;
98 }
[a19d7fc4]99
[6d0fc11]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
[af73327a]117 * for creation of a new volume, abort
[6d0fc11]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 */
[af73327a]125 HR_ERROR("missing device provided for volume "
[6d0fc11]126 "creation, aborting");
127 free(cfg);
128 async_answer_0(icall, EINVAL);
129 return;
130 }
131 }
132
[50603405]133 rc = hr_create_vol_struct(&vol, cfg->level, cfg->devname,
134 HR_METADATA_NATIVE);
[6d0fc11]135 if (rc != EOK) {
136 free(cfg);
137 async_answer_0(icall, rc);
138 return;
139 }
140
[50603405]141 rc = hr_init_extents_from_cfg(vol, cfg);
[baa4929]142 if (rc != EOK)
143 goto error;
[6d0fc11]144
[50603405]145 vol->hr_ops.init(vol);
[d082801]146 if (rc != EOK)
147 goto error;
148
[50603405]149 rc = vol->meta_ops->init_vol2meta(vol, vol->in_mem_md);
[0277ec2]150 if (rc != EOK)
151 goto error;
152
[50603405]153 rc = vol->hr_ops.create(vol);
[d082801]154 if (rc != EOK)
155 goto error;
156
[e0695ce]157 vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
[6d0fc11]158
[50603405]159 rc = hr_register_volume(vol);
[6d0fc11]160 if (rc != EOK)
161 goto error;
162
163 fibril_rwlock_write_lock(&hr_volumes_lock);
[50603405]164 list_append(&vol->lvolumes, &hr_volumes);
[6d0fc11]165 fibril_rwlock_write_unlock(&hr_volumes_lock);
166
[c5b60e25]167 HR_NOTE("created volume \"%s\" (%" PRIun ")\n", vol->devname,
168 vol->svc_id);
[6d0fc11]169
170 free(cfg);
171 async_answer_0(icall, rc);
[d082801]172 return;
173error:
[6d0fc11]174 free(cfg);
[50603405]175 hr_destroy_vol_struct(vol);
[d082801]176 async_answer_0(icall, rc);
177}
178
[31eb568]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 */
[d082801]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);
[8b51009]199 return;
200 }
[a19d7fc4]201
[d082801]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);
[8b51009]221 return;
[a19d7fc4]222 }
223
[d082801]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);
[8b51009]238 async_answer_0(icall, EOK);
[d082801]239 return;
240error:
241 free(cfg);
242 async_answer_0(&call, rc);
243 async_answer_0(icall, rc);
[a19d7fc4]244}
245
[31eb568]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 */
[6d0fc11]252static void hr_auto_assemble_srv(ipc_call_t *icall)
[94d84a0]253{
[a57dde4]254 HR_DEBUG("%s()", __func__);
[94d84a0]255
256 errno_t rc;
[6d0fc11]257 size_t size;
258 size_t assembled_cnt = 0;
[68e357e]259 ipc_call_t call;
[94d84a0]260
[6d0fc11]261 if (!async_data_read_receive(&call, &size)) {
[68e357e]262 async_answer_0(icall, EREFUSED);
[94d84a0]263 return;
264 }
265
[6d0fc11]266 if (size != sizeof(size_t)) {
[68e357e]267 async_answer_0(&call, EINVAL);
268 async_answer_0(icall, EINVAL);
[94d84a0]269 return;
270 }
271
[6d0fc11]272 rc = hr_util_try_assemble(NULL, &assembled_cnt);
[b0f1366]273 if (rc != EOK)
274 goto error;
275
[6d0fc11]276 rc = async_data_read_finalize(&call, &assembled_cnt, size);
[8a65373]277 if (rc != EOK)
278 goto error;
279
[6d0fc11]280 async_answer_0(icall, EOK);
[b0f1366]281 return;
282error:
[6d0fc11]283 async_answer_0(&call, rc);
[b0f1366]284 async_answer_0(icall, rc);
[94d84a0]285}
286
[31eb568]287/** Volume deactivation (server).
288 *
289 * Deactivates/detaches specified volume.
290 */
[a19d7fc4]291static void hr_stop_srv(ipc_call_t *icall)
292{
[a57dde4]293 HR_DEBUG("%s()", __func__);
[a19d7fc4]294
[cf28ffd3]295 errno_t rc = EOK;
[a19d7fc4]296 service_id_t svc_id;
297
298 svc_id = ipc_get_arg1(icall);
299
[c9ce6d22]300 rc = hr_remove_volume(svc_id);
[d1d355f]301
302 async_answer_0(icall, rc);
303}
304
[31eb568]305/** Automatic volume deactivation (server).
306 *
307 * Tries to deactivate/detach all volumes.
308 */
[d1d355f]309static void hr_stop_all_srv(ipc_call_t *icall)
310{
311 HR_DEBUG("%s()", __func__);
312
[c9ce6d22]313 service_id_t *vol_svcs = NULL;
[d1d355f]314 errno_t rc = EOK;
[c9ce6d22]315 size_t i, vol_cnt;
[d1d355f]316
[c9ce6d22]317 rc = hr_get_volume_svcs(&vol_cnt, &vol_svcs);
318 if (rc != EOK)
319 goto fail;
[7b359f5]320
[177f6ff]321 for (i = 0; i < vol_cnt; i++)
322 (void)hr_remove_volume(vol_svcs[i]);
[d1d355f]323
[c9ce6d22]324fail:
325 if (vol_svcs != NULL)
326 free(vol_svcs);
[a19d7fc4]327 async_answer_0(icall, rc);
328}
329
[31eb568]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 */
[d1d355f]337static void hr_fail_extent_srv(ipc_call_t *icall)
338{
339 HR_DEBUG("%s()", __func__);
340
341 service_id_t svc_id;
[13ada52]342 size_t extent_idx_to_fail;
[d1d355f]343 hr_volume_t *vol;
344
345 svc_id = (service_id_t)ipc_get_arg1(icall);
[13ada52]346 extent_idx_to_fail = (size_t)ipc_get_arg2(icall);
[d1d355f]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);
[ac4b70b]355 fibril_rwlock_write_lock(&vol->states_lock);
[d1d355f]356
[137f7cf5]357 hr_extent_t *ext = &vol->extents[extent_idx_to_fail];
358
[13ada52]359 switch (ext->state) {
[ac4b70b]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:
[13ada52]368 hr_update_ext_state(vol, extent_idx_to_fail, HR_EXT_FAILED);
369 (void)vol->meta_ops->erase_block(ext->svc_id);
[ac4b70b]370 hr_mark_vol_state_dirty(vol);
371 }
[d1d355f]372
373 fibril_rwlock_write_unlock(&vol->states_lock);
[ac4b70b]374 fibril_rwlock_read_unlock(&vol->extents_lock);
[d1d355f]375
[da80de9]376 vol->hr_ops.vol_state_eval(vol);
[d1d355f]377
378 async_answer_0(icall, EOK);
379}
380
[31eb568]381/** Add hotspare to volume (server).
382 *
383 * Adds hotspare to a volume.
384 */
[5b320ac]385static void hr_add_hotspare_srv(ipc_call_t *icall)
386{
[a57dde4]387 HR_DEBUG("%s()", __func__);
[5b320ac]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) {
[c6d2af8]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()));
[5b320ac]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
[e0bbecb]416/** Send volume states.
[31eb568]417 *
[e0bbecb]418 * Sends the client pairs of (volume service_id, state).
[31eb568]419 */
[e0bbecb]420static void hr_get_vol_states_srv(ipc_call_t *icall)
[095a989]421{
[a57dde4]422 HR_DEBUG("%s()", __func__);
[095a989]423
424 errno_t rc;
425 size_t vol_cnt = 0;
[e0bbecb]426 hr_pair_vol_state_t pair;
[095a989]427 ipc_call_t call;
428 size_t size;
429
[8b51009]430 fibril_rwlock_read_lock(&hr_volumes_lock);
[095a989]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
[e0bbecb]439 if (size != sizeof(vol_cnt)) {
[095a989]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
[b235c67]448 list_foreach(hr_volumes, lvolumes, hr_volume_t, vol) {
[e0bbecb]449 pair.svc_id = vol->svc_id;
450 pair.state = vol->state;
[095a989]451
452 if (!async_data_read_receive(&call, &size)) {
453 rc = EREFUSED;
454 goto error;
455 }
456
[e0bbecb]457 if (size != sizeof(pair)) {
[095a989]458 rc = EINVAL;
459 goto error;
460 }
461
[e0bbecb]462 rc = async_data_read_finalize(&call, &pair, size);
[095a989]463 if (rc != EOK)
464 goto error;
465 }
466
[8b51009]467 fibril_rwlock_read_unlock(&hr_volumes_lock);
[095a989]468 async_answer_0(icall, EOK);
469 return;
470error:
[8b51009]471 fibril_rwlock_read_unlock(&hr_volumes_lock);
[095a989]472 async_answer_0(&call, rc);
473 async_answer_0(icall, rc);
474}
475
[e0bbecb]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;
[f34568c]520 info.rebuild_blk = vol->rebuild_blk;
[e0bbecb]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
[31eb568]549/** HelenRAID server control IPC methods crossroad.
550 */
551static void hr_ctl_conn(ipc_call_t *icall)
[94d84a0]552{
[a57dde4]553 HR_DEBUG("%s()", __func__);
[94d84a0]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:
[d082801]569 hr_create_srv(&call);
[94d84a0]570 break;
[b0f1366]571 case HR_ASSEMBLE:
[d082801]572 hr_assemble_srv(&call);
[b0f1366]573 break;
[8b51009]574 case HR_AUTO_ASSEMBLE:
575 hr_auto_assemble_srv(&call);
576 break;
[a19d7fc4]577 case HR_STOP:
578 hr_stop_srv(&call);
579 break;
[d1d355f]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;
[5b320ac]586 case HR_ADD_HOTSPARE:
587 hr_add_hotspare_srv(&call);
588 break;
[e0bbecb]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);
[a19d7fc4]594 break;
[94d84a0]595 default:
596 async_answer_0(&call, EINVAL);
597 }
598 }
599}
600
[31eb568]601/** HelenRAID server IPC method crossroad.
602 *
603 * Distinguishes between control IPC and block device
604 * IPC calls.
605 */
[94d84a0]606static void hr_client_conn(ipc_call_t *icall, void *arg)
607{
[a57dde4]608 HR_DEBUG("%s()", __func__);
[94d84a0]609
610 hr_volume_t *vol;
611
[da5c257]612 service_id_t svc_id = ipc_get_arg2(icall);
[94d84a0]613
614 if (svc_id == ctl_sid) {
[31eb568]615 hr_ctl_conn(icall);
[94d84a0]616 } else {
617 vol = hr_get_volume(svc_id);
618 if (vol == NULL)
[7bfe468]619 async_answer_0(icall, ENOENT);
[94d84a0]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
[8b51009]636 fibril_rwlock_initialize(&hr_volumes_lock);
[94d84a0]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) {
[d199a6f]643 HR_ERROR("failed registering server: %s", str_error(rc));
[94d84a0]644 return EEXIST;
645 }
646
[817cb83]647 rc = loc_service_register(hr_srv, SERVICE_NAME_HR, fallback_port_id,
648 &ctl_sid);
[94d84a0]649 if (rc != EOK) {
[d199a6f]650 HR_ERROR("failed registering service: %s", str_error(rc));
[94d84a0]651 return EEXIST;
652 }
653
[e2a8fd2]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
[a056759]659 printf("%s: Accepting connections.\n", NAME);
[94d84a0]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.