source: mainline/uspace/srv/bd/hr/hr.c@ 81b4c795

Last change on this file since 81b4c795 was 817cb83, checked in by Miroslav Cimerman <mc@…>, 8 weeks ago

hr: update loc_service_register() calls

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