root/sys/dev/softraidvar.h
/* $OpenBSD: softraidvar.h,v 1.176 2022/12/19 15:27:06 kn Exp $ */
/*
 * Copyright (c) 2006 Marco Peereboom <marco@peereboom.us>
 * Copyright (c) 2008 Chris Kuethe <ckuethe@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.
 */

#ifndef SOFTRAIDVAR_H
#define SOFTRAIDVAR_H

#define SR_META_VERSION         6       /* bump when sr_metadata changes */
#define SR_META_SIZE            64      /* save space at chunk beginning */
#define SR_META_OFFSET          16      /* skip 8192 bytes at chunk beginning */

#define SR_BOOT_OFFSET          (SR_META_OFFSET + SR_META_SIZE)
#define SR_BOOT_LOADER_SIZE     320     /* Size of boot loader storage. */
#define SR_BOOT_LOADER_OFFSET   SR_BOOT_OFFSET
#define SR_BOOT_BLOCKS_SIZE     128     /* Size of boot block storage. */
#define SR_BOOT_BLOCKS_OFFSET   (SR_BOOT_LOADER_OFFSET + SR_BOOT_LOADER_SIZE)
#define SR_BOOT_SIZE            (SR_BOOT_LOADER_SIZE + SR_BOOT_BLOCKS_SIZE)

#define SR_CRYPTO_MAXKEYBYTES   32      /* max bytes in a key (AES-XTS-256) */
#define SR_CRYPTO_MAXKEYS       32      /* max keys per volume */
#define SR_CRYPTO_KEYBITS       512     /* AES-XTS with 2 * 256 bit keys */
#define SR_CRYPTO_KEYBYTES      (SR_CRYPTO_KEYBITS >> 3)
#define SR_CRYPTO_KDFHINTBYTES  256     /* size of opaque KDF hint */
#define SR_CRYPTO_CHECKBYTES    64      /* size of generic key chksum struct */
#define SR_CRYPTO_KEY_BLKSHIFT  30      /* 0.5TB per key */
#define SR_CRYPTO_KEY_BLKSIZE   (1ULL << SR_CRYPTO_KEY_BLKSHIFT)
#define SR_CRYPTO_MAXSIZE       (SR_CRYPTO_KEY_BLKSIZE * SR_CRYPTO_MAXKEYS)

/*
 * sr_crypto_genkdf is a generic hint for the KDF performed in userland and
 * is not interpreted by the kernel.
 */
struct sr_crypto_genkdf {
        u_int32_t       len;
        u_int32_t       type;
#define SR_CRYPTOKDFT_INVALID           0
#define SR_CRYPTOKDFT_PKCS5_PBKDF2      1
#define SR_CRYPTOKDFT_KEYDISK           2
#define SR_CRYPTOKDFT_BCRYPT_PBKDF      3
};

/*
 * sr_crypto_pbkdf is a hint for a PBKDF performed in userland and is not
 * interpreted by the kernel.
 */
struct sr_crypto_pbkdf {
        struct sr_crypto_genkdf generic;
        u_int32_t       rounds;
        u_int8_t        salt[128];
};

/*
 * sr_crypto_kdfinfo is used to copy masking keys and KDF hints from/to
 * userland. The embedded hint structures are not interpreted by the kernel.
 */
struct sr_crypto_kdfinfo {
        u_int32_t       len;
        u_int32_t       flags;
#define SR_CRYPTOKDF_INVALID    (0)
#define SR_CRYPTOKDF_KEY        (1<<0)
#define SR_CRYPTOKDF_HINT       (1<<1)
        u_int8_t        maskkey[SR_CRYPTO_MAXKEYBYTES];
        union {
                struct sr_crypto_genkdf generic;
                struct sr_crypto_pbkdf  pbkdf;
        }               _kdfhint;
#define genkdf          _kdfhint.generic
#define pbkdf           _kdfhint.pbkdf
};

#define SR_IOCTL_GET_KDFHINT            0x01    /* Get KDF hint. */
#define SR_IOCTL_CHANGE_PASSPHRASE      0x02    /* Change passphrase. */

