1 /* $OpenBSD: softraid_raid0.c,v 1.9 2008/07/19 22:41:58 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) 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 sr_ccb_put(ccb); 239 goto bad; 240 } 241 242 ccb = sr_ccb_get(sd); 243 if (!ccb) { 244 /* should never happen but handle more gracefully */ 245 printf("%s: %s: too many ccbs queued\n", 246 DEVNAME(sd->sd_sc), 247 sd->sd_meta->ssd_devname); 248 goto bad; 249 } 250 251 DNPRINTF(SR_D_DIS, "%s: %s raid io: lbaoffs: %lld " 252 "strip_no: %lld chunk: %lld stripoffs: %lld " 253 "chunkoffs: %lld physoffs: %lld length: %lld " 254 "leftover: %lld data: %p\n", 255 DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, lbaoffs, 256 strip_no, chunk, stripoffs, chunkoffs, physoffs, length, 257 leftover, data); 258 259 ccb->ccb_buf.b_flags = B_CALL; 260 ccb->ccb_buf.b_iodone = sr_raid0_intr; 261 ccb->ccb_buf.b_blkno = physoffs >> DEV_BSHIFT; 262 ccb->ccb_buf.b_bcount = length; 263 ccb->ccb_buf.b_bufsize = length; 264 ccb->ccb_buf.b_resid = length; 265 ccb->ccb_buf.b_data = data; 266 ccb->ccb_buf.b_error = 0; 267 ccb->ccb_buf.b_proc = curproc; 268 ccb->ccb_wu = wu; 269 ccb->ccb_buf.b_flags |= xs->flags & SCSI_DATA_IN ? 270 B_READ : B_WRITE; 271 ccb->ccb_target = chunk; 272 ccb->ccb_buf.b_dev = sd->sd_vol.sv_chunks[chunk]->src_dev_mm; 273 ccb->ccb_buf.b_vp = NULL; 274 LIST_INIT(&ccb->ccb_buf.b_dep); 275 TAILQ_INSERT_TAIL(&wu->swu_ccb, ccb, ccb_link); 276 277 DNPRINTF(SR_D_DIS, "%s: %s: sr_raid0: b_bcount: %d " 278 "b_blkno: %lld b_flags 0x%0x b_data %p\n", 279 DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, 280 ccb->ccb_buf.b_bcount, ccb->ccb_buf.b_blkno, 281 ccb->ccb_buf.b_flags, ccb->ccb_buf.b_data); 282 283 leftover -= length; 284 if (leftover == 0) 285 break; 286 287 data += length; 288 if (++chunk > no_chunk - 1) { 289 chunk = 0; 290 physoffs += length; 291 } else if (wu->swu_io_count == 1) 292 physoffs -= stripoffs; 293 length = MIN(leftover,strip_size); 294 } 295 296 s = splbio(); 297 298 if (sr_check_io_collision(wu)) 299 goto queued; 300 301 sr_raid_startwu(wu); 302 queued: 303 splx(s); 304 return (0); 305 bad: 306 /* wu is unwound by sr_wu_put */ 307 return (1); 308 } 309 310 void 311 sr_raid0_intr(struct buf *bp) 312 { 313 struct sr_ccb *ccb = (struct sr_ccb *)bp; 314 struct sr_workunit *wu = ccb->ccb_wu, *wup; 315 struct sr_discipline *sd = wu->swu_dis; 316 struct scsi_xfer *xs = wu->swu_xs; 317 struct sr_softc *sc = sd->sd_sc; 318 int s, pend; 319 320 DNPRINTF(SR_D_INTR, "%s: sr_intr bp %x xs %x\n", 321 DEVNAME(sc), bp, xs); 322 323 DNPRINTF(SR_D_INTR, "%s: sr_intr: b_bcount: %d b_resid: %d" 324 " b_flags: 0x%0x block: %lld target: %d\n", DEVNAME(sc), 325 ccb->ccb_buf.b_bcount, ccb->ccb_buf.b_resid, ccb->ccb_buf.b_flags, 326 ccb->ccb_buf.b_blkno, ccb->ccb_target); 327 328 s = splbio(); 329 330 if (ccb->ccb_buf.b_flags & B_ERROR) { 331 printf("%s: i/o error on block %lld target: %d b_error: %d\n", 332 DEVNAME(sc), ccb->ccb_buf.b_blkno, ccb->ccb_target, 333 ccb->ccb_buf.b_error); 334 DNPRINTF(SR_D_INTR, "%s: i/o error on block %lld target: %d\n", 335 DEVNAME(sc), ccb->ccb_buf.b_blkno, ccb->ccb_target); 336 wu->swu_ios_failed++; 337 ccb->ccb_state = SR_CCB_FAILED; 338 if (ccb->ccb_target != -1) 339 sd->sd_set_chunk_state(sd, ccb->ccb_target, 340 BIOC_SDOFFLINE); 341 else 342 panic("%s: invalid target on wu: %p", DEVNAME(sc), wu); 343 } else { 344 ccb->ccb_state = SR_CCB_OK; 345 wu->swu_ios_succeeded++; 346 } 347 wu->swu_ios_complete++; 348 349 DNPRINTF(SR_D_INTR, "%s: sr_intr: comp: %d count: %d failed: %d\n", 350 DEVNAME(sc), wu->swu_ios_complete, wu->swu_io_count, 351 wu->swu_ios_failed); 352 353 if (wu->swu_ios_complete >= wu->swu_io_count) { 354 if (wu->swu_ios_failed) 355 goto bad; 356 357 xs->error = XS_NOERROR; 358 xs->resid = 0; 359 xs->flags |= ITSDONE; 360 361 pend = 0; 362 TAILQ_FOREACH(wup, &sd->sd_wu_pendq, swu_link) { 363 if (wu == wup) { 364 /* wu on pendq, remove */ 365 TAILQ_REMOVE(&sd->sd_wu_pendq, wu, swu_link); 366 pend = 1; 367 368 if (wu->swu_collider) { 369 /* restart deferred wu */ 370 wu->swu_collider->swu_state = 371 SR_WU_INPROGRESS; 372 TAILQ_REMOVE(&sd->sd_wu_defq, 373 wu->swu_collider, swu_link); 374 sr_raid_startwu(wu->swu_collider); 375 } 376 break; 377 } 378 } 379 380 if (!pend) 381 printf("%s: wu: %p not on pending queue\n", 382 DEVNAME(sc), wu); 383 384 /* do not change the order of these 2 functions */ 385 sr_wu_put(wu); 386 scsi_done(xs); 387 388 if (sd->sd_sync && sd->sd_wu_pending == 0) 389 wakeup(sd); 390 } 391 392 splx(s); 393 return; 394 bad: 395 xs->error = XS_DRIVER_STUFFUP; 396 xs->flags |= ITSDONE; 397 sr_wu_put(wu); 398 scsi_done(xs); 399 splx(s); 400 } 401