softraid_raid0.c revision 1.3
1/* $OpenBSD: softraid_raid0.c,v 1.3 2008/01/24 14:01:06 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 */ 47int 48sr_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_alloc_wu(sd)) 59 goto bad; 60 if (sr_alloc_ccb(sd)) 61 goto bad; 62 63 /* setup runtime values */ 64 sd->mds.mdd_raid0.sr0_strip_bits = 65 sr_validate_stripsize(sd->sd_vol.sv_meta.svm_strip_size); 66 if (sd->mds.mdd_raid0.sr0_strip_bits == -1) 67 goto bad; 68 69 rv = 0; 70bad: 71 return (rv); 72} 73 74int 75sr_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_free_wu(sd); 86 sr_free_ccb(sd); 87 88 if (sd->sd_meta) 89 free(sd->sd_meta, M_DEVBUF); 90 91 rv = 0; 92 return (rv); 93} 94 95int 96sr_raid0_rw(struct sr_workunit *wu) 97{ 98 struct sr_discipline *sd = wu->swu_dis; 99 struct scsi_xfer *xs = wu->swu_xs; 100 struct sr_workunit *wup; 101 struct sr_ccb *ccb; 102 struct sr_chunk *scp; 103 int s; 104 daddr64_t blk, lbaoffs, strip_no, chunk, stripoffs; 105 daddr64_t strip_size, no_chunk, chunkoffs, physoffs; 106 daddr64_t strip_bits, length, leftover; 107 u_int8_t *data; 108 109 DNPRINTF(SR_D_DIS, "%s: sr_raid0_rw 0x%02x\n", DEVNAME(sd->sd_sc), 110 xs->cmd->opcode); 111 112 if (sd->sd_vol.sv_meta.svm_status == BIOC_SVOFFLINE) { 113 DNPRINTF(SR_D_DIS, "%s: sr_raid0_rw device offline\n", 114 DEVNAME(sd->sd_sc)); 115 goto bad; 116 } 117 118 if (xs->datalen == 0) { 119 printf("%s: %s: illegal block count\n", 120 DEVNAME(sd->sd_sc), sd->sd_vol.sv_meta.svm_devname); 121 goto bad; 122 } 123 124 if (xs->cmdlen == 10) 125 blk = _4btol(((struct scsi_rw_big *)xs->cmd)->addr); 126 else if (xs->cmdlen == 6) 127 blk = _3btol(((struct scsi_rw *)xs->cmd)->addr); 128 else { 129 printf("%s: %s: illegal cmdlen\n", DEVNAME(sd->sd_sc), 130 sd->sd_vol.sv_meta.svm_devname); 131 goto bad; 132 } 133 134 wu->swu_blk_start = blk; 135 wu->swu_blk_end = blk + (xs->datalen >> DEV_BSHIFT) - 1; 136 137 if (wu->swu_blk_end > sd->sd_vol.sv_meta.svm_size) { 138 DNPRINTF(SR_D_DIS, "%s: sr_raid0_rw out of bounds start: %lld " 139 "end: %lld length: %d\n", wu->swu_blk_start, 140 wu->swu_blk_end, xs->datalen); 141 142 sd->sd_scsi_sense.error_code = SSD_ERRCODE_CURRENT | 143 SSD_ERRCODE_VALID; 144 sd->sd_scsi_sense.flags = SKEY_ILLEGAL_REQUEST; 145 sd->sd_scsi_sense.add_sense_code = 0x21; 146 sd->sd_scsi_sense.add_sense_code_qual = 0x00; 147 sd->sd_scsi_sense.extra_len = 4; 148 goto bad; 149 } 150 151 strip_size = sd->sd_vol.sv_meta.svm_strip_size; 152 strip_bits = sd->mds.mdd_raid0.sr0_strip_bits; 153 no_chunk = sd->sd_vol.sv_meta.svm_no_chunk; 154 155 DNPRINTF(SR_D_DIS, "%s: %s: front end io: lba %lld size %d\n", 156 DEVNAME(sc), sd->sd_vol.sv_meta.svm_devname, blk, xs->datalen); 157 158 /* all offs are in bytes */ 159 lbaoffs = blk << DEV_BSHIFT; 160 strip_no = lbaoffs >> strip_bits; 161 chunk = strip_no % no_chunk; 162 stripoffs = lbaoffs & (strip_size - 1); 163 chunkoffs = (strip_no / no_chunk) << strip_bits; 164 physoffs = chunkoffs + stripoffs + 165 ((SR_META_OFFSET + SR_META_SIZE) << DEV_BSHIFT); 166 length = MIN(xs->datalen, strip_size - stripoffs); 167 leftover = xs->datalen; 168 data = xs->data; 169 for (wu->swu_io_count = 1;; wu->swu_io_count++) { 170 /* make sure chunk is online */ 171 scp = sd->sd_vol.sv_chunks[chunk]; 172 if (scp->src_meta.scm_status != BIOC_SDONLINE) { 173 sr_put_ccb(ccb); 174 goto bad; 175 } 176 177 ccb = sr_get_ccb(sd); 178 if (!ccb) { 179 /* should never happen but handle more gracefully */ 180 printf("%s: %s: too many ccbs queued\n", 181 DEVNAME(sd->sd_sc), 182 sd->sd_vol.sv_meta.svm_devname); 183 goto bad; 184 } 185 186 DNPRINTF(SR_D_DIS, "%s: %s raid io: lbaoffs: %lld " 187 "strip_no: %lld chunk: %lld stripoffs: %lld " 188 "chunkoffs: %lld physoffs: %lld length: %lld " 189 "leftover: %lld data: %p\n", 190 DEVNAME(sc), sd->sd_vol.sv_meta.svm_devname, lbaoffs, 191 strip_no, chunk, stripoffs, chunkoffs, physoffs, length, 192 leftover, data); 193 194 ccb->ccb_buf.b_flags = B_CALL; 195 ccb->ccb_buf.b_iodone = sr_raid0_intr; 196 ccb->ccb_buf.b_blkno = physoffs >> DEV_BSHIFT; 197 ccb->ccb_buf.b_bcount = length; 198 ccb->ccb_buf.b_bufsize = length; 199 ccb->ccb_buf.b_resid = length; 200 ccb->ccb_buf.b_data = data; 201 ccb->ccb_buf.b_error = 0; 202 ccb->ccb_buf.b_proc = curproc; 203 ccb->ccb_wu = wu; 204 ccb->ccb_buf.b_flags |= xs->flags & SCSI_DATA_IN ? 205 B_READ : B_WRITE; 206 ccb->ccb_target = chunk; 207 ccb->ccb_buf.b_dev = sd->sd_vol.sv_chunks[chunk]->src_dev_mm; 208 ccb->ccb_buf.b_vp = NULL; 209 LIST_INIT(&ccb->ccb_buf.b_dep); 210 TAILQ_INSERT_TAIL(&wu->swu_ccb, ccb, ccb_link); 211 212 DNPRINTF(SR_D_DIS, "%s: %s: sr_raid0: b_bcount: %d " 213 "b_blkno: %lld b_flags 0x%0x b_data %p\n", 214 DEVNAME(sd->sd_sc), sd->sd_vol.sv_meta.svm_devname, 215 ccb->ccb_buf.b_bcount, ccb->ccb_buf.b_blkno, 216 ccb->ccb_buf.b_flags, ccb->ccb_buf.b_data); 217 218 leftover -= length; 219 if (leftover == 0) 220 break; 221 222 data += length; 223 if (++chunk > no_chunk - 1) { 224 chunk = 0; 225 physoffs += length; 226 } else if (wu->swu_io_count == 1) 227 physoffs -= stripoffs; 228 length = MIN(leftover,strip_size); 229 } 230 231 s = splbio(); 232 233 /* walk queue backwards and fill in collider if we have one */ 234 TAILQ_FOREACH_REVERSE(wup, &sd->sd_wu_pendq, sr_wu_list, swu_link) { 235 if (wu->swu_blk_end < wup->swu_blk_start || 236 wup->swu_blk_end < wu->swu_blk_start) 237 continue; 238 239 /* we have an LBA collision, defer wu */ 240 wu->swu_state = SR_WU_DEFERRED; 241 if (wup->swu_collider) 242 /* wu is on deferred queue, append to last wu */ 243 while (wup->swu_collider) 244 wup = wup->swu_collider; 245 246 wup->swu_collider = wu; 247 TAILQ_INSERT_TAIL(&sd->sd_wu_defq, wu, swu_link); 248 sd->sd_wu_collisions++; 249 goto queued; 250 } 251 sr_raid_startwu(wu); 252queued: 253 splx(s); 254 return (0); 255bad: 256 /* wu is unwound by sr_put_wu */ 257 return (1); 258} 259 260void 261sr_raid0_intr(struct buf *bp) 262{ 263 struct sr_ccb *ccb = (struct sr_ccb *)bp; 264 struct sr_workunit *wu = ccb->ccb_wu, *wup; 265 struct sr_discipline *sd = wu->swu_dis; 266 struct scsi_xfer *xs = wu->swu_xs; 267 struct sr_softc *sc = sd->sd_sc; 268 int s, pend; 269 270 DNPRINTF(SR_D_INTR, "%s: sr_intr bp %x xs %x\n", 271 DEVNAME(sc), bp, xs); 272 273 DNPRINTF(SR_D_INTR, "%s: sr_intr: b_bcount: %d b_resid: %d" 274 " b_flags: 0x%0x block: %lld target: %d\n", DEVNAME(sc), 275 ccb->ccb_buf.b_bcount, ccb->ccb_buf.b_resid, ccb->ccb_buf.b_flags, 276 ccb->ccb_buf.b_blkno, ccb->ccb_target); 277 278 s = splbio(); 279 280 if (ccb->ccb_buf.b_flags & B_ERROR) { 281 printf("%s: i/o error on block %lld target: %d b_error: %d\n", 282 DEVNAME(sc), ccb->ccb_buf.b_blkno, ccb->ccb_target, 283 ccb->ccb_buf.b_error); 284 DNPRINTF(SR_D_INTR, "%s: i/o error on block %lld target: %d\n", 285 DEVNAME(sc), ccb->ccb_buf.b_blkno, ccb->ccb_target); 286 wu->swu_ios_failed++; 287 ccb->ccb_state = SR_CCB_FAILED; 288 if (ccb->ccb_target != -1) 289 sd->sd_set_chunk_state(sd, ccb->ccb_target, 290 BIOC_SDOFFLINE); 291 else 292 panic("%s: invalid target on wu: %p", DEVNAME(sc), wu); 293 } else { 294 ccb->ccb_state = SR_CCB_OK; 295 wu->swu_ios_succeeded++; 296 } 297 wu->swu_ios_complete++; 298 299 DNPRINTF(SR_D_INTR, "%s: sr_intr: comp: %d count: %d failed: %d\n", 300 DEVNAME(sc), wu->swu_ios_complete, wu->swu_io_count, 301 wu->swu_ios_failed); 302 303 if (wu->swu_ios_complete >= wu->swu_io_count) { 304 if (wu->swu_ios_failed) 305 goto bad; 306 307 xs->error = XS_NOERROR; 308 xs->resid = 0; 309 xs->flags |= ITSDONE; 310 311 pend = 0; 312 TAILQ_FOREACH(wup, &sd->sd_wu_pendq, swu_link) { 313 if (wu == wup) { 314 /* wu on pendq, remove */ 315 TAILQ_REMOVE(&sd->sd_wu_pendq, wu, swu_link); 316 pend = 1; 317 318 if (wu->swu_collider) { 319 /* restart deferred wu */ 320 wu->swu_collider->swu_state = 321 SR_WU_INPROGRESS; 322 TAILQ_REMOVE(&sd->sd_wu_defq, 323 wu->swu_collider, swu_link); 324 sr_raid_startwu(wu->swu_collider); 325 } 326 break; 327 } 328 } 329 330 if (!pend) 331 printf("%s: wu: %p not on pending queue\n", 332 DEVNAME(sc), wu); 333 334 /* do not change the order of these 2 functions */ 335 sr_put_wu(wu); 336 scsi_done(xs); 337 338 if (sd->sd_sync && sd->sd_wu_pending == 0) 339 wakeup(sd); 340 } 341 342 splx(s); 343 return; 344bad: 345 xs->error = XS_DRIVER_STUFFUP; 346 xs->flags |= ITSDONE; 347 sr_put_wu(wu); 348 scsi_done(xs); 349 splx(s); 350} 351