struct sr_crypto_kdfpair {
        struct sr_crypto_kdfinfo *kdfinfo1;
        u_int32_t       kdfsize1;
        struct sr_crypto_kdfinfo *kdfinfo2;
        u_int32_t       kdfsize2;
};

#if defined(_KERNEL) || defined(_STANDALONE)

#include <crypto/md5.h>

#define SR_META_V3_SIZE         64
#define SR_META_V3_OFFSET       16
#define SR_META_V3_DATA_OFFSET  (SR_META_V3_OFFSET + SR_META_V3_SIZE)

#define SR_META_F_NATIVE        0       /* Native metadata format. */
#define SR_META_F_INVALID       -1

#define SR_HEADER_SIZE          (SR_META_SIZE + SR_BOOT_SIZE)
#define SR_DATA_OFFSET          (SR_META_OFFSET + SR_HEADER_SIZE)

#define SR_HOTSPARE_LEVEL       0xffffffff
#define SR_HOTSPARE_VOLID       0xffffffff
#define SR_KEYDISK_LEVEL        0xfffffffe
#define SR_KEYDISK_VOLID        0xfffffffe

#define SR_UUID_MAX             16
struct sr_uuid {
        u_int8_t                sui_id[SR_UUID_MAX];
} __packed;

struct sr_disk {
        dev_t                   sdk_devno;
        SLIST_ENTRY(sr_disk)    sdk_link;
};
SLIST_HEAD(sr_disk_head, sr_disk);

struct sr_metadata {
        struct sr_meta_invariant {
                /* do not change order of ssd_magic, ssd_version */
                u_int64_t       ssd_magic;      /* magic id */
#define SR_MAGIC                0x4d4152436372616dLLU
                u_int32_t       ssd_version;    /* meta data version */
                u_int32_t       ssd_vol_flags;  /* volume specific flags. */
                struct sr_uuid  ssd_uuid;       /* unique identifier */

                /* chunks */
                u_int32_t       ssd_chunk_no;   /* number of chunks */
                u_int32_t       ssd_chunk_id;   /* chunk identifier */

                /* optional */
                u_int32_t       ssd_opt_no;     /* nr of optional md elements */
                u_int32_t       ssd_secsize;

                /* volume metadata */
                u_int32_t       ssd_volid;      /* volume id */
                u_int32_t       ssd_level;      /* raid level */
                int64_t         ssd_size;       /* virt disk size in blocks */
                char            ssd_vendor[8];  /* scsi vendor */
                char            ssd_product[16];/* scsi product */
                char            ssd_revision[4];/* scsi revision */
                /* optional volume members */
                u_int32_t       ssd_strip_size; /* strip size */
        } _sdd_invariant;
#define ssdi                    _sdd_invariant
        /* MD5 of invariant metadata */
        u_int8_t                ssd_checksum[MD5_DIGEST_LENGTH];
        char                    ssd_devname[32];/* /dev/XXXXX */
        u_int32_t               ssd_meta_flags;
#define SR_META_DIRTY           0x1
        u_int32_t               ssd_data_blkno;
        u_int64_t               ssd_ondisk;     /* on disk version counter */
        int64_t                 ssd_rebuild;    /* last block of rebuild */
} __packed;

struct sr_meta_chunk {
        struct sr_meta_chunk_invariant {
                u_int32_t       scm_volid;      /* vd we belong to */
                u_int32_t       scm_chunk_id;   /* chunk id */
                char            scm_devname[32];/* /dev/XXXXX */
                int64_t         scm_size;       /* size of partition in blocks*/
                int64_t         scm_coerced_size; /* coerced sz of part in blk*/
                struct sr_uuid  scm_uuid;       /* unique identifier */
        } _scm_invariant;
#define scmi                    _scm_invariant
        /* MD5 of invariant chunk metadata */
        u_int8_t                scm_checksum[MD5_DIGEST_LENGTH];
        u_int32_t               scm_status;     /* use bio bioc_disk status */
} __packed;

