md_jmicron.c revision 220210
1219974Smav/*- 2219974Smav * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org> 3219974Smav * All rights reserved. 4219974Smav * 5219974Smav * Redistribution and use in source and binary forms, with or without 6219974Smav * modification, are permitted provided that the following conditions 7219974Smav * are met: 8219974Smav * 1. Redistributions of source code must retain the above copyright 9219974Smav * notice, this list of conditions and the following disclaimer. 10219974Smav * 2. Redistributions in binary form must reproduce the above copyright 11219974Smav * notice, this list of conditions and the following disclaimer in the 12219974Smav * documentation and/or other materials provided with the distribution. 13219974Smav * 14219974Smav * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15219974Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16219974Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17219974Smav * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18219974Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19219974Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20219974Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21219974Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22219974Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23219974Smav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24219974Smav * SUCH DAMAGE. 25219974Smav */ 26219974Smav 27219974Smav#include <sys/cdefs.h> 28219974Smav__FBSDID("$FreeBSD: head/sys/geom/raid/md_jmicron.c 220210 2011-03-31 16:19:53Z mav $"); 29219974Smav 30219974Smav#include <sys/param.h> 31219974Smav#include <sys/bio.h> 32219974Smav#include <sys/endian.h> 33219974Smav#include <sys/kernel.h> 34219974Smav#include <sys/kobj.h> 35219974Smav#include <sys/limits.h> 36219974Smav#include <sys/lock.h> 37219974Smav#include <sys/malloc.h> 38219974Smav#include <sys/mutex.h> 39219974Smav#include <sys/systm.h> 40219974Smav#include <sys/taskqueue.h> 41219974Smav#include <geom/geom.h> 42219974Smav#include "geom/raid/g_raid.h" 43219974Smav#include "g_raid_md_if.h" 44219974Smav 45219974Smavstatic MALLOC_DEFINE(M_MD_JMICRON, "md_jmicron_data", "GEOM_RAID JMicron metadata"); 46219974Smav 47219974Smav#define JMICRON_MAX_DISKS 8 48219974Smav#define JMICRON_MAX_SPARE 2 49219974Smav 50219974Smavstruct jmicron_raid_conf { 51219974Smav u_int8_t signature[2]; 52219974Smav#define JMICRON_MAGIC "JM" 53219974Smav 54219974Smav u_int16_t version; 55219974Smav#define JMICRON_VERSION 0x0001 56219974Smav 57219974Smav u_int16_t checksum; 58219974Smav u_int8_t filler_1[10]; 59219974Smav u_int32_t disk_id; 60219974Smav u_int32_t offset; 61219974Smav u_int32_t disk_sectors_high; 62219974Smav u_int16_t disk_sectors_low; 63219974Smav u_int8_t filler_2[2]; 64219974Smav u_int8_t name[16]; 65219974Smav u_int8_t type; 66219974Smav#define JMICRON_T_RAID0 0 67219974Smav#define JMICRON_T_RAID1 1 68219974Smav#define JMICRON_T_RAID01 2 69219974Smav#define JMICRON_T_CONCAT 3 70219974Smav#define JMICRON_T_RAID5 5 71219974Smav 72219974Smav u_int8_t stripe_shift; 73219974Smav u_int16_t flags; 74219974Smav#define JMICRON_F_READY 0x0001 75219974Smav#define JMICRON_F_BOOTABLE 0x0002 76219974Smav#define JMICRON_F_BADSEC 0x0004 77219974Smav#define JMICRON_F_ACTIVE 0x0010 78219974Smav#define JMICRON_F_UNSYNC 0x0020 79219974Smav#define JMICRON_F_NEWEST 0x0040 80219974Smav 81219974Smav u_int8_t filler_3[4]; 82219974Smav u_int32_t spare[JMICRON_MAX_SPARE]; 83219974Smav u_int32_t disks[JMICRON_MAX_DISKS]; 84219974Smav#define JMICRON_DISK_MASK 0xFFFFFFF0 85219974Smav#define JMICRON_SEG_MASK 0x0000000F 86219974Smav u_int8_t filler_4[32]; 87219974Smav u_int8_t filler_5[384]; 88219974Smav}; 89219974Smav 90219974Smavstruct g_raid_md_jmicron_perdisk { 91219974Smav struct jmicron_raid_conf *pd_meta; 92219974Smav int pd_disk_pos; 93219974Smav int pd_disk_id; 94219974Smav off_t pd_disk_size; 95219974Smav}; 96219974Smav 97219974Smavstruct g_raid_md_jmicron_object { 98219974Smav struct g_raid_md_object mdio_base; 99219974Smav uint32_t mdio_config_id; 100219974Smav struct jmicron_raid_conf *mdio_meta; 101219974Smav struct callout mdio_start_co; /* STARTING state timer. */ 102219974Smav int mdio_total_disks; 103219974Smav int mdio_disks_present; 104219974Smav int mdio_started; 105219974Smav int mdio_incomplete; 106219974Smav struct root_hold_token *mdio_rootmount; /* Root mount delay token. */ 107219974Smav}; 108219974Smav 109219974Smavstatic g_raid_md_create_t g_raid_md_create_jmicron; 110219974Smavstatic g_raid_md_taste_t g_raid_md_taste_jmicron; 111219974Smavstatic g_raid_md_event_t g_raid_md_event_jmicron; 112219974Smavstatic g_raid_md_ctl_t g_raid_md_ctl_jmicron; 113219974Smavstatic g_raid_md_write_t g_raid_md_write_jmicron; 114219974Smavstatic g_raid_md_fail_disk_t g_raid_md_fail_disk_jmicron; 115219974Smavstatic g_raid_md_free_disk_t g_raid_md_free_disk_jmicron; 116219974Smavstatic g_raid_md_free_t g_raid_md_free_jmicron; 117219974Smav 118219974Smavstatic kobj_method_t g_raid_md_jmicron_methods[] = { 119219974Smav KOBJMETHOD(g_raid_md_create, g_raid_md_create_jmicron), 120219974Smav KOBJMETHOD(g_raid_md_taste, g_raid_md_taste_jmicron), 121219974Smav KOBJMETHOD(g_raid_md_event, g_raid_md_event_jmicron), 122219974Smav KOBJMETHOD(g_raid_md_ctl, g_raid_md_ctl_jmicron), 123219974Smav KOBJMETHOD(g_raid_md_write, g_raid_md_write_jmicron), 124219974Smav KOBJMETHOD(g_raid_md_fail_disk, g_raid_md_fail_disk_jmicron), 125219974Smav KOBJMETHOD(g_raid_md_free_disk, g_raid_md_free_disk_jmicron), 126219974Smav KOBJMETHOD(g_raid_md_free, g_raid_md_free_jmicron), 127219974Smav { 0, 0 } 128219974Smav}; 129219974Smav 130219974Smavstatic struct g_raid_md_class g_raid_md_jmicron_class = { 131219974Smav "JMicron", 132219974Smav g_raid_md_jmicron_methods, 133219974Smav sizeof(struct g_raid_md_jmicron_object), 134219974Smav .mdc_priority = 100 135219974Smav}; 136219974Smav 137219974Smavstatic void 138219974Smavg_raid_md_jmicron_print(struct jmicron_raid_conf *meta) 139219974Smav{ 140219974Smav int k; 141219974Smav 142219974Smav if (g_raid_debug < 1) 143219974Smav return; 144219974Smav 145219974Smav printf("********* ATA JMicron RAID Metadata *********\n"); 146219974Smav printf("signature <%c%c>\n", meta->signature[0], meta->signature[1]); 147219974Smav printf("version %04x\n", meta->version); 148219974Smav printf("checksum 0x%04x\n", meta->checksum); 149219974Smav printf("disk_id 0x%08x\n", meta->disk_id); 150219974Smav printf("offset 0x%08x\n", meta->offset); 151219974Smav printf("disk_sectors_high 0x%08x\n", meta->disk_sectors_high); 152219974Smav printf("disk_sectors_low 0x%04x\n", meta->disk_sectors_low); 153219974Smav printf("name <%.16s>\n", meta->name); 154219974Smav printf("type %d\n", meta->type); 155219974Smav printf("stripe_shift %d\n", meta->stripe_shift); 156219974Smav printf("flags %04x\n", meta->flags); 157219974Smav printf("spare "); 158219974Smav for (k = 0; k < JMICRON_MAX_SPARE; k++) 159219974Smav printf(" 0x%08x", meta->spare[k]); 160219974Smav printf("\n"); 161219974Smav printf("disks "); 162219974Smav for (k = 0; k < JMICRON_MAX_DISKS; k++) 163219974Smav printf(" 0x%08x", meta->disks[k]); 164219974Smav printf("\n"); 165219974Smav printf("=================================================\n"); 166219974Smav} 167219974Smav 168219974Smavstatic struct jmicron_raid_conf * 169219974Smavjmicron_meta_copy(struct jmicron_raid_conf *meta) 170219974Smav{ 171219974Smav struct jmicron_raid_conf *nmeta; 172219974Smav 173219974Smav nmeta = malloc(sizeof(*meta), M_MD_JMICRON, M_WAITOK); 174219974Smav memcpy(nmeta, meta, sizeof(*meta)); 175219974Smav return (nmeta); 176219974Smav} 177219974Smav 178219974Smavstatic int 179219974Smavjmicron_meta_total_disks(struct jmicron_raid_conf *meta) 180219974Smav{ 181219974Smav int pos; 182219974Smav 183219974Smav for (pos = 0; pos < JMICRON_MAX_DISKS; pos++) { 184219974Smav if (meta->disks[pos] == 0) 185219974Smav break; 186219974Smav } 187219974Smav return (pos); 188219974Smav} 189219974Smav 190219974Smavstatic int 191219974Smavjmicron_meta_total_spare(struct jmicron_raid_conf *meta) 192219974Smav{ 193219974Smav int pos, n; 194219974Smav 195219974Smav n = 0; 196219974Smav for (pos = 0; pos < JMICRON_MAX_SPARE; pos++) { 197219974Smav if (meta->spare[pos] != 0) 198219974Smav n++; 199219974Smav } 200219974Smav return (n); 201219974Smav} 202219974Smav 203219974Smav/* 204219974Smav * Generate fake Configuration ID based on disk IDs. 205219974Smav * Note: it will change after each disk set change. 206219974Smav */ 207219974Smavstatic uint32_t 208219974Smavjmicron_meta_config_id(struct jmicron_raid_conf *meta) 209219974Smav{ 210219974Smav int pos; 211219974Smav uint32_t config_id; 212219974Smav 213219974Smav config_id = 0; 214219974Smav for (pos = 0; pos < JMICRON_MAX_DISKS; pos++) 215219974Smav config_id += meta->disks[pos] << pos; 216219974Smav return (config_id); 217219974Smav} 218219974Smav 219219974Smavstatic void 220219974Smavjmicron_meta_get_name(struct jmicron_raid_conf *meta, char *buf) 221219974Smav{ 222219974Smav int i; 223219974Smav 224219974Smav strncpy(buf, meta->name, 16); 225219974Smav buf[16] = 0; 226219974Smav for (i = 15; i >= 0; i--) { 227219974Smav if (buf[i] > 0x20) 228219974Smav break; 229219974Smav buf[i] = 0; 230219974Smav } 231219974Smav} 232219974Smav 233219974Smavstatic void 234219974Smavjmicron_meta_put_name(struct jmicron_raid_conf *meta, char *buf) 235219974Smav{ 236219974Smav 237219974Smav memset(meta->name, 0x20, 16); 238219974Smav memcpy(meta->name, buf, MIN(strlen(buf), 16)); 239219974Smav} 240219974Smav 241219974Smavstatic int 242219974Smavjmicron_meta_find_disk(struct jmicron_raid_conf *meta, uint32_t id) 243219974Smav{ 244219974Smav int pos; 245219974Smav 246219974Smav id &= JMICRON_DISK_MASK; 247219974Smav for (pos = 0; pos < JMICRON_MAX_DISKS; pos++) { 248219974Smav if ((meta->disks[pos] & JMICRON_DISK_MASK) == id) 249219974Smav return (pos); 250219974Smav } 251219974Smav for (pos = 0; pos < JMICRON_MAX_SPARE; pos++) { 252219974Smav if ((meta->spare[pos] & JMICRON_DISK_MASK) == id) 253219974Smav return (-3); 254219974Smav } 255219974Smav return (-1); 256219974Smav} 257219974Smav 258219974Smavstatic struct jmicron_raid_conf * 259219974Smavjmicron_meta_read(struct g_consumer *cp) 260219974Smav{ 261219974Smav struct g_provider *pp; 262219974Smav struct jmicron_raid_conf *meta; 263219974Smav char *buf; 264219974Smav int error, i; 265219974Smav uint16_t checksum, *ptr; 266219974Smav 267219974Smav pp = cp->provider; 268219974Smav 269219974Smav /* Read the anchor sector. */ 270219974Smav buf = g_read_data(cp, 271219974Smav pp->mediasize - pp->sectorsize, pp->sectorsize, &error); 272219974Smav if (buf == NULL) { 273219974Smav G_RAID_DEBUG(1, "Cannot read metadata from %s (error=%d).", 274219974Smav pp->name, error); 275219974Smav return (NULL); 276219974Smav } 277219974Smav meta = (struct jmicron_raid_conf *)buf; 278219974Smav 279219974Smav /* Check if this is an JMicron RAID struct */ 280219974Smav if (strncmp(meta->signature, JMICRON_MAGIC, strlen(JMICRON_MAGIC))) { 281219974Smav G_RAID_DEBUG(1, "JMicron signature check failed on %s", pp->name); 282219974Smav g_free(buf); 283219974Smav return (NULL); 284219974Smav } 285219974Smav meta = malloc(sizeof(*meta), M_MD_JMICRON, M_WAITOK); 286219974Smav memcpy(meta, buf, min(sizeof(*meta), pp->sectorsize)); 287219974Smav g_free(buf); 288219974Smav 289219974Smav /* Check metadata checksum. */ 290219974Smav for (checksum = 0, ptr = (uint16_t *)meta, i = 0; i < 64; i++) 291219974Smav checksum += *ptr++; 292219974Smav if (checksum != 0) { 293219974Smav G_RAID_DEBUG(1, "JMicron checksum check failed on %s", pp->name); 294219974Smav free(meta, M_MD_JMICRON); 295219974Smav return (NULL); 296219974Smav } 297219974Smav 298219974Smav return (meta); 299219974Smav} 300219974Smav 301219974Smavstatic int 302219974Smavjmicron_meta_write(struct g_consumer *cp, struct jmicron_raid_conf *meta) 303219974Smav{ 304219974Smav struct g_provider *pp; 305219974Smav char *buf; 306219974Smav int error, i; 307219974Smav uint16_t checksum, *ptr; 308219974Smav 309219974Smav pp = cp->provider; 310219974Smav 311219974Smav /* Recalculate checksum for case if metadata were changed. */ 312219974Smav meta->checksum = 0; 313219974Smav for (checksum = 0, ptr = (uint16_t *)meta, i = 0; i < 64; i++) 314219974Smav checksum += *ptr++; 315219974Smav meta->checksum -= checksum; 316219974Smav 317219974Smav /* Create and fill buffer. */ 318219974Smav buf = malloc(pp->sectorsize, M_MD_JMICRON, M_WAITOK | M_ZERO); 319219974Smav memcpy(buf, meta, sizeof(*meta)); 320219974Smav 321219974Smav error = g_write_data(cp, 322219974Smav pp->mediasize - pp->sectorsize, buf, pp->sectorsize); 323219974Smav if (error != 0) { 324219974Smav G_RAID_DEBUG(1, "Cannot write metadata to %s (error=%d).", 325219974Smav pp->name, error); 326219974Smav } 327219974Smav 328219974Smav free(buf, M_MD_JMICRON); 329219974Smav return (error); 330219974Smav} 331219974Smav 332219974Smavstatic int 333219974Smavjmicron_meta_erase(struct g_consumer *cp) 334219974Smav{ 335219974Smav struct g_provider *pp; 336219974Smav char *buf; 337219974Smav int error; 338219974Smav 339219974Smav pp = cp->provider; 340219974Smav buf = malloc(pp->sectorsize, M_MD_JMICRON, M_WAITOK | M_ZERO); 341219974Smav error = g_write_data(cp, 342219974Smav pp->mediasize - pp->sectorsize, buf, pp->sectorsize); 343219974Smav if (error != 0) { 344219974Smav G_RAID_DEBUG(1, "Cannot erase metadata on %s (error=%d).", 345219974Smav pp->name, error); 346219974Smav } 347219974Smav free(buf, M_MD_JMICRON); 348219974Smav return (error); 349219974Smav} 350219974Smav 351219974Smavstatic struct g_raid_disk * 352219974Smavg_raid_md_jmicron_get_disk(struct g_raid_softc *sc, int id) 353219974Smav{ 354219974Smav struct g_raid_disk *disk; 355219974Smav struct g_raid_md_jmicron_perdisk *pd; 356219974Smav 357219974Smav TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 358219974Smav pd = (struct g_raid_md_jmicron_perdisk *)disk->d_md_data; 359219974Smav if (pd->pd_disk_pos == id) 360219974Smav break; 361219974Smav } 362219974Smav return (disk); 363219974Smav} 364219974Smav 365219974Smavstatic int 366219974Smavg_raid_md_jmicron_supported(int level, int qual, int disks, int force) 367219974Smav{ 368219974Smav 369219974Smav if (disks > 8) 370219974Smav return (0); 371219974Smav switch (level) { 372219974Smav case G_RAID_VOLUME_RL_RAID0: 373219974Smav if (disks < 1) 374219974Smav return (0); 375219974Smav if (!force && (disks < 2 || disks > 6)) 376219974Smav return (0); 377219974Smav break; 378219974Smav case G_RAID_VOLUME_RL_RAID1: 379219974Smav if (disks < 1) 380219974Smav return (0); 381219974Smav if (!force && (disks != 2)) 382219974Smav return (0); 383219974Smav break; 384219974Smav case G_RAID_VOLUME_RL_RAID1E: 385219974Smav if (disks < 2) 386219974Smav return (0); 387219974Smav if (!force && (disks != 4)) 388219974Smav return (0); 389219974Smav break; 390219974Smav case G_RAID_VOLUME_RL_SINGLE: 391219974Smav if (disks != 1) 392219974Smav return (0); 393219974Smav if (!force) 394219974Smav return (0); 395219974Smav break; 396219974Smav case G_RAID_VOLUME_RL_CONCAT: 397219974Smav if (disks < 2) 398219974Smav return (0); 399219974Smav break; 400219974Smav case G_RAID_VOLUME_RL_RAID5: 401219974Smav if (disks < 3) 402219974Smav return (0); 403219974Smav if (!force) 404219974Smav return (0); 405219974Smav break; 406219974Smav default: 407219974Smav return (0); 408219974Smav } 409219974Smav if (qual != G_RAID_VOLUME_RLQ_NONE) 410219974Smav return (0); 411219974Smav return (1); 412219974Smav} 413219974Smav 414219974Smavstatic int 415219974Smavg_raid_md_jmicron_start_disk(struct g_raid_disk *disk) 416219974Smav{ 417219974Smav struct g_raid_softc *sc; 418219974Smav struct g_raid_subdisk *sd, *tmpsd; 419219974Smav struct g_raid_disk *olddisk, *tmpdisk; 420219974Smav struct g_raid_md_object *md; 421219974Smav struct g_raid_md_jmicron_object *mdi; 422219974Smav struct g_raid_md_jmicron_perdisk *pd, *oldpd; 423219974Smav struct jmicron_raid_conf *meta; 424219974Smav int disk_pos, resurrection = 0; 425219974Smav 426219974Smav sc = disk->d_softc; 427219974Smav md = sc->sc_md; 428219974Smav mdi = (struct g_raid_md_jmicron_object *)md; 429219974Smav meta = mdi->mdio_meta; 430219974Smav pd = (struct g_raid_md_jmicron_perdisk *)disk->d_md_data; 431219974Smav olddisk = NULL; 432219974Smav 433219974Smav /* Find disk position in metadata by it's serial. */ 434220209Smav if (pd->pd_meta != NULL) 435220209Smav disk_pos = jmicron_meta_find_disk(meta, pd->pd_disk_id); 436220209Smav else 437220209Smav disk_pos = -1; 438219974Smav if (disk_pos < 0) { 439219974Smav G_RAID_DEBUG1(1, sc, "Unknown, probably new or stale disk"); 440219974Smav /* If we are in the start process, that's all for now. */ 441219974Smav if (!mdi->mdio_started) 442219974Smav goto nofit; 443219974Smav /* 444219974Smav * If we have already started - try to get use of the disk. 445219974Smav * Try to replace OFFLINE disks first, then FAILED. 446219974Smav */ 447219974Smav TAILQ_FOREACH(tmpdisk, &sc->sc_disks, d_next) { 448219974Smav if (tmpdisk->d_state != G_RAID_DISK_S_OFFLINE && 449219974Smav tmpdisk->d_state != G_RAID_DISK_S_FAILED) 450219974Smav continue; 451219974Smav /* Make sure this disk is big enough. */ 452219974Smav TAILQ_FOREACH(sd, &tmpdisk->d_subdisks, sd_next) { 453219974Smav if (sd->sd_offset + sd->sd_size + 512 > 454219974Smav pd->pd_disk_size) { 455219974Smav G_RAID_DEBUG1(1, sc, 456219974Smav "Disk too small (%ju < %ju)", 457219974Smav pd->pd_disk_size, 458219974Smav sd->sd_offset + sd->sd_size + 512); 459219974Smav break; 460219974Smav } 461219974Smav } 462219974Smav if (sd != NULL) 463219974Smav continue; 464219974Smav if (tmpdisk->d_state == G_RAID_DISK_S_OFFLINE) { 465219974Smav olddisk = tmpdisk; 466219974Smav break; 467219974Smav } else if (olddisk == NULL) 468219974Smav olddisk = tmpdisk; 469219974Smav } 470219974Smav if (olddisk == NULL) { 471219974Smavnofit: 472219974Smav if (disk_pos == -3 || pd->pd_disk_pos == -3) { 473219974Smav g_raid_change_disk_state(disk, 474219974Smav G_RAID_DISK_S_SPARE); 475219974Smav return (1); 476219974Smav } else { 477219974Smav g_raid_change_disk_state(disk, 478219974Smav G_RAID_DISK_S_STALE); 479219974Smav return (0); 480219974Smav } 481219974Smav } 482219974Smav oldpd = (struct g_raid_md_jmicron_perdisk *)olddisk->d_md_data; 483219974Smav disk_pos = oldpd->pd_disk_pos; 484219974Smav resurrection = 1; 485219974Smav } 486219974Smav 487219974Smav if (olddisk == NULL) { 488219974Smav /* Find placeholder by position. */ 489219974Smav olddisk = g_raid_md_jmicron_get_disk(sc, disk_pos); 490219974Smav if (olddisk == NULL) 491219974Smav panic("No disk at position %d!", disk_pos); 492219974Smav if (olddisk->d_state != G_RAID_DISK_S_OFFLINE) { 493219974Smav G_RAID_DEBUG1(1, sc, "More then one disk for pos %d", 494219974Smav disk_pos); 495219974Smav g_raid_change_disk_state(disk, G_RAID_DISK_S_STALE); 496219974Smav return (0); 497219974Smav } 498219974Smav oldpd = (struct g_raid_md_jmicron_perdisk *)olddisk->d_md_data; 499219974Smav } 500219974Smav 501219974Smav /* Replace failed disk or placeholder with new disk. */ 502219974Smav TAILQ_FOREACH_SAFE(sd, &olddisk->d_subdisks, sd_next, tmpsd) { 503219974Smav TAILQ_REMOVE(&olddisk->d_subdisks, sd, sd_next); 504219974Smav TAILQ_INSERT_TAIL(&disk->d_subdisks, sd, sd_next); 505219974Smav sd->sd_disk = disk; 506219974Smav } 507219974Smav oldpd->pd_disk_pos = -2; 508219974Smav pd->pd_disk_pos = disk_pos; 509219974Smav /* Update global metadata just in case. */ 510219974Smav meta->disks[disk_pos] = pd->pd_disk_id; 511219974Smav 512219974Smav /* If it was placeholder -- destroy it. */ 513219974Smav if (olddisk->d_state == G_RAID_DISK_S_OFFLINE) { 514219974Smav g_raid_destroy_disk(olddisk); 515219974Smav } else { 516219974Smav /* Otherwise, make it STALE_FAILED. */ 517219974Smav g_raid_change_disk_state(olddisk, G_RAID_DISK_S_STALE_FAILED); 518219974Smav } 519219974Smav 520219974Smav /* Welcome the new disk. */ 521219974Smav g_raid_change_disk_state(disk, G_RAID_DISK_S_ACTIVE); 522219974Smav TAILQ_FOREACH(sd, &disk->d_subdisks, sd_next) { 523219974Smav 524219974Smav /* 525219974Smav * Different disks may have different sizes/offsets, 526219974Smav * especially in concat mode. Update. 527219974Smav */ 528220209Smav if (!resurrection) { 529219974Smav sd->sd_offset = 530219974Smav (off_t)pd->pd_meta->offset * 16 * 512; //ZZZ 531219974Smav sd->sd_size = 532219974Smav (((off_t)pd->pd_meta->disk_sectors_high << 16) + 533219974Smav pd->pd_meta->disk_sectors_low) * 512; 534219974Smav } 535219974Smav 536219974Smav if (resurrection) { 537219974Smav /* Stale disk, almost same as new. */ 538219974Smav g_raid_change_subdisk_state(sd, 539219974Smav G_RAID_SUBDISK_S_NEW); 540219974Smav } else if ((meta->flags & JMICRON_F_BADSEC) != 0 && 541219974Smav (pd->pd_meta->flags & JMICRON_F_BADSEC) == 0) { 542219974Smav /* Cold-inserted or rebuilding disk. */ 543219974Smav g_raid_change_subdisk_state(sd, 544219974Smav G_RAID_SUBDISK_S_NEW); 545219974Smav } else if (pd->pd_meta->flags & JMICRON_F_UNSYNC) { 546219974Smav /* Dirty or resyncing disk.. */ 547219974Smav g_raid_change_subdisk_state(sd, 548219974Smav G_RAID_SUBDISK_S_STALE); 549219974Smav } else { 550219974Smav /* Up to date disk. */ 551219974Smav g_raid_change_subdisk_state(sd, 552219974Smav G_RAID_SUBDISK_S_ACTIVE); 553219974Smav } 554219974Smav g_raid_event_send(sd, G_RAID_SUBDISK_E_NEW, 555219974Smav G_RAID_EVENT_SUBDISK); 556219974Smav } 557219974Smav 558219974Smav /* Update status of our need for spare. */ 559219974Smav if (mdi->mdio_started) { 560219974Smav mdi->mdio_incomplete = 561219974Smav (g_raid_ndisks(sc, G_RAID_DISK_S_ACTIVE) < 562219974Smav mdi->mdio_total_disks); 563219974Smav } 564219974Smav 565219974Smav return (resurrection); 566219974Smav} 567219974Smav 568219974Smavstatic void 569219974Smavg_disk_md_jmicron_retaste(void *arg, int pending) 570219974Smav{ 571219974Smav 572219974Smav G_RAID_DEBUG(1, "Array is not complete, trying to retaste."); 573219974Smav g_retaste(&g_raid_class); 574219974Smav free(arg, M_MD_JMICRON); 575219974Smav} 576219974Smav 577219974Smavstatic void 578219974Smavg_raid_md_jmicron_refill(struct g_raid_softc *sc) 579219974Smav{ 580219974Smav struct g_raid_md_object *md; 581219974Smav struct g_raid_md_jmicron_object *mdi; 582219974Smav struct g_raid_disk *disk; 583219974Smav struct task *task; 584219974Smav int update, na; 585219974Smav 586219974Smav md = sc->sc_md; 587219974Smav mdi = (struct g_raid_md_jmicron_object *)md; 588219974Smav update = 0; 589219974Smav do { 590219974Smav /* Make sure we miss anything. */ 591219974Smav na = g_raid_ndisks(sc, G_RAID_DISK_S_ACTIVE); 592219974Smav if (na == mdi->mdio_total_disks) 593219974Smav break; 594219974Smav 595219974Smav G_RAID_DEBUG1(1, md->mdo_softc, 596219974Smav "Array is not complete (%d of %d), " 597219974Smav "trying to refill.", na, mdi->mdio_total_disks); 598219974Smav 599219974Smav /* Try to get use some of STALE disks. */ 600219974Smav TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 601219974Smav if (disk->d_state == G_RAID_DISK_S_STALE) { 602219974Smav update += g_raid_md_jmicron_start_disk(disk); 603219974Smav if (disk->d_state == G_RAID_DISK_S_ACTIVE) 604219974Smav break; 605219974Smav } 606219974Smav } 607219974Smav if (disk != NULL) 608219974Smav continue; 609219974Smav 610219974Smav /* Try to get use some of SPARE disks. */ 611219974Smav TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 612219974Smav if (disk->d_state == G_RAID_DISK_S_SPARE) { 613219974Smav update += g_raid_md_jmicron_start_disk(disk); 614219974Smav if (disk->d_state == G_RAID_DISK_S_ACTIVE) 615219974Smav break; 616219974Smav } 617219974Smav } 618219974Smav } while (disk != NULL); 619219974Smav 620219974Smav /* Write new metadata if we changed something. */ 621220210Smav if (update) 622219974Smav g_raid_md_write_jmicron(md, NULL, NULL, NULL); 623219974Smav 624219974Smav /* Update status of our need for spare. */ 625219974Smav mdi->mdio_incomplete = (g_raid_ndisks(sc, G_RAID_DISK_S_ACTIVE) < 626219974Smav mdi->mdio_total_disks); 627219974Smav 628219974Smav /* Request retaste hoping to find spare. */ 629219974Smav if (mdi->mdio_incomplete) { 630219974Smav task = malloc(sizeof(struct task), 631219974Smav M_MD_JMICRON, M_WAITOK | M_ZERO); 632219974Smav TASK_INIT(task, 0, g_disk_md_jmicron_retaste, task); 633219974Smav taskqueue_enqueue(taskqueue_swi, task); 634219974Smav } 635219974Smav} 636219974Smav 637219974Smavstatic void 638219974Smavg_raid_md_jmicron_start(struct g_raid_softc *sc) 639219974Smav{ 640219974Smav struct g_raid_md_object *md; 641219974Smav struct g_raid_md_jmicron_object *mdi; 642219974Smav struct g_raid_md_jmicron_perdisk *pd; 643219974Smav struct jmicron_raid_conf *meta; 644219974Smav struct g_raid_volume *vol; 645219974Smav struct g_raid_subdisk *sd; 646219974Smav struct g_raid_disk *disk; 647219974Smav off_t size; 648219974Smav int j, disk_pos; 649219974Smav char buf[17]; 650219974Smav 651219974Smav md = sc->sc_md; 652219974Smav mdi = (struct g_raid_md_jmicron_object *)md; 653219974Smav meta = mdi->mdio_meta; 654219974Smav 655219974Smav /* Create volumes and subdisks. */ 656219974Smav jmicron_meta_get_name(meta, buf); 657219974Smav vol = g_raid_create_volume(sc, buf, -1); 658219974Smav size = ((off_t)meta->disk_sectors_high << 16) + meta->disk_sectors_low; 659219974Smav size *= 512; //ZZZ 660219974Smav if (meta->type == JMICRON_T_RAID0) { 661219974Smav vol->v_raid_level = G_RAID_VOLUME_RL_RAID0; 662219974Smav vol->v_mediasize = size * mdi->mdio_total_disks; 663219974Smav } else if (meta->type == JMICRON_T_RAID1) { 664219974Smav vol->v_raid_level = G_RAID_VOLUME_RL_RAID1; 665219974Smav vol->v_mediasize = size; 666219974Smav } else if (meta->type == JMICRON_T_RAID01) { 667219974Smav vol->v_raid_level = G_RAID_VOLUME_RL_RAID1E; 668219974Smav vol->v_mediasize = size * mdi->mdio_total_disks / 2; 669219974Smav } else if (meta->type == JMICRON_T_CONCAT) { 670219974Smav if (mdi->mdio_total_disks == 1) 671219974Smav vol->v_raid_level = G_RAID_VOLUME_RL_SINGLE; 672219974Smav else 673219974Smav vol->v_raid_level = G_RAID_VOLUME_RL_CONCAT; 674219974Smav vol->v_mediasize = 0; 675219974Smav } else if (meta->type == JMICRON_T_RAID5) { 676219974Smav vol->v_raid_level = G_RAID_VOLUME_RL_RAID5; 677219974Smav vol->v_mediasize = size * (mdi->mdio_total_disks - 1); 678219974Smav } else { 679219974Smav vol->v_raid_level = G_RAID_VOLUME_RL_UNKNOWN; 680219974Smav vol->v_mediasize = 0; 681219974Smav } 682219974Smav vol->v_raid_level_qualifier = G_RAID_VOLUME_RLQ_NONE; 683219974Smav vol->v_strip_size = 1024 << meta->stripe_shift; //ZZZ 684219974Smav vol->v_disks_count = mdi->mdio_total_disks; 685219974Smav vol->v_sectorsize = 512; //ZZZ 686219974Smav for (j = 0; j < vol->v_disks_count; j++) { 687219974Smav sd = &vol->v_subdisks[j]; 688219974Smav sd->sd_offset = (off_t)meta->offset * 16 * 512; //ZZZ 689219974Smav sd->sd_size = size; 690219974Smav } 691219974Smav g_raid_start_volume(vol); 692219974Smav 693219974Smav /* Create disk placeholders to store data for later writing. */ 694219974Smav for (disk_pos = 0; disk_pos < mdi->mdio_total_disks; disk_pos++) { 695219974Smav pd = malloc(sizeof(*pd), M_MD_JMICRON, M_WAITOK | M_ZERO); 696219974Smav pd->pd_disk_pos = disk_pos; 697219974Smav pd->pd_disk_id = meta->disks[disk_pos]; 698219974Smav disk = g_raid_create_disk(sc); 699219974Smav disk->d_md_data = (void *)pd; 700219974Smav disk->d_state = G_RAID_DISK_S_OFFLINE; 701219974Smav sd = &vol->v_subdisks[disk_pos]; 702219974Smav sd->sd_disk = disk; 703219974Smav TAILQ_INSERT_TAIL(&disk->d_subdisks, sd, sd_next); 704219974Smav } 705219974Smav 706219974Smav /* Make all disks found till the moment take their places. */ 707219974Smav do { 708219974Smav TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 709219974Smav if (disk->d_state == G_RAID_DISK_S_NONE) { 710219974Smav g_raid_md_jmicron_start_disk(disk); 711219974Smav break; 712219974Smav } 713219974Smav } 714219974Smav } while (disk != NULL); 715219974Smav 716219974Smav mdi->mdio_started = 1; 717219974Smav G_RAID_DEBUG1(0, sc, "Array started."); 718219974Smav g_raid_md_write_jmicron(md, NULL, NULL, NULL); 719219974Smav 720219974Smav /* Pickup any STALE/SPARE disks to refill array if needed. */ 721219974Smav g_raid_md_jmicron_refill(sc); 722219974Smav 723219974Smav g_raid_event_send(vol, G_RAID_VOLUME_E_START, G_RAID_EVENT_VOLUME); 724219974Smav 725219974Smav callout_stop(&mdi->mdio_start_co); 726219974Smav G_RAID_DEBUG1(1, sc, "root_mount_rel %p", mdi->mdio_rootmount); 727219974Smav root_mount_rel(mdi->mdio_rootmount); 728219974Smav mdi->mdio_rootmount = NULL; 729219974Smav} 730219974Smav 731219974Smavstatic void 732219974Smavg_raid_md_jmicron_new_disk(struct g_raid_disk *disk) 733219974Smav{ 734219974Smav struct g_raid_softc *sc; 735219974Smav struct g_raid_md_object *md; 736219974Smav struct g_raid_md_jmicron_object *mdi; 737219974Smav struct jmicron_raid_conf *pdmeta; 738219974Smav struct g_raid_md_jmicron_perdisk *pd; 739219974Smav 740219974Smav sc = disk->d_softc; 741219974Smav md = sc->sc_md; 742219974Smav mdi = (struct g_raid_md_jmicron_object *)md; 743219974Smav pd = (struct g_raid_md_jmicron_perdisk *)disk->d_md_data; 744219974Smav pdmeta = pd->pd_meta; 745219974Smav 746219974Smav if (mdi->mdio_started) { 747219974Smav if (g_raid_md_jmicron_start_disk(disk)) 748219974Smav g_raid_md_write_jmicron(md, NULL, NULL, NULL); 749219974Smav } else { 750219974Smav /* 751219974Smav * If we haven't started yet - update common metadata 752219974Smav * to get subdisks details, avoiding data from spare disks. 753219974Smav */ 754219974Smav if (mdi->mdio_meta == NULL || 755219974Smav jmicron_meta_find_disk(mdi->mdio_meta, 756219974Smav mdi->mdio_meta->disk_id) == -3) { 757219974Smav if (mdi->mdio_meta != NULL) 758219974Smav free(mdi->mdio_meta, M_MD_JMICRON); 759219974Smav mdi->mdio_meta = jmicron_meta_copy(pdmeta); 760219974Smav mdi->mdio_total_disks = jmicron_meta_total_disks(pdmeta); 761219974Smav } 762219974Smav mdi->mdio_meta->flags |= pdmeta->flags & JMICRON_F_BADSEC; 763219974Smav 764219974Smav mdi->mdio_disks_present++; 765219974Smav G_RAID_DEBUG1(1, sc, "Matching disk (%d of %d+%d up)", 766219974Smav mdi->mdio_disks_present, 767219974Smav mdi->mdio_total_disks, 768219974Smav jmicron_meta_total_spare(mdi->mdio_meta)); 769219974Smav 770219974Smav /* If we collected all needed disks - start array. */ 771219974Smav if (mdi->mdio_disks_present == mdi->mdio_total_disks + 772219974Smav jmicron_meta_total_spare(mdi->mdio_meta)) 773219974Smav g_raid_md_jmicron_start(sc); 774219974Smav } 775219974Smav} 776219974Smav 777219974Smavstatic void 778219974Smavg_raid_jmicron_go(void *arg) 779219974Smav{ 780219974Smav struct g_raid_softc *sc; 781219974Smav struct g_raid_md_object *md; 782219974Smav struct g_raid_md_jmicron_object *mdi; 783219974Smav 784219974Smav sc = arg; 785219974Smav md = sc->sc_md; 786219974Smav mdi = (struct g_raid_md_jmicron_object *)md; 787219974Smav if (!mdi->mdio_started) { 788219974Smav G_RAID_DEBUG1(0, sc, "Force array start due to timeout."); 789219974Smav g_raid_event_send(sc, G_RAID_NODE_E_START, 0); 790219974Smav } 791219974Smav} 792219974Smav 793219974Smavstatic int 794219974Smavg_raid_md_create_jmicron(struct g_raid_md_object *md, struct g_class *mp, 795219974Smav struct g_geom **gp) 796219974Smav{ 797219974Smav struct g_raid_softc *sc; 798219974Smav struct g_raid_md_jmicron_object *mdi; 799219974Smav char name[16]; 800219974Smav 801219974Smav mdi = (struct g_raid_md_jmicron_object *)md; 802219974Smav mdi->mdio_config_id = arc4random(); 803219974Smav snprintf(name, sizeof(name), "JMicron-%08x", mdi->mdio_config_id); 804219974Smav sc = g_raid_create_node(mp, name, md); 805219974Smav if (sc == NULL) 806219974Smav return (G_RAID_MD_TASTE_FAIL); 807219974Smav md->mdo_softc = sc; 808219974Smav *gp = sc->sc_geom; 809219974Smav return (G_RAID_MD_TASTE_NEW); 810219974Smav} 811219974Smav 812219974Smavstatic int 813219974Smavg_raid_md_taste_jmicron(struct g_raid_md_object *md, struct g_class *mp, 814219974Smav struct g_consumer *cp, struct g_geom **gp) 815219974Smav{ 816219974Smav struct g_consumer *rcp; 817219974Smav struct g_provider *pp; 818219974Smav struct g_raid_md_jmicron_object *mdi, *mdi1; 819219974Smav struct g_raid_softc *sc; 820219974Smav struct g_raid_disk *disk; 821219974Smav struct jmicron_raid_conf *meta; 822219974Smav struct g_raid_md_jmicron_perdisk *pd; 823219974Smav struct g_geom *geom; 824219974Smav int error, disk_pos, result, spare, len; 825219974Smav char name[16]; 826219974Smav uint16_t vendor; 827219974Smav 828219974Smav G_RAID_DEBUG(1, "Tasting JMicron on %s", cp->provider->name); 829219974Smav mdi = (struct g_raid_md_jmicron_object *)md; 830219974Smav pp = cp->provider; 831219974Smav 832219974Smav /* Read metadata from device. */ 833219974Smav meta = NULL; 834219974Smav vendor = 0xffff; 835219974Smav if (g_access(cp, 1, 0, 0) != 0) 836219974Smav return (G_RAID_MD_TASTE_FAIL); 837219974Smav g_topology_unlock(); 838219974Smav len = 2; 839219974Smav if (pp->geom->rank == 1) 840219974Smav g_io_getattr("GEOM::hba_vendor", cp, &len, &vendor); 841219974Smav meta = jmicron_meta_read(cp); 842219974Smav g_topology_lock(); 843219974Smav g_access(cp, -1, 0, 0); 844219974Smav if (meta == NULL) { 845219974Smav if (g_raid_aggressive_spare) { 846219974Smav if (vendor == 0x197b) { 847219974Smav G_RAID_DEBUG(1, 848219974Smav "No JMicron metadata, forcing spare."); 849219974Smav spare = 2; 850219974Smav goto search; 851219974Smav } else { 852219974Smav G_RAID_DEBUG(1, 853219974Smav "JMicron vendor mismatch 0x%04x != 0x197b", 854219974Smav vendor); 855219974Smav } 856219974Smav } 857219974Smav return (G_RAID_MD_TASTE_FAIL); 858219974Smav } 859219974Smav 860219974Smav /* Check this disk position in obtained metadata. */ 861219974Smav disk_pos = jmicron_meta_find_disk(meta, meta->disk_id); 862219974Smav if (disk_pos == -1) { 863219974Smav G_RAID_DEBUG(1, "JMicron disk_id %08x not found", 864219974Smav meta->disk_id); 865219974Smav goto fail1; 866219974Smav } 867219974Smav 868219974Smav /* Metadata valid. Print it. */ 869219974Smav g_raid_md_jmicron_print(meta); 870219974Smav G_RAID_DEBUG(1, "JMicron disk position %d", disk_pos); 871219974Smav spare = (disk_pos == -2) ? 1 : 0; 872219974Smav 873219974Smavsearch: 874219974Smav /* Search for matching node. */ 875219974Smav sc = NULL; 876219974Smav mdi1 = NULL; 877219974Smav LIST_FOREACH(geom, &mp->geom, geom) { 878219974Smav sc = geom->softc; 879219974Smav if (sc == NULL) 880219974Smav continue; 881219974Smav if (sc->sc_stopping != 0) 882219974Smav continue; 883219974Smav if (sc->sc_md->mdo_class != md->mdo_class) 884219974Smav continue; 885219974Smav mdi1 = (struct g_raid_md_jmicron_object *)sc->sc_md; 886219974Smav if (spare == 2) { 887219974Smav if (mdi1->mdio_incomplete) 888219974Smav break; 889219974Smav } else { 890219974Smav if (mdi1->mdio_config_id == 891219974Smav jmicron_meta_config_id(meta)) 892219974Smav break; 893219974Smav } 894219974Smav } 895219974Smav 896219974Smav /* Found matching node. */ 897219974Smav if (geom != NULL) { 898219974Smav G_RAID_DEBUG(1, "Found matching array %s", sc->sc_name); 899219974Smav result = G_RAID_MD_TASTE_EXISTING; 900219974Smav 901219974Smav } else if (spare) { /* Not found needy node -- left for later. */ 902219974Smav G_RAID_DEBUG(1, "Spare is not needed at this time"); 903219974Smav goto fail1; 904219974Smav 905219974Smav } else { /* Not found matching node -- create one. */ 906219974Smav result = G_RAID_MD_TASTE_NEW; 907219974Smav mdi->mdio_config_id = jmicron_meta_config_id(meta); 908219974Smav snprintf(name, sizeof(name), "JMicron-%08x", 909219974Smav mdi->mdio_config_id); 910219974Smav sc = g_raid_create_node(mp, name, md); 911219974Smav md->mdo_softc = sc; 912219974Smav geom = sc->sc_geom; 913219974Smav callout_init(&mdi->mdio_start_co, 1); 914219974Smav callout_reset(&mdi->mdio_start_co, g_raid_start_timeout * hz, 915219974Smav g_raid_jmicron_go, sc); 916219974Smav mdi->mdio_rootmount = root_mount_hold("GRAID-JMicron"); 917219974Smav G_RAID_DEBUG1(1, sc, "root_mount_hold %p", mdi->mdio_rootmount); 918219974Smav } 919219974Smav 920219974Smav rcp = g_new_consumer(geom); 921219974Smav g_attach(rcp, pp); 922219974Smav if (g_access(rcp, 1, 1, 1) != 0) 923219974Smav ; //goto fail1; 924219974Smav 925219974Smav g_topology_unlock(); 926219974Smav sx_xlock(&sc->sc_lock); 927219974Smav 928219974Smav pd = malloc(sizeof(*pd), M_MD_JMICRON, M_WAITOK | M_ZERO); 929219974Smav pd->pd_meta = meta; 930219974Smav if (spare == 2) { 931219974Smav pd->pd_disk_pos = -3; 932219974Smav pd->pd_disk_id = arc4random() & JMICRON_DISK_MASK; 933219974Smav } else { 934219974Smav pd->pd_disk_pos = -1; 935219974Smav pd->pd_disk_id = meta->disk_id; 936219974Smav } 937219974Smav pd->pd_disk_size = pp->mediasize; 938219974Smav disk = g_raid_create_disk(sc); 939219974Smav disk->d_md_data = (void *)pd; 940219974Smav disk->d_consumer = rcp; 941219974Smav rcp->private = disk; 942219974Smav 943219974Smav /* Read kernel dumping information. */ 944219974Smav disk->d_kd.offset = 0; 945219974Smav disk->d_kd.length = OFF_MAX; 946219974Smav len = sizeof(disk->d_kd); 947219974Smav error = g_io_getattr("GEOM::kerneldump", rcp, &len, &disk->d_kd); 948219974Smav if (disk->d_kd.di.dumper == NULL) 949219974Smav G_RAID_DEBUG1(2, sc, "Dumping not supported by %s: %d.", 950219974Smav rcp->provider->name, error); 951219974Smav 952219974Smav g_raid_md_jmicron_new_disk(disk); 953219974Smav 954219974Smav sx_xunlock(&sc->sc_lock); 955219974Smav g_topology_lock(); 956219974Smav *gp = geom; 957219974Smav return (result); 958219974Smavfail1: 959219974Smav free(meta, M_MD_JMICRON); 960219974Smav return (G_RAID_MD_TASTE_FAIL); 961219974Smav} 962219974Smav 963219974Smavstatic int 964219974Smavg_raid_md_event_jmicron(struct g_raid_md_object *md, 965219974Smav struct g_raid_disk *disk, u_int event) 966219974Smav{ 967219974Smav struct g_raid_softc *sc; 968219974Smav struct g_raid_subdisk *sd; 969219974Smav struct g_raid_md_jmicron_object *mdi; 970219974Smav struct g_raid_md_jmicron_perdisk *pd; 971219974Smav 972219974Smav sc = md->mdo_softc; 973219974Smav mdi = (struct g_raid_md_jmicron_object *)md; 974219974Smav if (disk == NULL) { 975219974Smav switch (event) { 976219974Smav case G_RAID_NODE_E_START: 977219974Smav if (!mdi->mdio_started) 978219974Smav g_raid_md_jmicron_start(sc); 979219974Smav return (0); 980219974Smav } 981219974Smav return (-1); 982219974Smav } 983219974Smav pd = (struct g_raid_md_jmicron_perdisk *)disk->d_md_data; 984219974Smav switch (event) { 985219974Smav case G_RAID_DISK_E_DISCONNECTED: 986219974Smav /* If disk was assigned, just update statuses. */ 987219974Smav if (pd->pd_disk_pos >= 0) { 988219974Smav g_raid_change_disk_state(disk, G_RAID_DISK_S_OFFLINE); 989219974Smav if (disk->d_consumer) { 990219974Smav g_raid_kill_consumer(sc, disk->d_consumer); 991219974Smav disk->d_consumer = NULL; 992219974Smav } 993219974Smav TAILQ_FOREACH(sd, &disk->d_subdisks, sd_next) { 994219974Smav g_raid_change_subdisk_state(sd, 995219974Smav G_RAID_SUBDISK_S_NONE); 996219974Smav g_raid_event_send(sd, G_RAID_SUBDISK_E_DISCONNECTED, 997219974Smav G_RAID_EVENT_SUBDISK); 998219974Smav } 999219974Smav } else { 1000219974Smav /* Otherwise -- delete. */ 1001219974Smav g_raid_change_disk_state(disk, G_RAID_DISK_S_NONE); 1002219974Smav g_raid_destroy_disk(disk); 1003219974Smav } 1004219974Smav 1005219974Smav /* Write updated metadata to all disks. */ 1006219974Smav g_raid_md_write_jmicron(md, NULL, NULL, NULL); 1007219974Smav 1008219974Smav /* Check if anything left except placeholders. */ 1009219974Smav if (g_raid_ndisks(sc, -1) == 1010219974Smav g_raid_ndisks(sc, G_RAID_DISK_S_OFFLINE)) 1011219974Smav g_raid_destroy_node(sc, 0); 1012219974Smav else 1013219974Smav g_raid_md_jmicron_refill(sc); 1014219974Smav return (0); 1015219974Smav } 1016219974Smav return (-2); 1017219974Smav} 1018219974Smav 1019219974Smavstatic int 1020219974Smavg_raid_md_ctl_jmicron(struct g_raid_md_object *md, 1021219974Smav struct gctl_req *req) 1022219974Smav{ 1023219974Smav struct g_raid_softc *sc; 1024219974Smav struct g_raid_volume *vol; 1025219974Smav struct g_raid_subdisk *sd; 1026219974Smav struct g_raid_disk *disk; 1027219974Smav struct g_raid_md_jmicron_object *mdi; 1028219974Smav struct g_raid_md_jmicron_perdisk *pd; 1029219974Smav struct g_consumer *cp; 1030219974Smav struct g_provider *pp; 1031219974Smav char arg[16]; 1032219974Smav const char *verb, *volname, *levelname, *diskname; 1033219974Smav int *nargs, *force; 1034219974Smav off_t size, sectorsize, strip; 1035219974Smav intmax_t *sizearg, *striparg; 1036219974Smav int numdisks, i, len, level, qual, update; 1037219974Smav int error; 1038219974Smav 1039219974Smav sc = md->mdo_softc; 1040219974Smav mdi = (struct g_raid_md_jmicron_object *)md; 1041219974Smav verb = gctl_get_param(req, "verb", NULL); 1042219974Smav nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 1043219974Smav error = 0; 1044219974Smav if (strcmp(verb, "label") == 0) { 1045219974Smav 1046219974Smav if (*nargs < 4) { 1047219974Smav gctl_error(req, "Invalid number of arguments."); 1048219974Smav return (-1); 1049219974Smav } 1050219974Smav volname = gctl_get_asciiparam(req, "arg1"); 1051219974Smav if (volname == NULL) { 1052219974Smav gctl_error(req, "No volume name."); 1053219974Smav return (-2); 1054219974Smav } 1055219974Smav levelname = gctl_get_asciiparam(req, "arg2"); 1056219974Smav if (levelname == NULL) { 1057219974Smav gctl_error(req, "No RAID level."); 1058219974Smav return (-3); 1059219974Smav } 1060219974Smav if (g_raid_volume_str2level(levelname, &level, &qual)) { 1061219974Smav gctl_error(req, "Unknown RAID level '%s'.", levelname); 1062219974Smav return (-4); 1063219974Smav } 1064219974Smav numdisks = *nargs - 3; 1065219974Smav force = gctl_get_paraml(req, "force", sizeof(*force)); 1066219974Smav if (!g_raid_md_jmicron_supported(level, qual, numdisks, 1067219974Smav force ? *force : 0)) { 1068219974Smav gctl_error(req, "Unsupported RAID level " 1069219974Smav "(0x%02x/0x%02x), or number of disks (%d).", 1070219974Smav level, qual, numdisks); 1071219974Smav return (-5); 1072219974Smav } 1073219974Smav 1074219974Smav /* Search for disks, connect them and probe. */ 1075219974Smav size = 0x7fffffffffffffffllu; 1076219974Smav sectorsize = 0; 1077219974Smav for (i = 0; i < numdisks; i++) { 1078219974Smav snprintf(arg, sizeof(arg), "arg%d", i + 3); 1079219974Smav diskname = gctl_get_asciiparam(req, arg); 1080219974Smav if (diskname == NULL) { 1081219974Smav gctl_error(req, "No disk name (%s).", arg); 1082219974Smav error = -6; 1083219974Smav break; 1084219974Smav } 1085219974Smav if (strcmp(diskname, "NONE") == 0) { 1086219974Smav cp = NULL; 1087219974Smav pp = NULL; 1088219974Smav } else { 1089219974Smav g_topology_lock(); 1090219974Smav cp = g_raid_open_consumer(sc, diskname); 1091219974Smav if (cp == NULL) { 1092219974Smav gctl_error(req, "Can't open '%s'.", 1093219974Smav diskname); 1094219974Smav g_topology_unlock(); 1095219974Smav error = -7; 1096219974Smav break; 1097219974Smav } 1098219974Smav pp = cp->provider; 1099219974Smav } 1100219974Smav pd = malloc(sizeof(*pd), M_MD_JMICRON, M_WAITOK | M_ZERO); 1101219974Smav pd->pd_disk_pos = i; 1102219974Smav pd->pd_disk_id = arc4random() & JMICRON_DISK_MASK; 1103219974Smav disk = g_raid_create_disk(sc); 1104219974Smav disk->d_md_data = (void *)pd; 1105219974Smav disk->d_consumer = cp; 1106219974Smav if (cp == NULL) 1107219974Smav continue; 1108219974Smav cp->private = disk; 1109219974Smav g_topology_unlock(); 1110219974Smav 1111219974Smav /* Read kernel dumping information. */ 1112219974Smav disk->d_kd.offset = 0; 1113219974Smav disk->d_kd.length = OFF_MAX; 1114219974Smav len = sizeof(disk->d_kd); 1115219974Smav g_io_getattr("GEOM::kerneldump", cp, &len, &disk->d_kd); 1116219974Smav if (disk->d_kd.di.dumper == NULL) 1117219974Smav G_RAID_DEBUG1(2, sc, 1118219974Smav "Dumping not supported by %s.", 1119219974Smav cp->provider->name); 1120219974Smav 1121219974Smav pd->pd_disk_size = pp->mediasize; 1122219974Smav if (size > pp->mediasize) 1123219974Smav size = pp->mediasize; 1124219974Smav if (sectorsize < pp->sectorsize) 1125219974Smav sectorsize = pp->sectorsize; 1126219974Smav } 1127219974Smav if (error != 0) 1128219974Smav return (error); 1129219974Smav 1130220210Smav if (sectorsize <= 0) { 1131220210Smav gctl_error(req, "Can't get sector size."); 1132220210Smav return (-8); 1133220210Smav } 1134220210Smav 1135219974Smav /* Reserve space for metadata. */ 1136219974Smav size -= sectorsize; 1137219974Smav 1138219974Smav /* Handle size argument. */ 1139219974Smav len = sizeof(*sizearg); 1140219974Smav sizearg = gctl_get_param(req, "size", &len); 1141219974Smav if (sizearg != NULL && len == sizeof(*sizearg) && 1142219974Smav *sizearg > 0) { 1143219974Smav if (*sizearg > size) { 1144219974Smav gctl_error(req, "Size too big %lld > %lld.", 1145219974Smav (long long)*sizearg, (long long)size); 1146219974Smav return (-9); 1147219974Smav } 1148219974Smav size = *sizearg; 1149219974Smav } 1150219974Smav 1151219974Smav /* Handle strip argument. */ 1152219974Smav strip = 131072; 1153219974Smav len = sizeof(*striparg); 1154219974Smav striparg = gctl_get_param(req, "strip", &len); 1155219974Smav if (striparg != NULL && len == sizeof(*striparg) && 1156219974Smav *striparg > 0) { 1157219974Smav if (*striparg < sectorsize) { 1158219974Smav gctl_error(req, "Strip size too small."); 1159219974Smav return (-10); 1160219974Smav } 1161219974Smav if (*striparg % sectorsize != 0) { 1162219974Smav gctl_error(req, "Incorrect strip size."); 1163219974Smav return (-11); 1164219974Smav } 1165219974Smav if (strip > 65535 * sectorsize) { 1166219974Smav gctl_error(req, "Strip size too big."); 1167219974Smav return (-12); 1168219974Smav } 1169219974Smav strip = *striparg; 1170219974Smav } 1171219974Smav 1172219974Smav /* Round size down to strip or sector. */ 1173219974Smav if (level == G_RAID_VOLUME_RL_RAID1) 1174219974Smav size -= (size % sectorsize); 1175219974Smav else if (level == G_RAID_VOLUME_RL_RAID1E && 1176219974Smav (numdisks & 1) != 0) 1177219974Smav size -= (size % (2 * strip)); 1178219974Smav else 1179219974Smav size -= (size % strip); 1180219974Smav if (size <= 0) { 1181219974Smav gctl_error(req, "Size too small."); 1182219974Smav return (-13); 1183219974Smav } 1184219974Smav if (size > 0xffffffffffffllu * sectorsize) { 1185219974Smav gctl_error(req, "Size too big."); 1186219974Smav return (-14); 1187219974Smav } 1188219974Smav 1189219974Smav /* We have all we need, create things: volume, ... */ 1190219974Smav mdi->mdio_total_disks = numdisks; 1191219974Smav mdi->mdio_started = 1; 1192219974Smav vol = g_raid_create_volume(sc, volname, -1); 1193219974Smav vol->v_md_data = (void *)(intptr_t)0; 1194219974Smav vol->v_raid_level = level; 1195219974Smav vol->v_raid_level_qualifier = G_RAID_VOLUME_RLQ_NONE; 1196219974Smav vol->v_strip_size = strip; 1197219974Smav vol->v_disks_count = numdisks; 1198219974Smav if (level == G_RAID_VOLUME_RL_RAID0 || 1199219974Smav level == G_RAID_VOLUME_RL_CONCAT || 1200219974Smav level == G_RAID_VOLUME_RL_SINGLE) 1201219974Smav vol->v_mediasize = size * numdisks; 1202219974Smav else if (level == G_RAID_VOLUME_RL_RAID1) 1203219974Smav vol->v_mediasize = size; 1204219974Smav else if (level == G_RAID_VOLUME_RL_RAID5) 1205219974Smav vol->v_mediasize = size * (numdisks - 1); 1206219974Smav else { /* RAID1E */ 1207219974Smav vol->v_mediasize = ((size * numdisks) / strip / 2) * 1208219974Smav strip; 1209219974Smav } 1210219974Smav vol->v_sectorsize = sectorsize; 1211219974Smav g_raid_start_volume(vol); 1212219974Smav 1213219974Smav /* , and subdisks. */ 1214219974Smav TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 1215219974Smav pd = (struct g_raid_md_jmicron_perdisk *)disk->d_md_data; 1216219974Smav sd = &vol->v_subdisks[pd->pd_disk_pos]; 1217219974Smav sd->sd_disk = disk; 1218219974Smav sd->sd_offset = 0; 1219219974Smav sd->sd_size = size; 1220219974Smav TAILQ_INSERT_TAIL(&disk->d_subdisks, sd, sd_next); 1221219974Smav if (sd->sd_disk->d_consumer != NULL) { 1222219974Smav g_raid_change_disk_state(disk, 1223219974Smav G_RAID_DISK_S_ACTIVE); 1224219974Smav g_raid_change_subdisk_state(sd, 1225219974Smav G_RAID_SUBDISK_S_ACTIVE); 1226219974Smav g_raid_event_send(sd, G_RAID_SUBDISK_E_NEW, 1227219974Smav G_RAID_EVENT_SUBDISK); 1228219974Smav } else { 1229219974Smav g_raid_change_disk_state(disk, G_RAID_DISK_S_OFFLINE); 1230219974Smav } 1231219974Smav } 1232219974Smav 1233219974Smav /* Write metadata based on created entities. */ 1234219974Smav G_RAID_DEBUG1(0, sc, "Array started."); 1235219974Smav g_raid_md_write_jmicron(md, NULL, NULL, NULL); 1236219974Smav 1237219974Smav /* Pickup any STALE/SPARE disks to refill array if needed. */ 1238219974Smav g_raid_md_jmicron_refill(sc); 1239219974Smav 1240219974Smav g_raid_event_send(vol, G_RAID_VOLUME_E_START, 1241219974Smav G_RAID_EVENT_VOLUME); 1242219974Smav return (0); 1243219974Smav } 1244219974Smav if (strcmp(verb, "delete") == 0) { 1245219974Smav 1246219974Smav /* Check if some volume is still open. */ 1247219974Smav force = gctl_get_paraml(req, "force", sizeof(*force)); 1248219974Smav if (force != NULL && *force == 0 && 1249219974Smav g_raid_nopens(sc) != 0) { 1250219974Smav gctl_error(req, "Some volume is still open."); 1251219974Smav return (-4); 1252219974Smav } 1253219974Smav 1254219974Smav TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 1255219974Smav if (disk->d_consumer) 1256219974Smav jmicron_meta_erase(disk->d_consumer); 1257219974Smav } 1258219974Smav g_raid_destroy_node(sc, 0); 1259219974Smav return (0); 1260219974Smav } 1261219974Smav if (strcmp(verb, "remove") == 0 || 1262219974Smav strcmp(verb, "fail") == 0) { 1263219974Smav if (*nargs < 2) { 1264219974Smav gctl_error(req, "Invalid number of arguments."); 1265219974Smav return (-1); 1266219974Smav } 1267219974Smav for (i = 1; i < *nargs; i++) { 1268219974Smav snprintf(arg, sizeof(arg), "arg%d", i); 1269219974Smav diskname = gctl_get_asciiparam(req, arg); 1270219974Smav if (diskname == NULL) { 1271219974Smav gctl_error(req, "No disk name (%s).", arg); 1272219974Smav error = -2; 1273219974Smav break; 1274219974Smav } 1275219974Smav if (strncmp(diskname, "/dev/", 5) == 0) 1276219974Smav diskname += 5; 1277219974Smav 1278219974Smav TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 1279219974Smav if (disk->d_consumer != NULL && 1280219974Smav disk->d_consumer->provider != NULL && 1281219974Smav strcmp(disk->d_consumer->provider->name, 1282219974Smav diskname) == 0) 1283219974Smav break; 1284219974Smav } 1285219974Smav if (disk == NULL) { 1286219974Smav gctl_error(req, "Disk '%s' not found.", 1287219974Smav diskname); 1288219974Smav error = -3; 1289219974Smav break; 1290219974Smav } 1291219974Smav 1292219974Smav if (strcmp(verb, "fail") == 0) { 1293219974Smav g_raid_md_fail_disk_jmicron(md, NULL, disk); 1294219974Smav continue; 1295219974Smav } 1296219974Smav 1297219974Smav pd = (struct g_raid_md_jmicron_perdisk *)disk->d_md_data; 1298219974Smav 1299219974Smav /* Erase metadata on deleting disk. */ 1300219974Smav jmicron_meta_erase(disk->d_consumer); 1301219974Smav 1302219974Smav /* If disk was assigned, just update statuses. */ 1303219974Smav if (pd->pd_disk_pos >= 0) { 1304219974Smav g_raid_change_disk_state(disk, G_RAID_DISK_S_OFFLINE); 1305220209Smav g_raid_kill_consumer(sc, disk->d_consumer); 1306220209Smav disk->d_consumer = NULL; 1307219974Smav TAILQ_FOREACH(sd, &disk->d_subdisks, sd_next) { 1308219974Smav g_raid_change_subdisk_state(sd, 1309219974Smav G_RAID_SUBDISK_S_NONE); 1310219974Smav g_raid_event_send(sd, G_RAID_SUBDISK_E_DISCONNECTED, 1311219974Smav G_RAID_EVENT_SUBDISK); 1312219974Smav } 1313219974Smav } else { 1314219974Smav /* Otherwise -- delete. */ 1315219974Smav g_raid_change_disk_state(disk, G_RAID_DISK_S_NONE); 1316219974Smav g_raid_destroy_disk(disk); 1317219974Smav } 1318219974Smav } 1319219974Smav 1320219974Smav /* Write updated metadata to remaining disks. */ 1321219974Smav g_raid_md_write_jmicron(md, NULL, NULL, NULL); 1322219974Smav 1323219974Smav /* Check if anything left except placeholders. */ 1324219974Smav if (g_raid_ndisks(sc, -1) == 1325219974Smav g_raid_ndisks(sc, G_RAID_DISK_S_OFFLINE)) 1326219974Smav g_raid_destroy_node(sc, 0); 1327219974Smav else 1328219974Smav g_raid_md_jmicron_refill(sc); 1329219974Smav return (error); 1330219974Smav } 1331219974Smav if (strcmp(verb, "insert") == 0) { 1332219974Smav if (*nargs < 2) { 1333219974Smav gctl_error(req, "Invalid number of arguments."); 1334219974Smav return (-1); 1335219974Smav } 1336219974Smav update = 0; 1337219974Smav for (i = 1; i < *nargs; i++) { 1338219974Smav /* Get disk name. */ 1339219974Smav snprintf(arg, sizeof(arg), "arg%d", i); 1340219974Smav diskname = gctl_get_asciiparam(req, arg); 1341219974Smav if (diskname == NULL) { 1342219974Smav gctl_error(req, "No disk name (%s).", arg); 1343219974Smav error = -3; 1344219974Smav break; 1345219974Smav } 1346219974Smav 1347219974Smav /* Try to find provider with specified name. */ 1348219974Smav g_topology_lock(); 1349219974Smav cp = g_raid_open_consumer(sc, diskname); 1350219974Smav if (cp == NULL) { 1351219974Smav gctl_error(req, "Can't open disk '%s'.", 1352219974Smav diskname); 1353219974Smav g_topology_unlock(); 1354219974Smav error = -4; 1355219974Smav break; 1356219974Smav } 1357219974Smav pp = cp->provider; 1358219974Smav 1359219974Smav pd = malloc(sizeof(*pd), M_MD_JMICRON, M_WAITOK | M_ZERO); 1360219974Smav pd->pd_disk_pos = -3; 1361219974Smav pd->pd_disk_id = arc4random() & JMICRON_DISK_MASK; 1362219974Smav pd->pd_disk_size = pp->mediasize; 1363219974Smav 1364219974Smav disk = g_raid_create_disk(sc); 1365219974Smav disk->d_consumer = cp; 1366219974Smav disk->d_md_data = (void *)pd; 1367219974Smav cp->private = disk; 1368219974Smav g_topology_unlock(); 1369219974Smav 1370219974Smav /* Read kernel dumping information. */ 1371219974Smav disk->d_kd.offset = 0; 1372219974Smav disk->d_kd.length = OFF_MAX; 1373219974Smav len = sizeof(disk->d_kd); 1374219974Smav g_io_getattr("GEOM::kerneldump", cp, &len, &disk->d_kd); 1375219974Smav if (disk->d_kd.di.dumper == NULL) 1376219974Smav G_RAID_DEBUG1(2, sc, 1377219974Smav "Dumping not supported by %s.", 1378219974Smav cp->provider->name); 1379219974Smav 1380219974Smav /* Welcome the "new" disk. */ 1381219974Smav update += g_raid_md_jmicron_start_disk(disk); 1382219974Smav if (disk->d_state != G_RAID_DISK_S_ACTIVE && 1383219974Smav disk->d_state != G_RAID_DISK_S_SPARE) { 1384219974Smav gctl_error(req, "Disk '%s' doesn't fit.", 1385219974Smav diskname); 1386219974Smav g_raid_destroy_disk(disk); 1387219974Smav error = -8; 1388219974Smav break; 1389219974Smav } 1390219974Smav } 1391219974Smav 1392219974Smav /* Write new metadata if we changed something. */ 1393219974Smav if (update) 1394219974Smav g_raid_md_write_jmicron(md, NULL, NULL, NULL); 1395219974Smav return (error); 1396219974Smav } 1397219974Smav gctl_error(req, "Command '%s' is not supported.", verb); 1398219974Smav return (-100); 1399219974Smav} 1400219974Smav 1401219974Smavstatic int 1402219974Smavg_raid_md_write_jmicron(struct g_raid_md_object *md, struct g_raid_volume *tvol, 1403219974Smav struct g_raid_subdisk *tsd, struct g_raid_disk *tdisk) 1404219974Smav{ 1405219974Smav struct g_raid_softc *sc; 1406219974Smav struct g_raid_volume *vol; 1407219974Smav struct g_raid_subdisk *sd; 1408219974Smav struct g_raid_disk *disk; 1409219974Smav struct g_raid_md_jmicron_object *mdi; 1410219974Smav struct g_raid_md_jmicron_perdisk *pd; 1411219974Smav struct jmicron_raid_conf *meta; 1412219974Smav int i, spares; 1413219974Smav 1414219974Smav sc = md->mdo_softc; 1415219974Smav mdi = (struct g_raid_md_jmicron_object *)md; 1416219974Smav 1417219974Smav if (sc->sc_stopping == G_RAID_DESTROY_HARD) 1418219974Smav return (0); 1419219974Smav 1420219974Smav /* There is only one volume. */ 1421219974Smav vol = TAILQ_FIRST(&sc->sc_volumes); 1422219974Smav 1423219974Smav /* Fill global fields. */ 1424219974Smav meta = malloc(sizeof(*meta), M_MD_JMICRON, M_WAITOK | M_ZERO); 1425219974Smav strncpy(meta->signature, JMICRON_MAGIC, 2); 1426219974Smav meta->version = JMICRON_VERSION; 1427219974Smav jmicron_meta_put_name(meta, vol->v_name); 1428219974Smav if (vol->v_raid_level == G_RAID_VOLUME_RL_RAID0) 1429219974Smav meta->type = JMICRON_T_RAID0; 1430219974Smav else if (vol->v_raid_level == G_RAID_VOLUME_RL_RAID1) 1431219974Smav meta->type = JMICRON_T_RAID1; 1432219974Smav else if (vol->v_raid_level == G_RAID_VOLUME_RL_RAID1E) 1433219974Smav meta->type = JMICRON_T_RAID01; 1434219974Smav else if (vol->v_raid_level == G_RAID_VOLUME_RL_CONCAT || 1435219974Smav vol->v_raid_level == G_RAID_VOLUME_RL_SINGLE) 1436219974Smav meta->type = JMICRON_T_CONCAT; 1437219974Smav else 1438219974Smav meta->type = JMICRON_T_RAID5; 1439219974Smav meta->stripe_shift = fls(vol->v_strip_size / 2048); 1440219974Smav meta->flags = JMICRON_F_READY | JMICRON_F_BOOTABLE; 1441219974Smav for (i = 0; i < vol->v_disks_count; i++) { 1442219974Smav sd = &vol->v_subdisks[i]; 1443219974Smav if (sd->sd_disk == NULL || sd->sd_disk->d_md_data == NULL) 1444219974Smav meta->disks[i] = 0xffffffff; 1445219974Smav else { 1446219974Smav pd = (struct g_raid_md_jmicron_perdisk *) 1447219974Smav sd->sd_disk->d_md_data; 1448219974Smav meta->disks[i] = pd->pd_disk_id; 1449219974Smav } 1450219974Smav if (sd->sd_state < G_RAID_SUBDISK_S_STALE) 1451219974Smav meta->flags |= JMICRON_F_BADSEC; 1452219974Smav if (vol->v_dirty) 1453219974Smav meta->flags |= JMICRON_F_UNSYNC; 1454219974Smav } 1455219974Smav 1456219974Smav /* Put spares to their slots. */ 1457219974Smav spares = 0; 1458219974Smav TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 1459219974Smav pd = (struct g_raid_md_jmicron_perdisk *)disk->d_md_data; 1460219974Smav if (disk->d_state != G_RAID_DISK_S_SPARE) 1461219974Smav continue; 1462219974Smav meta->spare[spares] = pd->pd_disk_id; 1463219974Smav if (++spares >= 2) 1464219974Smav break; 1465219974Smav } 1466219974Smav 1467219974Smav /* We are done. Print meta data and store them to disks. */ 1468219974Smav if (mdi->mdio_meta != NULL) 1469219974Smav free(mdi->mdio_meta, M_MD_JMICRON); 1470219974Smav mdi->mdio_meta = meta; 1471219974Smav TAILQ_FOREACH(disk, &sc->sc_disks, d_next) { 1472219974Smav pd = (struct g_raid_md_jmicron_perdisk *)disk->d_md_data; 1473219974Smav if (disk->d_state != G_RAID_DISK_S_ACTIVE && 1474219974Smav disk->d_state != G_RAID_DISK_S_SPARE) 1475219974Smav continue; 1476219974Smav if (pd->pd_meta != NULL) { 1477219974Smav free(pd->pd_meta, M_MD_JMICRON); 1478219974Smav pd->pd_meta = NULL; 1479219974Smav } 1480219974Smav pd->pd_meta = jmicron_meta_copy(meta); 1481219974Smav pd->pd_meta->disk_id = pd->pd_disk_id; 1482219974Smav if ((sd = TAILQ_FIRST(&disk->d_subdisks)) != NULL) { 1483219974Smav pd->pd_meta->offset = 1484219974Smav (sd->sd_offset / 512) / 16; 1485219974Smav pd->pd_meta->disk_sectors_high = 1486219974Smav (sd->sd_size / 512) >> 16; 1487219974Smav pd->pd_meta->disk_sectors_low = 1488219974Smav (sd->sd_size / 512) & 0xffff; 1489219974Smav if (sd->sd_state < G_RAID_SUBDISK_S_STALE) 1490219974Smav pd->pd_meta->flags &= ~JMICRON_F_BADSEC; 1491219974Smav else if (sd->sd_state < G_RAID_SUBDISK_S_ACTIVE) 1492219974Smav pd->pd_meta->flags |= JMICRON_F_UNSYNC; 1493219974Smav } 1494219974Smav G_RAID_DEBUG(1, "Writing JMicron metadata to %s", 1495219974Smav g_raid_get_diskname(disk)); 1496219974Smav g_raid_md_jmicron_print(pd->pd_meta); 1497219974Smav jmicron_meta_write(disk->d_consumer, pd->pd_meta); 1498219974Smav } 1499219974Smav return (0); 1500219974Smav} 1501219974Smav 1502219974Smavstatic int 1503219974Smavg_raid_md_fail_disk_jmicron(struct g_raid_md_object *md, 1504219974Smav struct g_raid_subdisk *tsd, struct g_raid_disk *tdisk) 1505219974Smav{ 1506219974Smav struct g_raid_softc *sc; 1507219974Smav struct g_raid_md_jmicron_perdisk *pd; 1508219974Smav struct g_raid_subdisk *sd; 1509219974Smav 1510219974Smav sc = md->mdo_softc; 1511219974Smav pd = (struct g_raid_md_jmicron_perdisk *)tdisk->d_md_data; 1512219974Smav 1513219974Smav /* We can't fail disk that is not a part of array now. */ 1514219974Smav if (pd->pd_disk_pos < 0) 1515219974Smav return (-1); 1516219974Smav 1517219974Smav if (tdisk->d_consumer) 1518219974Smav jmicron_meta_erase(tdisk->d_consumer); 1519219974Smav 1520219974Smav /* Change states. */ 1521219974Smav g_raid_change_disk_state(tdisk, G_RAID_DISK_S_FAILED); 1522219974Smav TAILQ_FOREACH(sd, &tdisk->d_subdisks, sd_next) { 1523219974Smav g_raid_change_subdisk_state(sd, 1524219974Smav G_RAID_SUBDISK_S_FAILED); 1525219974Smav g_raid_event_send(sd, G_RAID_SUBDISK_E_FAILED, 1526219974Smav G_RAID_EVENT_SUBDISK); 1527219974Smav } 1528219974Smav 1529219974Smav /* Write updated metadata to remaining disks. */ 1530219974Smav g_raid_md_write_jmicron(md, NULL, NULL, tdisk); 1531219974Smav 1532219974Smav /* Check if anything left except placeholders. */ 1533219974Smav if (g_raid_ndisks(sc, -1) == 1534219974Smav g_raid_ndisks(sc, G_RAID_DISK_S_OFFLINE)) 1535219974Smav g_raid_destroy_node(sc, 0); 1536219974Smav else 1537219974Smav g_raid_md_jmicron_refill(sc); 1538219974Smav return (0); 1539219974Smav} 1540219974Smav 1541219974Smavstatic int 1542219974Smavg_raid_md_free_disk_jmicron(struct g_raid_md_object *md, 1543219974Smav struct g_raid_disk *disk) 1544219974Smav{ 1545219974Smav struct g_raid_md_jmicron_perdisk *pd; 1546219974Smav 1547219974Smav pd = (struct g_raid_md_jmicron_perdisk *)disk->d_md_data; 1548219974Smav if (pd->pd_meta != NULL) { 1549219974Smav free(pd->pd_meta, M_MD_JMICRON); 1550219974Smav pd->pd_meta = NULL; 1551219974Smav } 1552219974Smav free(pd, M_MD_JMICRON); 1553219974Smav disk->d_md_data = NULL; 1554219974Smav return (0); 1555219974Smav} 1556219974Smav 1557219974Smavstatic int 1558219974Smavg_raid_md_free_jmicron(struct g_raid_md_object *md) 1559219974Smav{ 1560219974Smav struct g_raid_md_jmicron_object *mdi; 1561219974Smav 1562219974Smav mdi = (struct g_raid_md_jmicron_object *)md; 1563219974Smav if (!mdi->mdio_started) { 1564219974Smav mdi->mdio_started = 0; 1565219974Smav callout_stop(&mdi->mdio_start_co); 1566219974Smav G_RAID_DEBUG1(1, md->mdo_softc, 1567219974Smav "root_mount_rel %p", mdi->mdio_rootmount); 1568219974Smav root_mount_rel(mdi->mdio_rootmount); 1569219974Smav mdi->mdio_rootmount = NULL; 1570219974Smav } 1571219974Smav if (mdi->mdio_meta != NULL) { 1572219974Smav free(mdi->mdio_meta, M_MD_JMICRON); 1573219974Smav mdi->mdio_meta = NULL; 1574219974Smav } 1575219974Smav return (0); 1576219974Smav} 1577219974Smav 1578219974SmavG_RAID_MD_DECLARE(g_raid_md_jmicron); 1579