diff options
author | Stefan Sperling <stsp@cvs.openbsd.org> | 2021-02-08 11:21:54 +0000 |
---|---|---|
committer | Stefan Sperling <stsp@cvs.openbsd.org> | 2021-02-08 11:21:54 +0000 |
commit | f7d9b9b5db3018abfef29751f141651163e86de0 (patch) | |
tree | c37e76672a85c154fb395d431c55e9d6c997ed23 | |
parent | a733f9a4a40d77e5a21edccaffc0228499806350 (diff) |
Add a RAID1C (raid1 + crypto) softraid(8) discipline.
The RAID1C discipline encrypts data like the CRYPTO discipline, and accepts
multiple chunks during creation and assembly like the RAID1 discipline.
To deal with failing disks a RAID1C volume may be assembled with a smaller
number of chunks than the volume was created with. The volume will then come
up in degraded state. If the volume is now detached and assembled again with
the correct number of chunks, any re-added chunks will require a rebuild.
Consequently, assembling RAID1C volumes requires careful attention to the
chunks passed via 'bioctl -l'. If a chunk is accidentally omitted from the
command line during volume assembly, then this chunk will need to be rebuilt.
At least one known-good chunk is required in order to assemble the volume.
Like CRYPTO, RAID1C supports passphrase and key-disk authentication.
Key-disk based volumes are assembled automatically if the key disk is present
while the system is booting up.
Unlike CRYPTO and RAID1, there is no boot support for RAID1C yet.
RAID1C largely reuses existing code of RAID1 and CRYPTO disciplines.
At present RAID1C's discipline-specific data structure is shared with that
of the CRYPTO discipline to allow re-use of existing CRYPTO code. A custom
RAID1C data structure would require CRYPTO code to access struct sr_crypto
via a pointer instead of via a member field of struct sr_discipline.
ok jsing@
-rw-r--r-- | sys/dev/softraid_raid1c.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/sys/dev/softraid_raid1c.c b/sys/dev/softraid_raid1c.c new file mode 100644 index 00000000000..1040b7d47db --- /dev/null +++ b/sys/dev/softraid_raid1c.c @@ -0,0 +1,325 @@ +/* $OpenBSD: softraid_raid1c.c,v 1.1 2021/02/08 11:21:53 stsp Exp $ */ +/* + * Copyright (c) 2007 Marco Peereboom <marco@peereboom.us> + * Copyright (c) 2008 Hans-Joerg Hoexer <hshoexer@openbsd.org> + * Copyright (c) 2008 Damien Miller <djm@mindrot.org> + * Copyright (c) 2009 Joel Sing <jsing@openbsd.org> + * Copyright (c) 2020 Stefan Sperling <stsp@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "bio.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/disk.h> +#include <sys/rwlock.h> +#include <sys/queue.h> +#include <sys/fcntl.h> +#include <sys/mount.h> +#include <sys/sensors.h> +#include <sys/stat.h> +#include <sys/task.h> +#include <sys/conf.h> +#include <sys/uio.h> + +#include <crypto/cryptodev.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <scsi/scsi_disk.h> + +#include <dev/softraidvar.h> + +/* RAID 1C functions. */ +int sr_raid1c_create(struct sr_discipline *, struct bioc_createraid *, + int, int64_t); +int sr_raid1c_add_offline_chunks(struct sr_discipline *, int); +int sr_raid1c_assemble(struct sr_discipline *, struct bioc_createraid *, + int, void *); +void sr_raid1c_write(struct cryptop *); +int sr_raid1c_rw(struct sr_workunit *); +int sr_raid1c_dev_rw(struct sr_workunit *, struct sr_crypto_wu *); + +/* RAID1 functions */ +extern int sr_raid1_init(struct sr_discipline *sd); +extern int sr_raid1_assemble(struct sr_discipline *, + struct bioc_createraid *, int, void *); +extern int sr_raid1_wu_done(struct sr_workunit *); +extern void sr_raid1_set_chunk_state(struct sr_discipline *, int, int); +extern void sr_raid1_set_vol_state(struct sr_discipline *); + +/* CRYPTO raid functions */ +extern struct sr_crypto_wu *sr_crypto_prepare(struct sr_workunit *, int); +extern int sr_crypto_meta_create(struct sr_discipline *, + struct bioc_createraid *); +extern int sr_crypto_assemble(struct sr_discipline *, + struct bioc_createraid *, int, void *); +extern int sr_crypto_alloc_resources(struct sr_discipline *); +extern void sr_crypto_free_resources(struct sr_discipline *); +extern int sr_crypto_ioctl(struct sr_discipline *, + struct bioc_discipline *); +extern int sr_crypto_meta_opt_handler(struct sr_discipline *, + struct sr_meta_opt_hdr *); +void sr_crypto_done(struct sr_workunit *); + +/* Discipline initialisation. */ +void +sr_raid1c_discipline_init(struct sr_discipline *sd) +{ + int i; + + /* Fill out discipline members. */ + sd->sd_wu_size = sizeof(struct sr_crypto_wu); + sd->sd_type = SR_MD_RAID1C; + strlcpy(sd->sd_name, "RAID1C", sizeof(sd->sd_name)); + sd->sd_capabilities = SR_CAP_SYSTEM_DISK | SR_CAP_AUTO_ASSEMBLE | + SR_CAP_REBUILD | SR_CAP_REDUNDANT; + sd->sd_max_wu = SR_RAID1C_NOWU; + + for (i = 0; i < SR_CRYPTO_MAXKEYS; i++) + sd->mds.mdd_crypto.scr_sid[i] = (u_int64_t)-1; + + /* Setup discipline specific function pointers. */ + sd->sd_alloc_resources = sr_crypto_alloc_resources; + sd->sd_assemble = sr_raid1c_assemble; + sd->sd_create = sr_raid1c_create; + sd->sd_free_resources = sr_crypto_free_resources; + sd->sd_ioctl_handler = sr_crypto_ioctl; + sd->sd_meta_opt_handler = sr_crypto_meta_opt_handler; + sd->sd_scsi_rw = sr_raid1c_rw; + sd->sd_scsi_done = sr_crypto_done; + sd->sd_scsi_wu_done = sr_raid1_wu_done; + sd->sd_set_chunk_state = sr_raid1_set_chunk_state; + sd->sd_set_vol_state = sr_raid1_set_vol_state; +} + +int +sr_raid1c_create(struct sr_discipline *sd, struct bioc_createraid *bc, + int no_chunk, int64_t coerced_size) +{ + int rv; + + if (no_chunk < 2) { + sr_error(sd->sd_sc, "%s requires two or more chunks", + sd->sd_name); + return EINVAL; + } + + sd->sd_meta->ssdi.ssd_size = coerced_size; + + rv = sr_raid1_init(sd); + if (rv) + return rv; + + return sr_crypto_meta_create(sd, bc); +} + +int +sr_raid1c_add_offline_chunks(struct sr_discipline *sd, int no_chunk) +{ + struct sr_chunk *ch_entry, *ch_prev; + struct sr_chunk **chunks; + int c; + + chunks = mallocarray(sd->sd_meta->ssdi.ssd_chunk_no, + sizeof(struct sr_chunk *), M_DEVBUF, M_WAITOK | M_ZERO); + + for (c = 0; c < no_chunk; c++) + chunks[c] = sd->sd_vol.sv_chunks[c]; + + for (c = no_chunk; c < sd->sd_meta->ssdi.ssd_chunk_no; c++) { + ch_prev = chunks[c - 1]; + ch_entry = malloc(sizeof(struct sr_chunk), M_DEVBUF, + M_WAITOK | M_ZERO); + ch_entry->src_meta.scm_status = BIOC_SDOFFLINE; + ch_entry->src_dev_mm = NODEV; + SLIST_INSERT_AFTER(ch_prev, ch_entry, src_link); + chunks[c] = ch_entry; + } + + free(sd->sd_vol.sv_chunks, M_DEVBUF, + sizeof(struct sr_chunk *) * no_chunk); + sd->sd_vol.sv_chunks = chunks; + + return (0); +} + +int +sr_raid1c_assemble(struct sr_discipline *sd, struct bioc_createraid *bc, + int no_chunk, void *data) +{ + int rv; + + /* Create NODEV place-holders for missing chunks. */ + if (no_chunk < sd->sd_meta->ssdi.ssd_chunk_no) { + rv = sr_raid1c_add_offline_chunks(sd, no_chunk); + if (rv) + return (rv); + } + + rv = sr_raid1_assemble(sd, bc, no_chunk, NULL); + if (rv) + return rv; + + return sr_crypto_assemble(sd, bc, no_chunk, data); +} + +int +sr_raid1c_dev_rw(struct sr_workunit *wu, struct sr_crypto_wu *crwu) +{ + struct sr_discipline *sd = wu->swu_dis; + struct scsi_xfer *xs = wu->swu_xs; + struct sr_ccb *ccb; + struct uio *uio; + struct sr_chunk *scp; + int ios, chunk, i, rt; + daddr_t blkno; + + blkno = wu->swu_blk_start; + + if (xs->flags & SCSI_DATA_IN) + ios = 1; + else + ios = sd->sd_meta->ssdi.ssd_chunk_no; + + for (i = 0; i < ios; i++) { + if (xs->flags & SCSI_DATA_IN) { + rt = 0; +ragain: + /* interleave reads */ + chunk = sd->mds.mdd_crypto.scr_raid1.sr1_counter++ % + sd->sd_meta->ssdi.ssd_chunk_no; + scp = sd->sd_vol.sv_chunks[chunk]; + switch (scp->src_meta.scm_status) { + case BIOC_SDONLINE: + case BIOC_SDSCRUB: + break; + + case BIOC_SDOFFLINE: + case BIOC_SDREBUILD: + case BIOC_SDHOTSPARE: + if (rt++ < sd->sd_meta->ssdi.ssd_chunk_no) + goto ragain; + + /* FALLTHROUGH */ + default: + /* volume offline */ + printf("%s: is offline, cannot read\n", + DEVNAME(sd->sd_sc)); + goto bad; + } + } else { + /* writes go on all working disks */ + chunk = i; + scp = sd->sd_vol.sv_chunks[chunk]; + switch (scp->src_meta.scm_status) { + case BIOC_SDONLINE: + if (ISSET(wu->swu_flags, SR_WUF_REBUILD)) + continue; + break; + + case BIOC_SDSCRUB: + case BIOC_SDREBUILD: + break; + + case BIOC_SDHOTSPARE: /* should never happen */ + case BIOC_SDOFFLINE: + continue; + + default: + goto bad; + } + } + + ccb = sr_ccb_rw(sd, chunk, blkno, xs->datalen, xs->data, + xs->flags, 0); + if (!ccb) { + /* should never happen but handle more gracefully */ + printf("%s: %s: too many ccbs queued\n", + DEVNAME(sd->sd_sc), + sd->sd_meta->ssd_devname); + goto bad; + } + if (!ISSET(xs->flags, SCSI_DATA_IN) && + !ISSET(wu->swu_flags, SR_WUF_REBUILD)) { + uio = crwu->cr_crp->crp_buf; + ccb->ccb_buf.b_data = uio->uio_iov->iov_base; + ccb->ccb_opaque = crwu; + } + sr_wu_enqueue_ccb(wu, ccb); + } + + sr_schedule_wu(wu); + + return (0); + +bad: + /* wu is unwound by sr_wu_put */ + if (crwu) + crwu->cr_crp->crp_etype = EINVAL; + return (1); +} + +void +sr_raid1c_write(struct cryptop *crp) +{ + struct sr_crypto_wu *crwu = crp->crp_opaque; + struct sr_workunit *wu = &crwu->cr_wu; + int s; + + DNPRINTF(SR_D_INTR, "%s: sr_raid1c_write: wu %p xs: %p\n", + DEVNAME(wu->swu_dis->sd_sc), wu, wu->swu_xs); + + if (crp->crp_etype) { + /* fail io */ + wu->swu_xs->error = XS_DRIVER_STUFFUP; + s = splbio(); + sr_scsi_done(wu->swu_dis, wu->swu_xs); + splx(s); + } + + sr_raid1c_dev_rw(wu, crwu); +} + +int +sr_raid1c_rw(struct sr_workunit *wu) +{ + struct sr_crypto_wu *crwu; + daddr_t blkno; + int rv = 0; + + DNPRINTF(SR_D_DIS, "%s: sr_raid1c_rw wu %p\n", + DEVNAME(wu->swu_dis->sd_sc), wu); + + if (sr_validate_io(wu, &blkno, "sr_raid1c_rw")) + return (1); + + if (ISSET(wu->swu_xs->flags, SCSI_DATA_OUT) && + !ISSET(wu->swu_flags, SR_WUF_REBUILD)) { + crwu = sr_crypto_prepare(wu, 1); + crwu->cr_crp->crp_callback = sr_raid1c_write; + rv = crypto_dispatch(crwu->cr_crp); + if (rv == 0) + rv = crwu->cr_crp->crp_etype; + } else + rv = sr_raid1c_dev_rw(wu, NULL); + + return (rv); +} |