/*
 * Check that HMAC-SHA1_k(decrypted scm_key) == sch_mac, where
 * k = SHA1(masking key)
 */
struct sr_crypto_chk_hmac_sha1 {
        u_int8_t        sch_mac[20];
} __packed;

#define SR_OPT_INVALID          0x00
#define SR_OPT_CRYPTO           0x01
#define SR_OPT_BOOT             0x02
#define SR_OPT_KEYDISK          0x03

struct sr_meta_opt_hdr {
        u_int32_t       som_type;       /* optional metadata type. */
        u_int32_t       som_length;     /* optional metadata length. */
        u_int8_t        som_checksum[MD5_DIGEST_LENGTH];
} __packed;

struct sr_meta_crypto {
        struct sr_meta_opt_hdr  scm_hdr;
        u_int32_t               scm_alg;        /* vol crypto algorithm */
#define SR_CRYPTOA_AES_XTS_128  1
#define SR_CRYPTOA_AES_XTS_256  2
        u_int32_t               scm_flags;      /* key & kdfhint valid */
#define SR_CRYPTOF_INVALID      (0)
#define SR_CRYPTOF_KEY          (1<<0)
#define SR_CRYPTOF_KDFHINT      (1<<1)
        u_int32_t               scm_mask_alg;   /* disk key masking crypt alg */
#define SR_CRYPTOM_AES_ECB_256  1
        u_int32_t               scm_pad1;
        u_int8_t                scm_reserved[64];

        /* symmetric keys used for disk encryption */
        u_int8_t                scm_key[SR_CRYPTO_MAXKEYS][SR_CRYPTO_KEYBYTES];
        /* hint to kdf algorithm (opaque to kernel) */
        u_int8_t                scm_kdfhint[SR_CRYPTO_KDFHINTBYTES];

        u_int32_t               scm_check_alg;  /* key chksum algorithm */
#define SR_CRYPTOC_HMAC_SHA1            1
        u_int32_t               scm_pad2;
        union {
                struct sr_crypto_chk_hmac_sha1  chk_hmac_sha1;
                u_int8_t                        chk_reserved2[64];
        }                       _scm_chk;
#define chk_hmac_sha1   _scm_chk.chk_hmac_sha1
} __packed;

#define SR_MAX_BOOT_DISKS 16
struct sr_meta_boot {
        struct sr_meta_opt_hdr  sbm_hdr;
        u_int32_t               sbm_bootblk_size;
        u_int32_t               sbm_bootldr_size;
        u_char                  sbm_root_duid[8];
        u_char                  sbm_boot_duid[SR_MAX_BOOT_DISKS][8];
} __packed;

struct sr_meta_keydisk {
        struct sr_meta_opt_hdr  skm_hdr;
        u_int8_t                skm_maskkey[SR_CRYPTO_MAXKEYBYTES];
} __packed;

#define SR_OLD_META_OPT_SIZE    2480
#define SR_OLD_META_OPT_OFFSET  8
#define SR_OLD_META_OPT_MD5     (SR_OLD_META_OPT_SIZE - MD5_DIGEST_LENGTH)

struct sr_meta_opt_item {
        struct sr_meta_opt_hdr  *omi_som;
        SLIST_ENTRY(sr_meta_opt_item) omi_link;
};

SLIST_HEAD(sr_meta_opt_head, sr_meta_opt_item);

struct sr_boot_chunk {
        struct sr_metadata *sbc_metadata;
        dev_t           sbc_mm;                 /* Device major/minor. */

        u_int32_t       sbc_chunk_id;           /* Chunk ID. */
        u_int32_t       sbc_state;              /* Chunk state. */
        u_int32_t       sbc_disk;               /* Disk number. */
        int             sbc_part;               /* Partition number. */
        u_int64_t       sbc_ondisk;             /* Ondisk version. */

        void            *sbc_diskinfo;          /* MD disk information. */

        SLIST_ENTRY(sr_boot_chunk) sbc_link;
};

