source: mainline/uspace/srv/bd/hr/hr.c@ 6aafb48

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

hr: rebuild: fix deadlock on extents_lock

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