source: mainline/uspace/lib/device/src/hr.c

Last change on this file was 2192a01, checked in by Miroslav Cimerman <mc@…>, 11 hours ago

hr: basic Linux Multiple Device RAID format support

  • Property mode set to 100644
File size: 11.4 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 libdevice
30 * @{
31 */
32/**
33 * @file HelenRAID client API
34 */
35
36#include <abi/ipc/interfaces.h>
37#include <async.h>
38#include <hr.h>
39#include <ipc/hr.h>
40#include <ipc/services.h>
41#include <loc.h>
42#include <stdlib.h>
43#include <stdio.h>
44#include <str.h>
45
46/** Initialize server session.
47 *
48 * @param rhr Place to store inited session
49 *
50 * @return EOK on success or an error code
51 */
52errno_t hr_sess_init(hr_t **rhr)
53{
54 errno_t rc;
55 hr_t *hr = NULL;
56
57 if (rhr == NULL)
58 return EINVAL;
59
60 hr = calloc(1, sizeof(hr_t));
61 if (hr == NULL) {
62 rc = ENOMEM;
63 goto error;
64 }
65
66 service_id_t hr_svcid;
67
68 rc = loc_service_get_id(SERVICE_NAME_HR, &hr_svcid, 0);
69 if (rc != EOK)
70 goto error;
71
72 hr->sess = loc_service_connect(hr_svcid, INTERFACE_HR, 0);
73 if (hr->sess == NULL) {
74 rc = EIO;
75 goto error;
76 }
77
78 *rhr = hr;
79 return EOK;
80error:
81 if (hr != NULL)
82 free(hr);
83
84 return rc;
85}
86
87/** Destroy server session.
88 *
89 * @param hr Session to destroy
90 */
91void hr_sess_destroy(hr_t *hr)
92{
93 if (hr == NULL)
94 return;
95
96 async_hangup(hr->sess);
97 free(hr);
98}
99
100/** Create volume.
101 *
102 * @param hr Server session
103 * @param hr_config Config to create from
104 *
105 * @return EOK on success or an error code
106 */
107errno_t hr_create(hr_t *hr, hr_config_t *hr_config)
108{
109 errno_t rc, retval;
110 async_exch_t *exch;
111 aid_t req;
112
113 exch = async_exchange_begin(hr->sess);
114 if (exch == NULL)
115 return EINVAL;
116
117 req = async_send_0(exch, HR_CREATE, NULL);
118
119 rc = async_data_write_start(exch, hr_config, sizeof(hr_config_t));
120 if (rc != EOK) {
121 async_exchange_end(exch);
122 async_forget(req);
123 return rc;
124 }
125
126 async_exchange_end(exch);
127 async_wait_for(req, &retval);
128 return retval;
129}
130
131/** Assemble volumes.
132 *
133 * @param hr Server session
134 * @param hr_config Config to assemble from
135 * @param rassembled_cnt Place to store assembled count
136 *
137 * @return EOK on success or an error code
138 */
139errno_t hr_assemble(hr_t *hr, hr_config_t *hr_config, size_t *rassembled_cnt)
140{
141 errno_t rc;
142 async_exch_t *exch;
143 aid_t req;
144 size_t assembled_cnt;
145
146 exch = async_exchange_begin(hr->sess);
147 if (exch == NULL)
148 return EINVAL;
149
150 req = async_send_0(exch, HR_ASSEMBLE, NULL);
151
152 rc = async_data_write_start(exch, hr_config, sizeof(hr_config_t));
153 if (rc != EOK) {
154 async_exchange_end(exch);
155 async_forget(req);
156 return rc;
157 }
158
159 rc = async_data_read_start(exch, &assembled_cnt, sizeof(size_t));
160 if (rc != EOK) {
161 async_exchange_end(exch);
162 async_forget(req);
163 return rc;
164 }
165
166 async_exchange_end(exch);
167 async_wait_for(req, &rc);
168
169 if (rassembled_cnt != NULL)
170 *rassembled_cnt = assembled_cnt;
171
172 return rc;
173}
174
175/** Automatically assemble volumes.
176 *
177 * @param hr Server session
178 * @param rassembled_cnt Place to store assembled count
179 *
180 * @return EOK on success or an error code
181 */
182errno_t hr_auto_assemble(hr_t *hr, size_t *rassembled_cnt)
183{
184 errno_t rc;
185 size_t assembled_cnt;
186
187 async_exch_t *exch = async_exchange_begin(hr->sess);
188 if (exch == NULL) {
189 rc = EINVAL;
190 goto error;
191 }
192
193 aid_t req = async_send_0(exch, HR_AUTO_ASSEMBLE, NULL);
194
195 rc = async_data_read_start(exch, &assembled_cnt, sizeof(size_t));
196 if (rc != EOK) {
197 async_exchange_end(exch);
198 async_forget(req);
199 return rc;
200 }
201
202 async_exchange_end(exch);
203 async_wait_for(req, &rc);
204
205 if (rassembled_cnt != NULL)
206 *rassembled_cnt = assembled_cnt;
207error:
208 return rc;
209}
210
211/** Stop/deactivate volume.
212 *
213 * @param hr Server session
214 * @param devname Volume name
215 *
216 * @return EOK on success or an error code
217 */
218errno_t hr_stop(hr_t *hr, const char *devname)
219{
220 errno_t rc;
221 async_exch_t *exch;
222 service_id_t svc_id;
223
224 rc = loc_service_get_id(devname, &svc_id, 0);
225 if (rc != EOK)
226 return rc;
227
228 exch = async_exchange_begin(hr->sess);
229 if (exch == NULL) {
230 rc = EINVAL;
231 goto error;
232 }
233
234 rc = async_req_1_0(exch, HR_STOP, svc_id);
235 async_exchange_end(exch);
236error:
237 return rc;
238}
239
240/** Stop/deactivate all volumes.
241 *
242 * @param hr Server session
243 *
244 * @return EOK on success or an error code
245 */
246errno_t hr_stop_all(hr_t *hr)
247{
248 async_exch_t *exch;
249 errno_t rc;
250
251 exch = async_exchange_begin(hr->sess);
252 if (exch == NULL) {
253 rc = EINVAL;
254 goto error;
255 }
256
257 rc = async_req_0_0(exch, HR_STOP_ALL);
258 async_exchange_end(exch);
259error:
260 return rc;
261}
262
263/** Fail an extent in volume.
264 *
265 * @param hr Server session
266 * @param volume_name Volume name
267 * @param extent Extent index to fail
268 *
269 * @return EOK on success or an error code
270 */
271errno_t hr_fail_extent(hr_t *hr, const char *volume_name, unsigned long extent)
272{
273 errno_t rc;
274 async_exch_t *exch;
275 service_id_t vol_svc_id;
276
277 rc = loc_service_get_id(volume_name, &vol_svc_id, 0);
278 if (rc != EOK)
279 return rc;
280
281 exch = async_exchange_begin(hr->sess);
282 if (exch == NULL) {
283 rc = EINVAL;
284 goto error;
285 }
286
287 rc = async_req_2_0(exch, HR_FAIL_EXTENT, vol_svc_id, extent);
288 async_exchange_end(exch);
289error:
290 return rc;
291}
292
293/** Add a hotspare to volume.
294 *
295 * @param hr Server session
296 * @param volume_name Volume name
297 * @param hotspare Hotspare service name
298 *
299 * @return EOK on success or an error code
300 */
301errno_t hr_add_hotspare(hr_t *hr, const char *volume_name, const char *hotspare)
302{
303 errno_t rc;
304 async_exch_t *exch;
305 service_id_t vol_svc_id, hs_svc_id;
306
307 rc = loc_service_get_id(volume_name, &vol_svc_id, 0);
308 if (rc != EOK)
309 return rc;
310
311 rc = loc_service_get_id(hotspare, &hs_svc_id, 0);
312 if (rc != EOK)
313 return rc;
314
315 exch = async_exchange_begin(hr->sess);
316 if (exch == NULL) {
317 rc = EINVAL;
318 goto error;
319 }
320
321 rc = async_req_2_0(exch, HR_ADD_HOTSPARE, vol_svc_id, hs_svc_id);
322 async_exchange_end(exch);
323error:
324 return rc;
325}
326
327/** Get state of volumes.
328 *
329 * @param hr Server session
330 * @param rpairs Place to store pointer to (service id, vol state) pairs
331 * @param rcnt Place to store pair count
332 *
333 * @return EOK on success or an error code
334 */
335errno_t hr_get_vol_states(hr_t *hr, hr_pair_vol_state_t **rpairs, size_t *rcnt)
336{
337 errno_t rc, retval;
338 async_exch_t *exch;
339 aid_t req;
340 size_t cnt, i;
341 hr_pair_vol_state_t *pairs = NULL;
342
343 exch = async_exchange_begin(hr->sess);
344 if (exch == NULL) {
345 rc = EINVAL;
346 goto error;
347 }
348
349 req = async_send_0(exch, HR_GET_VOL_STATES, NULL);
350 rc = async_data_read_start(exch, &cnt, sizeof(size_t));
351 if (rc != EOK) {
352 async_exchange_end(exch);
353 async_forget(req);
354 return rc;
355 }
356
357 pairs = calloc(cnt, sizeof(*pairs));
358 if (pairs == NULL) {
359 async_exchange_end(exch);
360 async_forget(req);
361 return ENOMEM;
362 }
363
364 for (i = 0; i < cnt; i++) {
365 rc = async_data_read_start(exch, &pairs[i], sizeof(*pairs));
366 if (rc != EOK) {
367 async_exchange_end(exch);
368 async_forget(req);
369 goto error;
370 }
371 }
372
373 async_exchange_end(exch);
374 async_wait_for(req, &retval);
375 if (retval != EOK) {
376 rc = retval;
377 goto error;
378 }
379
380 if (rpairs != NULL)
381 *rpairs = pairs;
382 if (rcnt != NULL)
383 *rcnt = cnt;
384 return EOK;
385
386error:
387 if (pairs != NULL)
388 free(pairs);
389 return rc;
390}
391
392/** Get volume info.
393 *
394 * @param hr Server session
395 * @param svc_id Service id of volume
396 * @param rinfo Place to store volume info
397 *
398 * @return EOK on success or an error code
399 */
400errno_t hr_get_vol_info(hr_t *hr, service_id_t svc_id, hr_vol_info_t *rinfo)
401{
402 errno_t rc, retval;
403 async_exch_t *exch;
404 aid_t req;
405
406 exch = async_exchange_begin(hr->sess);
407 if (exch == NULL) {
408 rc = EINVAL;
409 goto error;
410 }
411
412 req = async_send_0(exch, HR_GET_VOL_INFO, NULL);
413 rc = async_data_write_start(exch, &svc_id, sizeof(svc_id));
414 if (rc != EOK) {
415 async_exchange_end(exch);
416 async_forget(req);
417 return rc;
418 }
419
420 rc = async_data_read_start(exch, rinfo, sizeof(*rinfo));
421 async_exchange_end(exch);
422 if (rc != EOK) {
423 async_forget(req);
424 goto error;
425 }
426
427 async_wait_for(req, &retval);
428 if (retval != EOK) {
429 rc = retval;
430 goto error;
431 }
432
433error:
434 return rc;
435}
436
437/** Get volume state string.
438 *
439 * @param state State value
440 *
441 * @return State string
442 */
443const char *hr_get_vol_state_str(hr_vol_state_t state)
444{
445 switch (state) {
446 case HR_VOL_NONE:
447 return "NONE/UNKNOWN";
448 case HR_VOL_OPTIMAL:
449 return "OPTIMAL";
450 case HR_VOL_FAULTY:
451 return "FAULTY";
452 case HR_VOL_DEGRADED:
453 return "DEGRADED";
454 case HR_VOL_REBUILD:
455 return "REBUILD";
456 default:
457 return "Invalid state value";
458 }
459}
460
461/** Get extent state string.
462 *
463 * @param state State value
464 *
465 * @return State string
466 */
467const char *hr_get_ext_state_str(hr_ext_state_t state)
468{
469 switch (state) {
470 case HR_EXT_NONE:
471 return "NONE/UNKNOWN";
472 case HR_EXT_INVALID:
473 return "INVALID";
474 case HR_EXT_ONLINE:
475 return "ONLINE";
476 case HR_EXT_MISSING:
477 return "MISSING";
478 case HR_EXT_FAILED:
479 return "FAILED";
480 case HR_EXT_REBUILD:
481 return "REBUILD";
482 case HR_EXT_HOTSPARE:
483 return "HOTSPARE";
484 default:
485 return "Invalid state value";
486 }
487}
488
489/** Get volume layout string.
490 *
491 * @param layout Layout value
492 *
493 * @return Layout string
494 */
495const char *hr_get_layout_str(hr_layout_t layout)
496{
497 switch (layout) {
498 case HR_LAYOUT_NONE:
499 return "RAID layout not set";
500 case HR_LAYOUT_RAID4_0:
501 return "RAID-4 Non-Rotating Parity 0";
502 case HR_LAYOUT_RAID4_N:
503 return "RAID-4 Non-Rotating Parity N";
504 case HR_LAYOUT_RAID5_0R:
505 return "RAID-5 Rotating Parity 0 with Data Restart";
506 case HR_LAYOUT_RAID5_NR:
507 return "RAID-5 Rotating Parity N with Data Restart";
508 case HR_LAYOUT_RAID5_NC:
509 return "RAID-5 Rotating Parity N with Data Continuation";
510 default:
511 return "Invalid RAID layout";
512 }
513}
514
515/** Get volume level string.
516 *
517 * @param level Levelvalue
518 *
519 * @return Level string
520 */
521const char *hr_get_level_str(hr_level_t level)
522{
523 switch (level) {
524 case HR_LVL_0:
525 return "stripe (RAID 0)";
526 case HR_LVL_1:
527 return "mirror (RAID 1)";
528 case HR_LVL_4:
529 return "dedicated parity (RAID 4)";
530 case HR_LVL_5:
531 return "distributed parity (RAID 5)";
532 default:
533 return "Invalid RAID level";
534 }
535}
536
537/** Get volume metadata type string.
538 *
539 * @param type Metadata type value
540 *
541 * @return Metadata type string
542 */
543const char *hr_get_metadata_type_str(hr_metadata_type_t type)
544{
545 switch (type) {
546 case HR_METADATA_NATIVE:
547 return "HelenRAID native";
548 case HR_METADATA_GEOM_MIRROR:
549 return "GEOM::MIRROR";
550 case HR_METADATA_GEOM_STRIPE:
551 return "GEOM::STRIPE";
552 case HR_METADATA_SOFTRAID:
553 return "OpenBSD softraid";
554 case HR_METADATA_MD:
555 return "Linux Multiple Device";
556 default:
557 return "Invalid metadata type value";
558 }
559}
560
561/** @}
562 */
Note: See TracBrowser for help on using the repository browser.