SLIST_HEAD(sr_boot_chunk_head, sr_boot_chunk);

struct sr_boot_volume {
        struct sr_uuid  sbv_uuid;               /* Volume UUID. */
        u_int32_t       sbv_level;              /* RAID Level. */
        u_int32_t       sbv_volid;              /* Volume ID. */
        u_int32_t       sbv_chunk_no;           /* Number of chunks. */
        u_int32_t       sbv_flags;              /* Volume specific flags. */
        u_int32_t       sbv_state;              /* Volume state. */
        int64_t         sbv_size;               /* Virtual disk size. */
        u_int32_t       sbv_secsize;            /* Sector size */
        u_int32_t       sbv_data_blkno;         /* Data offset. */
        u_int64_t       sbv_ondisk;             /* Ondisk version. */

        u_int32_t       sbv_chunks_found;       /* Number of chunks found. */
        u_int32_t       sbv_unit;               /* Disk unit number. */
        char            sbv_part;               /* Partition opened. */
        void            *sbv_diskinfo;          /* MD disk information. */

        u_int8_t        *sbv_keys;              /* Disk keys for volume. */
        u_int8_t        *sbv_maskkey;           /* Mask key for disk keys. */

        struct sr_boot_chunk_head sbv_chunks;   /* List of chunks. */
        struct sr_meta_opt_head sbv_meta_opt;   /* List of optional metadata. */

        SLIST_ENTRY(sr_boot_volume)     sbv_link;
};

SLIST_HEAD(sr_boot_volume_head, sr_boot_volume);

#endif /* _KERNEL | _STANDALONE */

#ifdef _KERNEL

#include <dev/biovar.h>

#include <sys/buf.h>
#include <sys/queue.h>
#include <sys/rwlock.h>
#include <sys/task.h>

#include <scsi/scsi_all.h>
#include <scsi/scsi_disk.h>
#include <scsi/scsiconf.h>

#define DEVNAME(_s)     ((_s)->sc_dev.dv_xname)

/* #define SR_DEBUG */
#ifdef SR_DEBUG
extern u_int32_t                sr_debug;
#define DNPRINTF(n,x...)        do { if (sr_debug & n) printf(x); } while(0)
#define SR_D_CMD                0x0001
#define SR_D_INTR               0x0002
#define SR_D_MISC               0x0004
#define SR_D_IOCTL              0x0008
#define SR_D_CCB                0x0010
#define SR_D_WU                 0x0020
#define SR_D_META               0x0040
#define SR_D_DIS                0x0080
#define SR_D_STATE              0x0100
#define SR_D_REBUILD            0x0200
#else
#define DNPRINTF(n,x...)
#endif

#define SR_MAX_LD               256
#define SR_MAX_CMDS             16
#define SR_MAX_STATES           7
#define SR_VM_IGNORE_DIRTY      1
#define SR_REBUILD_IO_SIZE      128 /* blocks */

extern struct sr_uuid   sr_bootuuid;
extern u_int8_t         sr_bootkey[SR_CRYPTO_MAXKEYBYTES];

/* forward define to prevent dependency goo */
struct sr_softc;

struct sr_ccb {
        struct buf              ccb_buf;        /* MUST BE FIRST!! */

        struct sr_workunit      *ccb_wu;
        struct sr_discipline    *ccb_dis;

        int                     ccb_target;
        int                     ccb_state;
#define SR_CCB_FREE             0
#define SR_CCB_INPROGRESS       1
#define SR_CCB_OK               2
#define SR_CCB_FAILED           3

        int                     ccb_flags;
#define SR_CCBF_FREEBUF         (1<<0)          /* free ccb_buf.b_data */

        void                    *ccb_opaque; /* discipline usable pointer */

        TAILQ_ENTRY(sr_ccb)     ccb_link;
};

TAILQ_HEAD(sr_ccb_list, sr_ccb);

struct sr_workunit {
        struct scsi_xfer        *swu_xs;
        struct sr_discipline    *swu_dis;

