1 /* $OpenBSD: softraid_raid0.c,v 1.12 2009/04/28 02:54:53 marco Exp $ */ 2 /* 3 * Copyright (c) 2008 Marco Peereboom <marco@peereboom.us> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "bio.h" 19 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/buf.h> 23 #include <sys/device.h> 24 #include <sys/ioctl.h> 25 #include <sys/proc.h> 26 #include <sys/malloc.h> 27 #include <sys/kernel.h> 28 #include <sys/disk.h> 29 #include <sys/rwlock.h> 30 #include <sys/queue.h> 31 #include <sys/fcntl.h> 32 #include <sys/disklabel.h> 33 #include <sys/mount.h> 34 #include <sys/sensors.h> 35 #include <sys/stat.h> 36 #include <sys/conf.h> 37 #include <sys/uio.h> 38 39 #include <scsi/scsi_all.h> 40 #include <scsi/scsiconf.h> 41 #include <scsi/scsi_disk.h> 42 43 #include <dev/softraidvar.h> 44 #include <dev/rndvar.h> 45 46 /* RAID 0 functions */ 47 int 48 sr_raid0_alloc_resources(struct sr_discipline *sd) 49 { 50 int rv = EINVAL; 51 52 if (!sd) 53 return (rv); 54 55 DNPRINTF(SR_D_DIS, "%s: sr_raid0_alloc_resources\n", 56 DEVNAME(sd->sd_sc)); 57 58 if (sr_wu_alloc(sd)) 59 goto bad; 60 if (sr_ccb_alloc(sd)) 61 goto bad; 62 63 /* setup runtime values */ 64 sd->mds.mdd_raid0.sr0_strip_bits = 65 sr_validate_stripsize(sd->sd_meta->ssdi.ssd_strip_size); 66 if (sd->mds.mdd_raid0.sr0_strip_bits == -1) 67 goto bad; 68 69 rv = 0; 70 bad: 71 return (rv); 72 } 73 74 int 75 sr_raid0_free_resources(struct sr_discipline *sd) 76 { 77 int rv = EINVAL; 78 79 if (!sd) 80 return (rv); 81 82 DNPRINTF(SR_D_DIS, "%s: sr_raid0_free_resources\n", 83 DEVNAME(sd->sd_sc)); 84 85 sr_wu_free(sd); 86 sr_ccb_free(sd); 87 88 rv = 0; 89 return (rv); 90 } 91 92 void 93 sr_raid0_set_chunk_state(struct sr_discipline *sd, int c, int new_state) 94 { 95 int old_state, s; 96 97 DNPRINTF(SR_D_STATE, "%s: %s: %s: sr_raid_set_chunk_state %d -> %d\n", 98 DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, 99 sd->sd_vol.sv_chunks[c]->src_meta.scmi.scm_devname, c, new_state); 100 101 /* ok to go to splbio since this only happens in error path */ 102 s = splbio(); 103 old_state = sd->sd_vol.sv_chunks[c]->src_meta.scm_status; 104 105 /* multiple IOs to the same chunk that fail will come through here */ 106 if (old_state == new_state) 107 goto done; 108 109 switch (old_state) { 110 case BIOC_SDONLINE: 111 if (new_state == BIOC_SDOFFLINE) 112 break; 113 else 114 goto die; 115 break; 116 117 case BIOC_SDOFFLINE: 118 goto die; 119 120 default: 121 die: 122 splx(s); /* XXX */ 123 panic("%s: %s: %s: invalid chunk state transition " 124 "%d -> %d\n", DEVNAME(sd->sd_sc), 125 sd->sd_meta->ssd_devname, 126 sd->sd_vol.sv_chunks[c]->src_meta.scmi.scm_devname, 127 old_state, new_state); 128 /* NOTREACHED */ 129 } 130 131 sd->sd_vol.sv_chunks[c]->src_meta.scm_status = new_state; 132 sd->sd_set_vol_state(sd); 133 134 sd->sd_must_flush = 1; 135 workq_add_task(NULL, 0, sr_meta_save_callback, sd, NULL); 136 done: 137 splx(s); 138 } 139 140 void 141 sr_raid0_set_vol_state(struct sr_discipline *sd) 142 { 143 int states[SR_MAX_STATES]; 144 int new_state, i, s, nd; 145 int old_state = sd->sd_vol_status; 146 147 DNPRINTF(SR_D_STATE, "%s: %s: sr_raid_set_vol_state\n", 148 DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname); 149 150 nd = sd->sd_meta->ssdi.ssd_chunk_no; 151 152 for (i = 0; i < SR_MAX_STATES; i++) 153 states[i] = 0; 154 155 for (i = 0; i < nd; i++) { 156 s = sd->sd_vol.sv_chunks[i]->src_meta.scm_status; 157 if (s > SR_MAX_STATES) 158 panic("%s: %s: %s: invalid chunk state", 159 DEVNAME(sd->sd_sc), 160 sd->sd_meta->ssd_devname, 161 sd->sd_vol.sv_chunks[i]->src_meta.scmi.scm_devname); 162 states[s]++; 163 } 164 165 if (states[BIOC_SDONLINE] == nd) 166 new_state = BIOC_SVONLINE; 167 else 168 new_state = BIOC_SVOFFLINE; 169 170 DNPRINTF(SR_D_STATE, "%s: %s: sr_raid_set_vol_state %d -> %d\n", 171 DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, 172 old_state, new_state); 173 174 switch (old_state) { 175 case BIOC_SVONLINE: 176 if (new_state == BIOC_SVOFFLINE || new_state == BIOC_SVONLINE) 177 break; 178 else 179 goto die; 180 break; 181 182 case BIOC_SVOFFLINE: 183 /* XXX this might be a little too much */ 184 goto die; 185 186 default: 187 die: 188 panic("%s: %s: invalid volume state transition " 189 "%d -> %d\n", DEVNAME(sd->sd_sc), 190 sd->sd_meta->ssd_devname, 191 old_state, new_state); 192 /* NOTREACHED */ 193 } 194 195 sd->sd_vol_status = new_state; 196 } 197 198 int 199 sr_raid0_rw(struct sr_workunit *wu) 200 { 201 struct sr_discipline *sd = wu->swu_dis; 202 struct scsi_xfer *xs = wu->swu_xs; 203 struct sr_ccb *ccb; 204 struct sr_chunk *scp; 205 int s; 206 daddr64_t blk, lbaoffs, strip_no, chunk, stripoffs; 207 daddr64_t strip_size, no_chunk, chunkoffs, physoffs; 208 daddr64_t strip_bits, length, leftover; 209 u_int8_t *data; 210 211 /* blk and scsi error will be handled by sr_validate_io */ 212 if (sr_validate_io(wu, &blk, "sr_raid0_rw")) 213 goto bad; 214 215 strip_size = sd->sd_meta->ssdi.ssd_strip_size; 216 strip_bits = sd->mds.mdd_raid0.sr0_strip_bits; 217 no_chunk = sd->sd_meta->ssdi.ssd_chunk_no; 218 219 DNPRINTF(SR_D_DIS, "%s: %s: front end io: lba %lld size %d\n", 220 DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, 221 blk, xs->datalen); 222 223 /* all offs are in bytes */ 224 lbaoffs = blk << DEV_BSHIFT; 225 strip_no = lbaoffs >> strip_bits; 226 chunk = strip_no % no_chunk; 227 stripoffs = lbaoffs & (strip_size - 1); 228 chunkoffs = (strip_no / no_chunk) << strip_bits; 229 physoffs = chunkoffs + stripoffs + 230 ((SR_META_OFFSET + SR_META_SIZE) << DEV_BSHIFT); 231 length = MIN(xs->datalen, strip_size - stripoffs); 232 leftover = xs->datalen; 233 data = xs->data; 234 for (wu->swu_io_count = 1;; wu->swu_io_count++) { 235 /* make sure chunk is online */ 236 scp = sd->sd_vol.sv_chunks[chunk]; 237 if (scp->src_meta.scm_status != BIOC_SDONLINE) { 238 goto bad; 239 } 240 241 ccb = sr_ccb_get(sd); 242 if (!ccb) { 243 /* should never happen but handle more gracefully */ 244 printf("%s: %s: too many ccbs queued\n", 245 DEVNAME(sd->sd_sc), 246 sd->sd_meta->ssd_devname); 247 goto bad; 248 } 249 250 DNPRINTF(SR_D_DIS, "%s: %s raid io: lbaoffs: %lld " 251 "strip_no: %lld chunk: %lld stripoffs: %lld " 252 "chunkoffs: %lld physoffs: %lld length: %lld " 253 "leftover: %lld data: %p\n", 254 DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, lbaoffs, 255 strip_no, chunk, stripoffs, chunkoffs, physoffs, length, 256 leftover, data); 257 258 ccb->ccb_buf.b_flags = B_CALL; 259 ccb->ccb_buf.b_iodone = sr_raid0_intr; 260 ccb->ccb_buf.b_blkno = physoffs >> DEV_BSHIFT; 261 ccb->ccb_buf.b_bcount = length; 262 ccb->ccb_buf.b_bufsize = length; 263 ccb->ccb_buf.b_resid = length; 264 ccb->ccb_buf.b_data = data; 265 ccb->ccb_buf.b_error = 0; 266 ccb->ccb_buf.b_proc = curproc; 267 ccb->ccb_wu = wu; 268 ccb->ccb_buf.b_flags |= xs->flags & SCSI_DATA_IN ? 269 B_READ : B_WRITE; 270 ccb->ccb_target = chunk; 271 ccb->ccb_buf.b_dev = sd->sd_vol.sv_chunks[chunk]->src_dev_mm; 272 ccb->ccb_buf.b_vp = NULL; 273 LIST_INIT(&ccb->ccb_buf.b_dep); 274 TAILQ_INSERT_TAIL(&wu->swu_ccb, ccb, ccb_link); 275 276 DNPRINTF(SR_D_DIS, "%s: %s: sr_raid0: b_bcount: %d " 277 "b_blkno: %lld b_flags 0x%0x b_data %p\n", 278 DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, 279 ccb->ccb_buf.b_bcount, ccb->ccb_buf.b_blkno, 280 ccb->ccb_buf.b_flags, ccb->ccb_buf.b_data); 281 282 leftover -= length; 283 if (leftover == 0) 284 break; 285 286 data += length; 287 if (++chunk > no_chunk - 1) { 288 chunk = 0; 289 physoffs += length; 290 } else if (wu->swu_io_count == 1) 291 physoffs -= stripoffs; 292 length = MIN(leftover,strip_size); 293 } 294 295 s = splbio(); 296 297 if (sr_check_io_collision(wu)) 298 goto queued; 299 300 sr_raid_startwu(wu); 301 queued: 302 splx(s); 303 return (0); 304 bad: 305 /* wu is unwound by sr_wu_put */ 306 return (1); 307 } 308 309 void 310 sr_raid0_intr(struct buf *bp) 311 { 312 struct sr_ccb *ccb = (struct sr_ccb *)bp; 313 struct sr_workunit *wu = ccb->ccb_wu, *wup; 314 struct sr_discipline *sd = wu->swu_dis; 315 struct scsi_xfer *xs = wu->swu_xs; 316 struct sr_softc *sc = sd->sd_sc; 317 int s, pend; 318 319 DNPRINTF(SR_D_INTR, "%s: sr_intr bp %x xs %x\n", 320 DEVNAME(sc), bp, xs); 321 322 DNPRINTF(SR_D_INTR, "%s: sr_intr: b_bcount: %d b_resid: %d" 323 " b_flags: 0x%0x block: %lld target: %d\n", DEVNAME(sc), 324 ccb->ccb_buf.b_bcount, ccb->ccb_buf.b_resid, ccb->ccb_buf.b_flags, 325 ccb->ccb_buf.b_blkno, ccb->ccb_target); 326 327 s = splbio(); 328 329 if (ccb->ccb_buf.b_flags & B_ERROR) { 330 printf("%s: i/o error on block %lld target: %d b_error: %d\n", 331 DEVNAME(sc), ccb->ccb_buf.b_blkno, ccb->ccb_target, 332 ccb->ccb_buf.b_error); 333 DNPRINTF(SR_D_INTR, "%s: i/o error on block %lld target: %d\n", 334 DEVNAME(sc), ccb->ccb_buf.b_blkno, ccb->ccb_target); 335 wu->swu_ios_failed++; 336 ccb->ccb_state = SR_CCB_FAILED; 337 if (ccb->ccb_target != -1) 338 sd->sd_set_chunk_state(sd, ccb->ccb_target, 339 BIOC_SDOFFLINE); 340 else 341 panic("%s: invalid target on wu: %p", DEVNAME(sc), wu); 342 } else { 343 ccb->ccb_state = SR_CCB_OK; 344 wu->swu_ios_succeeded++; 345 } 346 wu->swu_ios_complete++; 347 348 DNPRINTF(SR_D_INTR, "%s: sr_intr: comp: %d count: %d failed: %d\n", 349 DEVNAME(sc), wu->swu_ios_complete, wu->swu_io_count, 350 wu->swu_ios_failed); 351 352 if (wu->swu_ios_complete >= wu->swu_io_count) { 353 if (wu->swu_ios_failed) 354 goto bad; 355 356 xs->error = XS_NOERROR; 357 xs->resid = 0; 358 xs->flags |= ITSDONE; 359 360 pend = 0; 361 TAILQ_FOREACH(wup, &sd->sd_wu_pendq, swu_link) { 362 if (wu == wup) { 363 /* wu on pendq, remove */ 364 TAILQ_REMOVE(&sd->sd_wu_pendq, wu, swu_link); 365 pend = 1; 366 367 if (wu->swu_collider) { 368 /* restart deferred wu */ 369 wu->swu_collider->swu_state = 370 SR_WU_INPROGRESS; 371 TAILQ_REMOVE(&sd->sd_wu_defq, 372 wu->swu_collider, swu_link); 373 sr_raid_startwu(wu->swu_collider); 374 } 375 break; 376 } 377 } 378 379 if (!pend) 380 printf("%s: wu: %p not on pending queue\n", 381 DEVNAME(sc), wu); 382 383 /* do not change the order of these 2 functions */ 384 sr_wu_put(wu); 385 sr_scsi_done(sd, xs); 386 387 if (sd->sd_sync && sd->sd_wu_pending == 0) 388 wakeup(sd); 389 } 390 391 splx(s); 392 return; 393 bad: 394 xs->error = XS_DRIVER_STUFFUP; 395 xs->flags |= ITSDONE; 396 sr_wu_put(wu); 397 sr_scsi_done(sd, xs); 398 splx(s); 399 } 400