18dab5b0mav/*-
2a82e3a8pfg * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3a82e3a8pfg *
48dab5b0mav * Copyright (c) 2011 Alexander Motin <mav@FreeBSD.org>
5c283985mav * Copyright (c) 2000 - 2008 S��ren Schmidt <sos@FreeBSD.org>
68dab5b0mav * All rights reserved.
78dab5b0mav *
88dab5b0mav * Redistribution and use in source and binary forms, with or without
98dab5b0mav * modification, are permitted provided that the following conditions
108dab5b0mav * are met:
118dab5b0mav * 1. Redistributions of source code must retain the above copyright
128dab5b0mav *    notice, this list of conditions and the following disclaimer.
138dab5b0mav * 2. Redistributions in binary form must reproduce the above copyright
148dab5b0mav *    notice, this list of conditions and the following disclaimer in the
158dab5b0mav *    documentation and/or other materials provided with the distribution.
168dab5b0mav *
178dab5b0mav * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
188dab5b0mav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
198dab5b0mav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
208dab5b0mav * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
218dab5b0mav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
228dab5b0mav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
238dab5b0mav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
248dab5b0mav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
258dab5b0mav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
268dab5b0mav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
278dab5b0mav * SUCH DAMAGE.
288dab5b0mav */
298dab5b0mav
308dab5b0mav#include <sys/cdefs.h>
318dab5b0mav__FBSDID("$FreeBSD$");
328dab5b0mav
338dab5b0mav#include <sys/param.h>
348dab5b0mav#include <sys/bio.h>
358dab5b0mav#include <sys/endian.h>
368dab5b0mav#include <sys/kernel.h>
378dab5b0mav#include <sys/kobj.h>
388dab5b0mav#include <sys/limits.h>
398dab5b0mav#include <sys/lock.h>
408dab5b0mav#include <sys/malloc.h>
418dab5b0mav#include <sys/mutex.h>
428dab5b0mav#include <sys/systm.h>
438dab5b0mav#include <sys/taskqueue.h>
448dab5b0mav#include <geom/geom.h>
4510d53fccem#include <geom/geom_dbg.h>
468dab5b0mav#include "geom/raid/g_raid.h"
478dab5b0mav#include "g_raid_md_if.h"
488dab5b0mav
498dab5b0mavstatic MALLOC_DEFINE(M_MD_SII, "md_sii_data", "GEOM_RAID SiI metadata");
508dab5b0mav
518dab5b0mavstruct sii_raid_conf {
528dab5b0mav	uint16_t	ata_params_00_53[54];
538dab5b0mav	uint64_t	total_sectors;		/* 54 - 57 */
548dab5b0mav	uint16_t	ata_params_58_81[72];
558dab5b0mav	uint16_t	product_id;		/* 130 */
568dab5b0mav	uint16_t	vendor_id;		/* 131 */
578dab5b0mav	uint16_t	version_minor;		/* 132 */
588dab5b0mav	uint16_t	version_major;		/* 133 */
598dab5b0mav	uint8_t		timestamp[6];		/* 134 - 136 */
608dab5b0mav	uint16_t	strip_sectors;		/* 137 */
618dab5b0mav	uint16_t	dummy_2;
628dab5b0mav	uint8_t		disk_number;		/* 139 */
638dab5b0mav	uint8_t		type;
648dab5b0mav#define SII_T_RAID0             0x00
658dab5b0mav#define SII_T_RAID1             0x01
668dab5b0mav#define SII_T_RAID01            0x02
678dab5b0mav#define SII_T_SPARE             0x03
688dab5b0mav#define SII_T_CONCAT            0x04
698dab5b0mav#define SII_T_RAID5             0x10
708dab5b0mav#define SII_T_RESERVED          0xfd
718dab5b0mav#define SII_T_JBOD              0xff
728dab5b0mav
738dab5b0mav	uint8_t		raid0_disks;		/* 140 */
748dab5b0mav	uint8_t		raid0_ident;
758dab5b0mav	uint8_t		raid1_disks;		/* 141 */
768dab5b0mav	uint8_t		raid1_ident;
778dab5b0mav	uint64_t	rebuild_lba;		/* 142 - 145 */
788dab5b0mav	uint32_t	generation;		/* 146 - 147 */
798dab5b0mav	uint8_t		disk_status;		/* 148 */
808dab5b0mav#define SII_S_CURRENT           0x01
818dab5b0mav#define SII_S_REBUILD           0x02
828dab5b0mav#define SII_S_DROPPED           0x03
838dab5b0mav#define SII_S_REMOVED           0x04
848dab5b0mav
858dab5b0mav	uint8_t		raid_status;
868dab5b0mav#define SII_S_ONLINE            0x01
878dab5b0mav#define SII_S_AVAILABLE         0x02
888dab5b0mav
898dab5b0mav	uint8_t		raid_location;		/* 149 */
908dab5b0mav	uint8_t		disk_location;
918dab5b0mav	uint8_t		auto_rebuild;		/* 150 */
928dab5b0mav#define SII_R_REBUILD           0x00
938dab5b0mav#define SII_R_NOREBUILD         0xff
948dab5b0mav
958dab5b0mav	uint8_t		dummy_3;
968dab5b0mav	uint8_t		name[16];		/* 151 - 158 */
978dab5b0mav	uint16_t	checksum;		/* 159 */
988dab5b0mav	uint16_t	ata_params_160_255[96];
998dab5b0mav} __packed;
1008dab5b0mav
1018dab5b0mavstruct g_raid_md_sii_perdisk {
1028dab5b0mav	struct sii_raid_conf	*pd_meta;
1038dab5b0mav	int			 pd_disk_pos;
1048dab5b0mav	off_t			 pd_disk_size;
1058dab5b0mav};
1068dab5b0mav
1078dab5b0mavstruct g_raid_md_sii_object {
1088dab5b0mav	struct g_raid_md_object	 mdio_base;
1098dab5b0mav	uint8_t			 mdio_timestamp[6];
1108dab5b0mav	uint8_t			 mdio_location;
1118dab5b0mav	uint32_t		 mdio_generation;
1128dab5b0mav	struct sii_raid_conf	*mdio_meta;
1138dab5b0mav	struct callout		 mdio_start_co;	/* STARTING state timer. */
1148dab5b0mav	int			 mdio_total_disks;
1158dab5b0mav	int			 mdio_disks_present;
1168dab5b0mav	int			 mdio_started;
1178dab5b0mav	int			 mdio_incomplete;
1188dab5b0mav	struct root_hold_token	*mdio_rootmount; /* Root mount delay token. */
1198dab5b0mav};
1208dab5b0mav
1218dab5b0mavstatic g_raid_md_create_t g_raid_md_create_sii;
1228dab5b0mavstatic g_raid_md_taste_t g_raid_md_taste_sii;
1238dab5b0mavstatic g_raid_md_event_t g_raid_md_event_sii;
1248dab5b0mavstatic g_raid_md_ctl_t g_raid_md_ctl_sii;
1258dab5b0mavstatic g_raid_md_write_t g_raid_md_write_sii;
1268dab5b0mavstatic g_raid_md_fail_disk_t g_raid_md_fail_disk_sii;
1278dab5b0mavstatic g_raid_md_free_disk_t g_raid_md_free_disk_sii;
1288dab5b0mavstatic g_raid_md_free_t g_raid_md_free_sii;
1298dab5b0mav
1308dab5b0mavstatic kobj_method_t g_raid_md_sii_methods[] = {
1318dab5b0mav	KOBJMETHOD(g_raid_md_create,	g_raid_md_create_sii),
1328dab5b0mav	KOBJMETHOD(g_raid_md_taste,	g_raid_md_taste_sii),
1338dab5b0mav	KOBJMETHOD(g_raid_md_event,	g_raid_md_event_sii),
1348dab5b0mav	KOBJMETHOD(g_raid_md_ctl,	g_raid_md_ctl_sii),
1358dab5b0mav	KOBJMETHOD(g_raid_md_write,	g_raid_md_write_sii),
1368dab5b0mav	KOBJMETHOD(g_raid_md_fail_disk,	g_raid_md_fail_disk_sii),
1378dab5b0mav	KOBJMETHOD(g_raid_md_free_disk,	g_raid_md_free_disk_sii),
1388dab5b0mav	KOBJMETHOD(g_raid_md_free,	g_raid_md_free_sii),
1398dab5b0mav	{ 0, 0 }
1408dab5b0mav};
1418dab5b0mav
1428dab5b0mavstatic struct g_raid_md_class g_raid_md_sii_class = {
1438dab5b0mav	"SiI",
1448dab5b0mav	g_raid_md_sii_methods,
1458dab5b0mav	sizeof(struct g_raid_md_sii_object),
146db9e01amav	.mdc_enable = 1,
1478dab5b0mav	.mdc_priority = 100
1488dab5b0mav};
1498dab5b0mav
1508dab5b0mavstatic void
1518dab5b0mavg_raid_md_sii_print(struct sii_raid_conf *meta)
1528dab5b0mav{
1538dab5b0mav
1548dab5b0mav	if (g_raid_debug < 1)
1558dab5b0mav		return;
1568dab5b0mav
1578dab5b0mav	printf("********* ATA SiI RAID Metadata *********\n");
1588dab5b0mav	printf("total_sectors       %llu\n",
1598dab5b0mav	    (long long unsigned)meta->total_sectors);
1608dab5b0mav	printf("product_id          0x%04x\n", meta->product_id);
1618dab5b0mav	printf("vendor_id           0x%04x\n", meta->vendor_id);
1628dab5b0mav	printf("version_minor       0x%04x\n", meta->version_minor);
1638dab5b0mav	printf("version_major       0x%04x\n", meta->version_major);
1648dab5b0mav	printf("timestamp           0x%02x%02x%02x%02x%02x%02x\n",
1658dab5b0mav	    meta->timestamp[5], meta->timestamp[4], meta->timestamp[3],
1668dab5b0mav	    meta->timestamp[2], meta->timestamp[1], meta->timestamp[0]);
1678dab5b0mav	printf("strip_sectors       %d\n", meta->strip_sectors);
1688dab5b0mav	printf("disk_number         %d\n", meta->disk_number);
1698dab5b0mav	printf("type                0x%02x\n", meta->type);
1708dab5b0mav	printf("raid0_disks         %d\n", meta->raid0_disks);
1718dab5b0mav	printf("raid0_ident         %d\n", meta->raid0_ident);
1728dab5b0mav	printf("raid1_disks         %d\n", meta->raid1_disks);
1738dab5b0mav	printf("raid1_ident         %d\n", meta->raid1_ident);
1748dab5b0mav	printf("rebuild_lba         %llu\n",
1758dab5b0mav	    (long long unsigned)meta->rebuild_lba);
1768dab5b0mav	printf("generation          %d\n", meta->generation);
1778dab5b0mav	printf("disk_status         %d\n", meta->disk_status);
1788dab5b0mav	printf("raid_status         %d\n", meta->raid_status);
1798dab5b0mav	printf("raid_location       %d\n", meta->raid_location);
1808dab5b0mav	printf("disk_location       %d\n", meta->disk_location);
1818dab5b0mav	printf("auto_rebuild        %d\n", meta->auto_rebuild);
1828dab5b0mav	printf("name                <%.16s>\n", meta->name);
1838dab5b0mav	printf("checksum            0x%04x\n", meta->checksum);
1848dab5b0mav	printf("=================================================\n");
1858dab5b0mav}
1868dab5b0mav
1878dab5b0mavstatic struct sii_raid_conf *
1888dab5b0mavsii_meta_copy(struct sii_raid_conf *meta)
1898dab5b0mav{
1908dab5b0mav	struct sii_raid_conf *nmeta;
1918dab5b0mav
1928dab5b0mav	nmeta = malloc(sizeof(*meta), M_MD_SII, M_WAITOK);
1938dab5b0mav	memcpy(nmeta, meta, sizeof(*meta));
1948dab5b0mav	return (nmeta);
1958dab5b0mav}
1968dab5b0mav
1978dab5b0mavstatic int
1988dab5b0mavsii_meta_total_disks(struct sii_raid_conf *meta)
1998dab5b0mav{
2008dab5b0mav
2018dab5b0mav	switch (meta->type) {
2028dab5b0mav	case SII_T_RAID0:
2038dab5b0mav	case SII_T_RAID5:
2048dab5b0mav	case SII_T_CONCAT:
2058dab5b0mav		return (meta->raid0_disks);
2068dab5b0mav	case SII_T_RAID1:
2078dab5b0mav		return (meta->raid1_disks);
2088dab5b0mav	case SII_T_RAID01:
2098dab5b0mav		return (meta->raid0_disks * meta->raid1_disks);
2108dab5b0mav	case SII_T_SPARE:
2118dab5b0mav	case SII_T_JBOD:
2128dab5b0mav		return (1);
2138dab5b0mav	}
2148dab5b0mav	return (0);
2158dab5b0mav}
2168dab5b0mav
2178dab5b0mavstatic int
2188dab5b0mavsii_meta_disk_pos(struct sii_raid_conf *meta, struct sii_raid_conf *pdmeta)
2198dab5b0mav{
2208dab5b0mav
2218dab5b0mav	if (pdmeta->type == SII_T_SPARE)
2228dab5b0mav		return (-3);
2238dab5b0mav
2248dab5b0mav	if (memcmp(&meta->timestamp, &pdmeta->timestamp, 6) != 0)
2258dab5b0mav		return (-1);
2268dab5b0mav
2278dab5b0mav	switch (pdmeta->type) {
2288dab5b0mav	case SII_T_RAID0:
2298dab5b0mav	case SII_T_RAID1:
2308dab5b0mav	case SII_T_RAID5:
2318dab5b0mav	case SII_T_CONCAT:
2328dab5b0mav		return (pdmeta->disk_number);
2338dab5b0mav	case SII_T_RAID01:
2348dab5b0mav		return (pdmeta->raid1_ident * pdmeta->raid1_disks +
2358dab5b0mav		    pdmeta->raid0_ident);
2368dab5b0mav	case SII_T_JBOD:
2378dab5b0mav		return (0);
2388dab5b0mav	}
2398dab5b0mav	return (-1);
2408dab5b0mav}
2418dab5b0mav
2428dab5b0mavstatic void
2438dab5b0mavsii_meta_get_name(struct sii_raid_conf *meta, char *buf)
2448dab5b0mav{
2458dab5b0mav	int i;
2468dab5b0mav
2478dab5b0mav	strncpy(buf, meta->name, 16);
2488dab5b0mav	buf[16] = 0;
2498dab5b0mav	for (i = 15; i >= 0; i--) {
2508dab5b0mav		if (buf[i] > 0x20)
2518dab5b0mav			break;
2528dab5b0mav		buf[i] = 0;
2538dab5b0mav	}
2548dab5b0mav}
2558dab5b0mav
2568dab5b0mavstatic void
2578dab5b0mavsii_meta_put_name(struct sii_raid_conf *meta, char *buf)
2588dab5b0mav{
2598dab5b0mav
2608dab5b0mav	memset(meta->name, 0x20, 16);
2618dab5b0mav	memcpy(meta->name, buf, MIN(strlen(buf), 16));
2628dab5b0mav}
2638dab5b0mav
2648dab5b0mavstatic struct sii_raid_conf *
2658dab5b0mavsii_meta_read(struct g_consumer *cp)
2668dab5b0mav{
2678dab5b0mav	struct g_provider *pp;
2688dab5b0mav	struct sii_raid_conf *meta;
2698dab5b0mav	char *buf;
2708dab5b0mav	int error, i;
2718dab5b0mav	uint16_t checksum, *ptr;
2728dab5b0mav
2738dab5b0mav	pp = cp->provider;
2748dab5b0mav
2758dab5b0mav	/* Read the anchor sector. */
2768dab5b0mav	buf = g_read_data(cp,
2778dab5b0mav	    pp->mediasize - pp->sectorsize, pp->sectorsize, &error);
2788dab5b0mav	if (buf == NULL) {
2798dab5b0mav		G_RAID_DEBUG(1, "Cannot read metadata from %s (error=%d).",
2808dab5b0mav		    pp->name, error);
2818dab5b0mav		return (NULL);
2828dab5b0mav	}
283f68d5demav	meta = (struct sii_raid_conf *)buf;
2848dab5b0mav
2858dab5b0mav	/* Check vendor ID. */
2868dab5b0mav	if (meta->vendor_id != 0x1095) {
2878dab5b0mav		G_RAID_DEBUG(1, "SiI vendor ID check failed on %s (0x%04x)",
2888dab5b0mav		    pp->name, meta->vendor_id);
289f68d5demav		g_free(buf);
2908dab5b0mav		return (NULL);
2918dab5b0mav	}
2928dab5b0mav
2938dab5b0mav	/* Check metadata major version. */
2948dab5b0mav	if (meta->version_major != 2) {
2958dab5b0mav		G_RAID_DEBUG(1, "SiI version check failed on %s (%d.%d)",
2968dab5b0mav		    pp->name, meta->version_major, meta->version_minor);
297f68d5demav		g_free(buf);
2988dab5b0mav		return (NULL);
299