        int                     swu_state;
#define SR_WU_FREE              0
#define SR_WU_INPROGRESS        1
#define SR_WU_OK                2
#define SR_WU_FAILED            3
#define SR_WU_PARTIALLYFAILED   4
#define SR_WU_DEFERRED          5
#define SR_WU_PENDING           6
#define SR_WU_RESTART           7
#define SR_WU_REQUEUE           8
#define SR_WU_CONSTRUCT         9

        int                     swu_flags;      /* additional hints */
#define SR_WUF_REBUILD          (1<<0)          /* rebuild io */
#define SR_WUF_REBUILDIOCOMP    (1<<1)          /* rebuild io complete */
#define SR_WUF_FAIL             (1<<2)          /* RAID6: failure */
#define SR_WUF_FAILIOCOMP       (1<<3)
#define SR_WUF_WAKEUP           (1<<4)          /* Wakeup on I/O completion. */
#define SR_WUF_DISCIPLINE       (1<<5)          /* Discipline specific I/O. */
#define SR_WUF_FAKE             (1<<6)          /* Faked workunit. */

        /* workunit io range */
        daddr_t                 swu_blk_start;
        daddr_t                 swu_blk_end;

        /* number of ios that makes up the whole work unit */
        u_int32_t               swu_io_count;

        /* in flight totals */
        u_int32_t               swu_ios_complete;
        u_int32_t               swu_ios_failed;
        u_int32_t               swu_ios_succeeded;

        /* colliding wu */
        struct sr_workunit      *swu_collider;

        /* all ios that make up this workunit */
        struct sr_ccb_list      swu_ccb;

        /* task memory */
        struct task             swu_task;
        int                     swu_cb_active;  /* in callback */

        TAILQ_ENTRY(sr_workunit) swu_link;      /* Link in processing queue. */
        TAILQ_ENTRY(sr_workunit) swu_next;      /* Next work unit in chain. */
};

TAILQ_HEAD(sr_wu_list, sr_workunit);

/* RAID 0 */
#define SR_RAID0_NOWU           16
struct sr_raid0 {
        int32_t                 sr0_strip_bits;
};

/* RAID 1 */
#define SR_RAID1_NOWU           16
struct sr_raid1 {
        u_int32_t               sr1_counter;
};

/* RAID 5 */
#define SR_RAID5_NOWU           16
struct sr_raid5 {
        int32_t                 sr5_strip_bits;
};

/* RAID 6 */
#define SR_RAID6_NOWU           16
struct sr_raid6 {
        int32_t                 sr6_strip_bits;
};

/* CRYPTO */
TAILQ_HEAD(sr_crypto_wu_head, sr_crypto_wu);
#define SR_CRYPTO_NOWU          16

/*
 * The per-I/O data that we need to preallocate. We cannot afford to allow I/O
 * to start failing when memory pressure kicks in. We can store this in the WU
 * because we assert that only one ccb per WU will ever be active during crypto.
 */
struct sr_crypto_wu {
        struct sr_workunit               cr_wu;         /* Must be first. */
        struct uio                       cr_uio;
        struct iovec                     cr_iov;
        struct cryptop                  *cr_crp;
        void                            *cr_dmabuf;
};

struct sr_crypto {
        struct sr_meta_crypto   *scr_meta;
        struct sr_chunk         *key_disk;

        int                     scr_alg;
        int                     scr_klen;

        /* XXX only keep scr_sid over time */
        u_int8_t                scr_key[SR_CRYPTO_MAXKEYS][SR_CRYPTO_KEYBYTES];
        u_int8_t                scr_maskkey[SR_CRYPTO_MAXKEYBYTES];
        u_int64_t               scr_sid[SR_CRYPTO_MAXKEYS];
};

#define SR_CONCAT_NOWU          16
struct sr_concat {
};

/* RAID 1C */
#define SR_RAID1C_NOWU          16
struct sr_raid1c {
        struct sr_crypto        sr1c_crypto;
        struct sr_raid1         sr1c_raid1;
};

struct sr_chunk {
        struct sr_meta_chunk    src_meta;       /* chunk meta data */

