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

Last change on this file since 6f13257 was a2281efc, checked in by Miroslav Cimerman <mc@…>, 3 months ago

hr: print service id of created volume

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