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

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

hr: use the term 'volume' instead of 'array'

  • Property mode set to 100644
File size: 12.9 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 *);
[56602e0]61static void hr_print_state_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
[50603405]157 rc = vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
[6d0fc11]158 if (rc != EOK)
159 goto error;
160
[50603405]161 rc = hr_register_volume(vol);
[6d0fc11]162 if (rc != EOK)
163 goto error;
164
165 fibril_rwlock_write_lock(&hr_volumes_lock);
[50603405]166 list_append(&vol->lvolumes, &hr_volumes);
[6d0fc11]167 fibril_rwlock_write_unlock(&hr_volumes_lock);
168
[d1d355f]169 HR_NOTE("created volume \"%s\"\n", vol->devname);
[6d0fc11]170
171 free(cfg);
172 async_answer_0(icall, rc);
[d082801]173 return;
174error:
[6d0fc11]175 free(cfg);
[50603405]176 hr_destroy_vol_struct(vol);
[d082801]177 async_answer_0(icall, rc);
178}
179
[31eb568]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 */
[d082801]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);
[8b51009]200 return;
201 }
[a19d7fc4]202
[d082801]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);
[8b51009]222 return;
[a19d7fc4]223 }
224
[d082801]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);
[8b51009]239 async_answer_0(icall, EOK);
[d082801]240 return;
241error:
242 free(cfg);
243 async_answer_0(&call, rc);
244 async_answer_0(icall, rc);
[a19d7fc4]245}
246
[31eb568]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 */
[6d0fc11]253static void hr_auto_assemble_srv(ipc_call_t *icall)
[94d84a0]254{
[a57dde4]255 HR_DEBUG("%s()", __func__);
[94d84a0]256
257 errno_t rc;
[6d0fc11]258 size_t size;
259 size_t assembled_cnt = 0;
[68e357e]260 ipc_call_t call;
[94d84a0]261
[6d0fc11]262 if (!async_data_read_receive(&call, &size)) {
[68e357e]263 async_answer_0(icall, EREFUSED);
[94d84a0]264 return;
265 }
266
[6d0fc11]267 if (size != sizeof(size_t)) {
[68e357e]268 async_answer_0(&call, EINVAL);
269 async_answer_0(icall, EINVAL);
[94d84a0]270 return;
271 }
272
[6d0fc11]273 rc = hr_util_try_assemble(NULL, &assembled_cnt);
[b0f1366]274 if (rc != EOK)
275 goto error;
276
[6d0fc11]277 rc = async_data_read_finalize(&call, &assembled_cnt, size);
[8a65373]278 if (rc != EOK)
279 goto error;
280
[6d0fc11]281 async_answer_0(icall, EOK);
[b0f1366]282 return;
283error:
[6d0fc11]284 async_answer_0(&call, rc);
[b0f1366]285 async_answer_0(icall, rc);
[94d84a0]286}
287
[31eb568]288/** Volume deactivation (server).
289 *
290 * Deactivates/detaches specified volume.
291 */
[a19d7fc4]292static void hr_stop_srv(ipc_call_t *icall)
293{
[a57dde4]294 HR_DEBUG("%s()", __func__);
[a19d7fc4]295
[cf28ffd3]296 errno_t rc = EOK;
[a19d7fc4]297 service_id_t svc_id;
298 hr_volume_t *vol;
299
300 svc_id = ipc_get_arg1(icall);
301
302 vol = hr_get_volume(svc_id);
303 if (vol == NULL) {
304 async_answer_0(icall, ENOENT);
305 return;
306 }
307
[d1d355f]308 rc = hr_remove_volume(vol);
309
310 async_answer_0(icall, rc);
311}
312
[31eb568]313/** Automatic volume deactivation (server).
314 *
315 * Tries to deactivate/detach all volumes.
316 */
[d1d355f]317static void hr_stop_all_srv(ipc_call_t *icall)
318{
319 HR_DEBUG("%s()", __func__);
320
321 hr_volume_t *vol;
322 errno_t rc = EOK;
323
324 while (true) {
325 fibril_rwlock_write_lock(&hr_volumes_lock);
326 if (list_empty(&hr_volumes)) {
327 fibril_rwlock_write_unlock(&hr_volumes_lock);
328 break;
[cf28ffd3]329 }
[401b9e42]330
[d1d355f]331 vol = list_pop(&hr_volumes, hr_volume_t, lvolumes);
[401b9e42]332
[d1d355f]333 fibril_rwlock_write_unlock(&hr_volumes_lock);
[7b359f5]334
[d1d355f]335 rc = hr_remove_volume(vol);
336 if (rc != EOK)
337 break;
[a19d7fc4]338 }
[d1d355f]339
[a19d7fc4]340 async_answer_0(icall, rc);
341}
342
[31eb568]343/** Simulate volume extent failure (server).
344 *
345 * Changes the specified extent's state to FAULTY.
346 * Other extents' metadata are marked as dirty, therefore
347 * it effectively invalides the specified extent as well
348 * for further uses.
349 */
[d1d355f]350static void hr_fail_extent_srv(ipc_call_t *icall)
351{
352 HR_DEBUG("%s()", __func__);
353
354 service_id_t svc_id;
355 size_t fail_extent;
356 hr_volume_t *vol;
357
358 svc_id = (service_id_t)ipc_get_arg1(icall);
359 fail_extent = (size_t)ipc_get_arg2(icall);
360
361 vol = hr_get_volume(svc_id);
362 if (vol == NULL) {
363 async_answer_0(icall, ENOENT);
364 return;
365 }
366
367 fibril_rwlock_read_lock(&vol->extents_lock);
[ac4b70b]368 fibril_rwlock_write_lock(&vol->states_lock);
[d1d355f]369
[56602e0]370 switch (vol->extents[fail_extent].state) {
[ac4b70b]371 case HR_EXT_NONE:
372 case HR_EXT_MISSING:
373 case HR_EXT_FAILED:
374 fibril_rwlock_write_unlock(&vol->states_lock);
375 fibril_rwlock_read_unlock(&vol->extents_lock);
376 async_answer_0(icall, EINVAL);
377 return;
378 default:
[56602e0]379 hr_update_ext_state(vol, fail_extent, HR_EXT_FAILED);
[ac4b70b]380 hr_mark_vol_state_dirty(vol);
381 }
[d1d355f]382
383 fibril_rwlock_write_unlock(&vol->states_lock);
[ac4b70b]384 fibril_rwlock_read_unlock(&vol->extents_lock);
[d1d355f]385
[56602e0]386 vol->hr_ops.state_event(vol);
[d1d355f]387
388 async_answer_0(icall, EOK);
389}
390
[31eb568]391/** Add hotspare to volume (server).
392 *
393 * Adds hotspare to a volume.
394 */
[5b320ac]395static void hr_add_hotspare_srv(ipc_call_t *icall)
396{
[a57dde4]397 HR_DEBUG("%s()", __func__);
[5b320ac]398
399 errno_t rc = EOK;
400 service_id_t vol_svc_id;
401 service_id_t hotspare;
402 hr_volume_t *vol;
403
404 vol_svc_id = ipc_get_arg1(icall);
405 hotspare = ipc_get_arg2(icall);
406
407 vol = hr_get_volume(vol_svc_id);
408 if (vol == NULL) {
409 async_answer_0(icall, ENOENT);
410 return;
411 }
412
413 if (vol->hr_ops.add_hotspare == NULL) {
[c6d2af8]414 HR_NOTE("hotspare not supported on RAID level = %d, "
415 "metadata type = %s\n", vol->level,
416 hr_get_metadata_type_str(vol->meta_ops->get_type()));
[5b320ac]417 async_answer_0(icall, ENOTSUP);
418 return;
419 }
420
421 rc = vol->hr_ops.add_hotspare(vol, hotspare);
422
423 async_answer_0(icall, rc);
424}
425
[31eb568]426/** Volume state printing (server).
427 *
428 * Prints info about all active volumes.
429 */
[56602e0]430static void hr_print_state_srv(ipc_call_t *icall)
[095a989]431{
[a57dde4]432 HR_DEBUG("%s()", __func__);
[095a989]433
434 errno_t rc;
435 size_t vol_cnt = 0;
436 hr_vol_info_t info;
437 ipc_call_t call;
438 size_t size;
439
[8b51009]440 fibril_rwlock_read_lock(&hr_volumes_lock);
[095a989]441
442 vol_cnt = list_count(&hr_volumes);
443
444 if (!async_data_read_receive(&call, &size)) {
445 rc = EREFUSED;
446 goto error;
447 }
448
449 if (size != sizeof(size_t)) {
450 rc = EINVAL;
451 goto error;
452 }
453
454 rc = async_data_read_finalize(&call, &vol_cnt, size);
455 if (rc != EOK)
456 goto error;
457
[b235c67]458 list_foreach(hr_volumes, lvolumes, hr_volume_t, vol) {
459 memcpy(info.extents, vol->extents,
[dfa2313]460 sizeof(hr_extent_t) * HR_MAX_EXTENTS);
[5b320ac]461 memcpy(info.hotspares, vol->hotspares,
462 sizeof(hr_extent_t) * HR_MAX_HOTSPARES);
[b235c67]463 info.svc_id = vol->svc_id;
[65706f1]464 info.extent_no = vol->extent_no;
[5b320ac]465 info.hotspare_no = vol->hotspare_no;
[b235c67]466 info.level = vol->level;
[b0f1366]467 /* print usable number of blocks */
[f647b87]468 /* TODO: change to data_blkno */
[b235c67]469 info.nblocks = vol->data_blkno;
470 info.strip_size = vol->strip_size;
471 info.bsize = vol->bsize;
[56602e0]472 info.state = vol->state;
[37a9c1e]473 info.layout = vol->layout;
[095a989]474
475 if (!async_data_read_receive(&call, &size)) {
476 rc = EREFUSED;
477 goto error;
478 }
479
480 if (size != sizeof(hr_vol_info_t)) {
481 rc = EINVAL;
482 goto error;
483 }
484
485 rc = async_data_read_finalize(&call, &info, size);
486 if (rc != EOK)
487 goto error;
488 }
489
[8b51009]490 fibril_rwlock_read_unlock(&hr_volumes_lock);
[095a989]491 async_answer_0(icall, EOK);
492 return;
493error:
[8b51009]494 fibril_rwlock_read_unlock(&hr_volumes_lock);
[095a989]495 async_answer_0(&call, rc);
496 async_answer_0(icall, rc);
497}
498
[31eb568]499/** HelenRAID server control IPC methods crossroad.
500 */
501static void hr_ctl_conn(ipc_call_t *icall)
[94d84a0]502{
[a57dde4]503 HR_DEBUG("%s()", __func__);
[94d84a0]504
505 async_accept_0(icall);
506
507 while (true) {
508 ipc_call_t call;
509 async_get_call(&call);
510 sysarg_t method = ipc_get_imethod(&call);
511
512 if (!method) {
513 async_answer_0(&call, EOK);
514 return;
515 }
516
517 switch (method) {
518 case HR_CREATE:
[d082801]519 hr_create_srv(&call);
[94d84a0]520 break;
[b0f1366]521 case HR_ASSEMBLE:
[d082801]522 hr_assemble_srv(&call);
[b0f1366]523 break;
[8b51009]524 case HR_AUTO_ASSEMBLE:
525 hr_auto_assemble_srv(&call);
526 break;
[a19d7fc4]527 case HR_STOP:
528 hr_stop_srv(&call);
529 break;
[d1d355f]530 case HR_STOP_ALL:
531 hr_stop_all_srv(&call);
532 break;
533 case HR_FAIL_EXTENT:
534 hr_fail_extent_srv(&call);
535 break;
[5b320ac]536 case HR_ADD_HOTSPARE:
537 hr_add_hotspare_srv(&call);
538 break;
[a19d7fc4]539 case HR_STATUS:
[56602e0]540 hr_print_state_srv(&call);
[a19d7fc4]541 break;
[94d84a0]542 default:
543 async_answer_0(&call, EINVAL);
544 }
545 }
546}
547
[31eb568]548/** HelenRAID server IPC method crossroad.
549 *
550 * Distinguishes between control IPC and block device
551 * IPC calls.
552 */
[94d84a0]553static void hr_client_conn(ipc_call_t *icall, void *arg)
554{
[a57dde4]555 HR_DEBUG("%s()", __func__);
[94d84a0]556
557 hr_volume_t *vol;
558
[da5c257]559 service_id_t svc_id = ipc_get_arg2(icall);
[94d84a0]560
561 if (svc_id == ctl_sid) {
[31eb568]562 hr_ctl_conn(icall);
[94d84a0]563 } else {
564 vol = hr_get_volume(svc_id);
565 if (vol == NULL)
[7bfe468]566 async_answer_0(icall, ENOENT);
[94d84a0]567 bd_conn(icall, &vol->hr_bds);
568 }
569}
570
571int main(int argc, char **argv)
572{
573 errno_t rc;
574
575 printf("%s: HelenRAID server\n", NAME);
576
577 rc = log_init(NAME);
578 if (rc != EOK) {
579 printf("%s: failed to initialize logging\n", NAME);
580 return 1;
581 }
582
[8b51009]583 fibril_rwlock_initialize(&hr_volumes_lock);
[94d84a0]584 list_initialize(&hr_volumes);
585
586 async_set_fallback_port_handler(hr_client_conn, NULL);
587
588 rc = loc_server_register(NAME, &hr_srv);
589 if (rc != EOK) {
[d199a6f]590 HR_ERROR("failed registering server: %s", str_error(rc));
[94d84a0]591 return EEXIST;
592 }
593
594 rc = loc_service_register(hr_srv, SERVICE_NAME_HR, &ctl_sid);
595 if (rc != EOK) {
[d199a6f]596 HR_ERROR("failed registering service: %s", str_error(rc));
[94d84a0]597 return EEXIST;
598 }
599
[a056759]600 printf("%s: Accepting connections.\n", NAME);
[94d84a0]601 task_retval(0);
602 async_manager();
603
604 return 0;
605}
606
607/** @}
608 */
Note: See TracBrowser for help on using the repository browser.