        /* runtime data */
        dev_t                   src_dev_mm;     /* major/minor */
        struct vnode            *src_vn;        /* vnode */

        /* helper members before metadata makes it onto the chunk  */
        int                     src_meta_ondisk;/* set when meta is on disk */
        char                    src_devname[32];
        u_char                  src_duid[8];    /* Chunk disklabel UID. */
        int64_t                 src_size;       /* in blocks */
        u_int32_t               src_secsize;

        SLIST_ENTRY(sr_chunk)   src_link;
};

SLIST_HEAD(sr_chunk_head, sr_chunk);

struct sr_volume {
        /* runtime data */
        struct sr_chunk_head    sv_chunk_list;  /* linked list of all chunks */
        struct sr_chunk         **sv_chunks;    /* array to same chunks */
        int64_t                 sv_chunk_minsz; /* Size of smallest chunk. */
        int64_t                 sv_chunk_maxsz; /* Size of largest chunk. */

        /* sensors */
        struct ksensor          sv_sensor;
        int                     sv_sensor_attached;
};

struct sr_discipline {
        struct sr_softc         *sd_sc;         /* link back to sr softc */
        size_t                  sd_wu_size;     /* alloc and free size */
        u_int8_t                sd_type;        /* type of discipline */
#define SR_MD_RAID0             0
#define SR_MD_RAID1             1
#define SR_MD_RAID5             2
#define SR_MD_CACHE             3
#define SR_MD_CRYPTO            4
        /* AOE was 5 and 6. */
        /* SR_MD_RAID4 was 7. */
#define SR_MD_RAID6             8
#define SR_MD_CONCAT            9
#define SR_MD_RAID1C            10
        char                    sd_name[10];    /* human readable discipline name */
        u_int16_t               sd_target;      /* scsibus target discipline uses */

        u_int32_t               sd_capabilities;
#define SR_CAP_SYSTEM_DISK      0x00000001      /* Attaches as a system disk. */
#define SR_CAP_AUTO_ASSEMBLE    0x00000002      /* Can auto assemble. */
#define SR_CAP_REBUILD          0x00000004      /* Supports rebuild. */
#define SR_CAP_NON_COERCED      0x00000008      /* Uses non-coerced size. */
#define SR_CAP_REDUNDANT        0x00000010      /* Redundant copies of data. */

        union {
            struct sr_raid0     mdd_raid0;
            struct sr_raid1     mdd_raid1;
            struct sr_raid5     mdd_raid5;
            struct sr_raid6     mdd_raid6;
            struct sr_concat    mdd_concat;
#ifdef CRYPTO
            struct sr_crypto    mdd_crypto;
            struct sr_raid1c    mdd_raid1c;
#endif /* CRYPTO */
        }                       sd_dis_specific;/* dis specific members */
#define mds                     sd_dis_specific

        struct taskq            *sd_taskq;

        /* discipline metadata */
        struct sr_metadata      *sd_meta;       /* in memory copy of metadata */
        void                    *sd_meta_foreign; /* non native metadata */
        u_int32_t               sd_meta_flags;
        int                     sd_meta_type;   /* metadata functions */
        struct sr_meta_opt_head sd_meta_opt; /* optional metadata. */

        int                     sd_sync;
        int                     sd_must_flush;

        int                     sd_deleted;

        /* discipline volume */
        struct sr_volume        sd_vol;         /* volume associated */
        int                     sd_vol_status;  /* runtime vol status */
        /* discipline resources */
        struct sr_ccb           *sd_ccb;
        struct sr_ccb_list      sd_ccb_freeq;
        u_int32_t               sd_max_ccb_per_wu;

        struct sr_wu_list       sd_wu;          /* all workunits */
        u_int32_t               sd_max_wu;
        int                     sd_reb_active;  /* rebuild in progress */
        int                     sd_reb_abort;   /* abort rebuild */
        int                     sd_ready;       /* fully operational */

        struct sr_wu_list       sd_wu_freeq;    /* free wu queue */
        struct sr_wu_list       sd_wu_pendq;    /* pending wu queue */
        struct sr_wu_list       sd_wu_defq;     /* deferred wu queue */

        struct mutex            sd_wu_mtx;
        struct scsi_iopool      sd_iopool;

        /* discipline stats */
        int                     sd_wu_pending;
        u_int64_t               sd_wu_collisions;

        /* discipline functions */
        int                     (*sd_create)(struct sr_discipline *,
                                    struct bioc_createraid *, int, int64_t);
        int                     (*sd_assemble)(struct sr_discipline *,
                                    struct bioc_createraid *, int, void *);
        int                     (*sd_alloc_resources)(struct sr_discipline *);
        void                    (*sd_free_resources)(struct sr_discipline *);
        int                     (*sd_ioctl_handler)(struct sr_discipline *,
                                    struct bioc_discipline *);
        int                     (*sd_start_discipline)(struct sr_discipline *);
        void                    (*sd_set_chunk_state)(struct sr_discipline *,
                                    int, int);
        void                    (*sd_set_vol_state)(struct sr_discipline *);
        int                     (*sd_openings)(struct sr_discipline *);
        int                     (*sd_meta_opt_handler)(struct sr_discipline *,
                                    struct sr_meta_opt_hdr *);
        void                    (*sd_rebuild)(struct sr_discipline *);

        /* SCSI emulation */
        struct scsi_sense_data  sd_scsi_sense;
        int                     (*sd_scsi_rw)(struct sr_workunit *);
        void                    (*sd_scsi_intr)(struct buf *);
        int                     (*sd_scsi_wu_done)(struct sr_workunit *);
        void                    (*sd_scsi_done)(struct sr_workunit *);
        int                     (*sd_scsi_sync)(struct sr_workunit *);
        int                     (*sd_scsi_tur)(struct sr_workunit *);
        int                     (*sd_scsi_start_stop)(struct sr_workunit *);
        int                     (*sd_scsi_inquiry)(struct sr_workunit *);
        int                     (*sd_scsi_read_cap)(struct sr_workunit *);
        int                     (*sd_scsi_req_sense)(struct sr_workunit *);

        /* background operation */
        struct proc             *sd_background_proc;

        /* Tasks. */
        struct task             sd_meta_save_task;
        struct task             sd_hotspare_rebuild_task;

        TAILQ_ENTRY(sr_discipline) sd_link;
};

TAILQ_HEAD(sr_discipline_list, sr_discipline);

struct sr_softc {
        struct device           sc_dev;

        struct rwlock           sc_lock;

        struct bio_status       sc_status;      /* Status and messages. */

        struct sr_chunk_head    sc_hotspare_list;       /* List of hotspares. */
        struct rwlock           sc_hs_lock;     /* Lock for hotspares list. */
        int                     sc_hotspare_no; /* Number of hotspares. */

        struct ksensordev       sc_sensordev;
        struct sensor_task      *sc_sensor_task;

        struct scsibus_softc    *sc_scsibus;

        /* The target lookup has to be cheap since it happens for each I/O. */
        struct sr_discipline    *sc_targets[SR_MAX_LD];
        struct sr_discipline_list sc_dis_list;
};

/* hotplug */
void                    sr_hotplug_register(struct sr_discipline *, void *);
void                    sr_hotplug_unregister(struct sr_discipline *, void *);

/* Hotspare and rebuild. */
void                    sr_hotspare_rebuild_callback(void *);

/* work units & ccbs */
int                     sr_ccb_alloc(struct sr_discipline *);
void                    sr_ccb_free(struct sr_discipline *);
struct sr_ccb           *sr_ccb_get(struct sr_discipline *);
void                    sr_ccb_put(struct sr_ccb *);
struct sr_ccb           *sr_ccb_rw(struct sr_discipline *, int, daddr_t,
                            long, u_int8_t *, int, int);
void                    sr_ccb_done(struct sr_ccb *);
int                     sr_wu_alloc(struct sr_discipline *);
void                    sr_wu_free(struct sr_discipline *);
void                    *sr_wu_get(void *);
void                    sr_wu_put(void *, void *);
void                    sr_wu_init(struct sr_discipline *,
                            struct sr_workunit *);
void                    sr_wu_enqueue_ccb(struct sr_workunit *,
                            struct sr_ccb *);
void                    sr_wu_release_ccbs(struct sr_workunit *);
void                    sr_wu_done(struct sr_workunit *);

/* misc functions */
void                    sr_info(struct sr_softc *, const char *, ...);
void                    sr_warn(struct sr_softc *, const char *, ...);
void                    sr_error(struct sr_softc *, const char *, ...);
int32_t                 sr_validate_stripsize(u_int32_t);
int                     sr_meta_read(struct sr_discipline *);
int                     sr_meta_native_read(struct sr_discipline *, dev_t,
                            struct sr_metadata *, void *);
int                     sr_meta_validate(struct sr_discipline *, dev_t,
                            struct sr_metadata *, void *);
void                    sr_meta_save_callback(void *);
int                     sr_meta_save(struct sr_discipline *, u_int32_t);
void                    sr_meta_getdevname(struct sr_softc *, dev_t, char *,
                            int);
void                    sr_meta_opt_load(struct sr_softc *,
                            struct sr_metadata *, struct sr_meta_opt_head *);
void                    *sr_block_get(struct sr_discipline *, long);
void                    sr_block_put(struct sr_discipline *, void *, int);
void                    sr_checksum(struct sr_softc *, void *, void *,
                            u_int32_t);
int                     sr_validate_io(struct sr_workunit *, daddr_t *,
                            char *);
void                    sr_schedule_wu(struct sr_workunit *);
void                    sr_scsi_done(struct sr_discipline *,
                            struct scsi_xfer *);
struct sr_workunit      *sr_scsi_wu_get(struct sr_discipline *, int);
void                    sr_scsi_wu_put(struct sr_discipline *,
                            struct sr_workunit *);
int                     sr_chunk_in_use(struct sr_softc *, dev_t);
int                     sr_rebuild_percent(struct sr_discipline *);

/* discipline functions */
int                     sr_raid_inquiry(struct sr_workunit *);
int                     sr_raid_read_cap(struct sr_workunit *);
int                     sr_raid_tur(struct sr_workunit *);
int                     sr_raid_request_sense( struct sr_workunit *);
int                     sr_raid_start_stop(struct sr_workunit *);
int                     sr_raid_sync(struct sr_workunit *);
void                    sr_raid_intr(struct buf *);
void                    sr_raid_startwu(struct sr_workunit *);
void                    sr_raid_recreate_wu(struct sr_workunit *);

/* Discipline specific initialisation. */
void                    sr_raid0_discipline_init(struct sr_discipline *);
void                    sr_raid1_discipline_init(struct sr_discipline *);
void                    sr_raid5_discipline_init(struct sr_discipline *);
void                    sr_raid6_discipline_init(struct sr_discipline *);
void                    sr_crypto_discipline_init(struct sr_discipline *);
void                    sr_concat_discipline_init(struct sr_discipline *);
void                    sr_raid1c_discipline_init(struct sr_discipline *);

/* Crypto discipline hooks. */
int                     sr_crypto_get_kdf(struct bioc_createraid *,
                            struct sr_discipline *, struct sr_crypto *);
int                     sr_crypto_create_keys(struct sr_discipline *,
                            struct sr_crypto *);
struct sr_chunk *       sr_crypto_create_key_disk(struct sr_discipline *,
                            struct sr_crypto *, dev_t);
struct sr_chunk *       sr_crypto_read_key_disk(struct sr_discipline *,
                            struct sr_crypto *, dev_t);

/* Hibernate I/O function */
int                     sr_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr,
                            size_t size, int op, void *page);

#ifdef SR_DEBUG
void                    sr_dump_block(void *, int);
void                    sr_dump_mem(u_int8_t *, int);
#endif

#endif /* _KERNEL */

#endif /* SOFTRAIDVAR_H */