1 /* $OpenBSD: softraid_concat.c,v 1.2 2012/01/22 11:13:32 jsing Exp $ */ 2 /* 3 * Copyright (c) 2008 Marco Peereboom <marco@peereboom.us> 4 * Copyright (c) 2011 Joel Sing <jsing@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include "bio.h" 20 21 #include <sys/param.h> 22 #include <sys/systm.h> 23 #include <sys/device.h> 24 #include <sys/buf.h> 25 #include <sys/queue.h> 26 #include <sys/sensors.h> 27 28 #include <scsi/scsi_all.h> 29 #include <scsi/scsiconf.h> 30 #include <scsi/scsi_disk.h> 31 32 #include <dev/softraidvar.h> 33 34 /* CONCAT functions. */ 35 int sr_concat_create(struct sr_discipline *, struct bioc_createraid *, 36 int, int64_t); 37 int sr_concat_assemble(struct sr_discipline *, struct bioc_createraid *, 38 int); 39 int sr_concat_alloc_resources(struct sr_discipline *); 40 int sr_concat_free_resources(struct sr_discipline *); 41 int sr_concat_rw(struct sr_workunit *); 42 void sr_concat_intr(struct buf *); 43 44 /* Discipline initialisation. */ 45 void 46 sr_concat_discipline_init(struct sr_discipline *sd) 47 { 48 49 /* Fill out discipline members. */ 50 sd->sd_type = SR_MD_CONCAT; 51 sd->sd_capabilities = SR_CAP_SYSTEM_DISK | SR_CAP_AUTO_ASSEMBLE | 52 SR_CAP_NON_COERCED; 53 sd->sd_max_wu = SR_CONCAT_NOWU; 54 55 /* Setup discipline specific function pointers. */ 56 sd->sd_alloc_resources = sr_concat_alloc_resources; 57 sd->sd_assemble = sr_concat_assemble; 58 sd->sd_create = sr_concat_create; 59 sd->sd_free_resources = sr_concat_free_resources; 60 sd->sd_scsi_rw = sr_concat_rw; 61 } 62 63 int 64 sr_concat_create(struct sr_discipline *sd, struct bioc_createraid *bc, 65 int no_chunk, int64_t coerced_size) 66 { 67 int i; 68 69 if (no_chunk < 2) { 70 sr_error(sd->sd_sc, "CONCAT requires two or more chunks"); 71 return EINVAL; 72 } 73 74 strlcpy(sd->sd_name, "CONCAT", sizeof(sd->sd_name)); 75 76 sd->sd_meta->ssdi.ssd_size = 0; 77 for (i = 0; i < no_chunk; i++) 78 sd->sd_meta->ssdi.ssd_size += 79 sd->sd_vol.sv_chunks[i]->src_size; 80 sd->sd_max_ccb_per_wu = SR_CONCAT_NOWU * no_chunk; 81 82 return 0; 83 } 84 85 int 86 sr_concat_assemble(struct sr_discipline *sd, struct bioc_createraid *bc, 87 int no_chunk) 88 { 89 90 sd->sd_max_ccb_per_wu = SR_CONCAT_NOWU * no_chunk; 91 92 return 0; 93 } 94 95 int 96 sr_concat_alloc_resources(struct sr_discipline *sd) 97 { 98 int rv = EINVAL; 99 100 if (!sd) 101 return (rv); 102 103 DNPRINTF(SR_D_DIS, "%s: sr_concat_alloc_resources\n", 104 DEVNAME(sd->sd_sc)); 105 106 if (sr_wu_alloc(sd)) 107 goto bad; 108 if (sr_ccb_alloc(sd)) 109 goto bad; 110 111 rv = 0; 112 bad: 113 return (rv); 114 } 115 116 int 117 sr_concat_free_resources(struct sr_discipline *sd) 118 { 119 int rv = EINVAL; 120 121 if (!sd) 122 return (rv); 123 124 DNPRINTF(SR_D_DIS, "%s: sr_concat_free_resources\n", 125 DEVNAME(sd->sd_sc)); 126 127 sr_wu_free(sd); 128 sr_ccb_free(sd); 129 130 rv = 0; 131 return (rv); 132 } 133 134 int 135 sr_concat_rw(struct sr_workunit *wu) 136 { 137 struct sr_discipline *sd = wu->swu_dis; 138 struct scsi_xfer *xs = wu->swu_xs; 139 struct sr_ccb *ccb; 140 struct sr_chunk *scp; 141 int s; 142 daddr64_t blk, lbaoffs, chunk, chunksize; 143 daddr64_t no_chunk, chunkend, physoffs; 144 daddr64_t length, leftover; 145 u_int8_t *data; 146 147 /* blk and scsi error will be handled by sr_validate_io */ 148 if (sr_validate_io(wu, &blk, "sr_concat_rw")) 149 goto bad; 150 151 no_chunk = sd->sd_meta->ssdi.ssd_chunk_no; 152 153 DNPRINTF(SR_D_DIS, "%s: %s: front end io: lba %lld size %d\n", 154 DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, 155 blk, xs->datalen); 156 157 /* All offsets are in bytes. */ 158 lbaoffs = blk << DEV_BSHIFT; 159 leftover = xs->datalen; 160 data = xs->data; 161 for (wu->swu_io_count = 1;; wu->swu_io_count++) { 162 163 chunkend = 0; 164 physoffs = lbaoffs; 165 for (chunk = 0; chunk < no_chunk; chunk++) { 166 chunksize = sd->sd_vol.sv_chunks[chunk]->src_size << 167 DEV_BSHIFT; 168 chunkend += chunksize; 169 if (lbaoffs < chunkend) 170 break; 171 physoffs -= chunksize; 172 } 173 if (lbaoffs > chunkend) 174 goto bad; 175 176 length = MIN(MIN(leftover, chunkend - lbaoffs), MAXPHYS); 177 physoffs += sd->sd_meta->ssd_data_offset << DEV_BSHIFT; 178 179 /* make sure chunk is online */ 180 scp = sd->sd_vol.sv_chunks[chunk]; 181 if (scp->src_meta.scm_status != BIOC_SDONLINE) { 182 goto bad; 183 } 184 185 ccb = sr_ccb_get(sd); 186 if (!ccb) { 187 /* should never happen but handle more gracefully */ 188 printf("%s: %s: too many ccbs queued\n", 189 DEVNAME(sd->sd_sc), 190 sd->sd_meta->ssd_devname); 191 goto bad; 192 } 193 194 DNPRINTF(SR_D_DIS, "%s: %s concat io: lbaoffs: %lld " 195 "chunk: %lld chunkend: %lld physoffs: %lld length: %lld " 196 "leftover: %lld data: %p\n", 197 DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, lbaoffs, 198 chunk, chunkend, physoffs, length, leftover, data); 199 200 ccb->ccb_buf.b_flags = B_CALL | B_PHYS; 201 ccb->ccb_buf.b_iodone = sr_concat_intr; 202 ccb->ccb_buf.b_blkno = physoffs >> DEV_BSHIFT; 203 ccb->ccb_buf.b_bcount = length; 204 ccb->ccb_buf.b_bufsize = length; 205 ccb->ccb_buf.b_resid = length; 206 ccb->ccb_buf.b_data = data; 207 ccb->ccb_buf.b_error = 0; 208 ccb->ccb_buf.b_proc = curproc; 209 ccb->ccb_buf.b_bq = NULL; 210 ccb->ccb_wu = wu; 211 ccb->ccb_buf.b_flags |= xs->flags & SCSI_DATA_IN ? 212 B_READ : B_WRITE; 213 ccb->ccb_target = chunk; 214 ccb->ccb_buf.b_dev = sd->sd_vol.sv_chunks[chunk]->src_dev_mm; 215 ccb->ccb_buf.b_vp = sd->sd_vol.sv_chunks[chunk]->src_vn; 216 if ((ccb->ccb_buf.b_flags & B_READ) == 0) 217 ccb->ccb_buf.b_vp->v_numoutput++; 218 LIST_INIT(&ccb->ccb_buf.b_dep); 219 TAILQ_INSERT_TAIL(&wu->swu_ccb, ccb, ccb_link); 220 221 DNPRINTF(SR_D_DIS, "%s: %s: sr_concat: b_bcount: %d " 222 "b_blkno: %lld b_flags 0x%0x b_data %p\n", 223 DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, 224 ccb->ccb_buf.b_bcount, ccb->ccb_buf.b_blkno, 225 ccb->ccb_buf.b_flags, ccb->ccb_buf.b_data); 226 227 leftover -= length; 228 if (leftover == 0) 229 break; 230 data += length; 231 lbaoffs += length; 232 } 233 234 s = splbio(); 235 236 if (!sr_check_io_collision(wu)) 237 sr_raid_startwu(wu); 238 239 splx(s); 240 return (0); 241 bad: 242 /* wu is unwound by sr_wu_put */ 243 return (1); 244 } 245 246 void 247 sr_concat_intr(struct buf *bp) 248 { 249 struct sr_ccb *ccb = (struct sr_ccb *)bp; 250 struct sr_workunit *wu = ccb->ccb_wu, *wup; 251 struct sr_discipline *sd = wu->swu_dis; 252 struct scsi_xfer *xs = wu->swu_xs; 253 struct sr_softc *sc = sd->sd_sc; 254 int s, pend; 255 256 DNPRINTF(SR_D_INTR, "%s: sr_intr bp %x xs %x\n", 257 DEVNAME(sc), bp, xs); 258 259 DNPRINTF(SR_D_INTR, "%s: sr_intr: b_bcount: %d b_resid: %d" 260 " b_flags: 0x%0x block: %lld target: %d\n", DEVNAME(sc), 261 ccb->ccb_buf.b_bcount, ccb->ccb_buf.b_resid, ccb->ccb_buf.b_flags, 262 ccb->ccb_buf.b_blkno, ccb->ccb_target); 263 264 s = splbio(); 265 266 if (ccb->ccb_buf.b_flags & B_ERROR) { 267 printf("%s: i/o error on block %lld target: %d b_error: %d\n", 268 DEVNAME(sc), ccb->ccb_buf.b_blkno, ccb->ccb_target, 269 ccb->ccb_buf.b_error); 270 DNPRINTF(SR_D_INTR, "%s: i/o error on block %lld target: %d\n", 271 DEVNAME(sc), ccb->ccb_buf.b_blkno, ccb->ccb_target); 272 wu->swu_ios_failed++; 273 ccb->ccb_state = SR_CCB_FAILED; 274 if (ccb->ccb_target != -1) 275 sd->sd_set_chunk_state(sd, ccb->ccb_target, 276 BIOC_SDOFFLINE); 277 else 278 panic("%s: invalid target on wu: %p", DEVNAME(sc), wu); 279 } else { 280 ccb->ccb_state = SR_CCB_OK; 281 wu->swu_ios_succeeded++; 282 } 283 wu->swu_ios_complete++; 284 285 DNPRINTF(SR_D_INTR, "%s: sr_intr: comp: %d count: %d failed: %d\n", 286 DEVNAME(sc), wu->swu_ios_complete, wu->swu_io_count, 287 wu->swu_ios_failed); 288 289 if (wu->swu_ios_complete >= wu->swu_io_count) { 290 if (wu->swu_ios_failed) 291 goto bad; 292 293 xs->error = XS_NOERROR; 294 xs->resid = 0; 295 296 pend = 0; 297 TAILQ_FOREACH(wup, &sd->sd_wu_pendq, swu_link) { 298 if (wu == wup) { 299 /* wu on pendq, remove */ 300 TAILQ_REMOVE(&sd->sd_wu_pendq, wu, swu_link); 301 pend = 1; 302 303 if (wu->swu_collider) { 304 /* restart deferred wu */ 305 wu->swu_collider->swu_state = 306 SR_WU_INPROGRESS; 307 TAILQ_REMOVE(&sd->sd_wu_defq, 308 wu->swu_collider, swu_link); 309 sr_raid_startwu(wu->swu_collider); 310 } 311 break; 312 } 313 } 314 315 if (!pend) 316 printf("%s: wu: %p not on pending queue\n", 317 DEVNAME(sc), wu); 318 319 sr_scsi_done(sd, xs); 320 321 if (sd->sd_sync && sd->sd_wu_pending == 0) 322 wakeup(sd); 323 } 324 325 splx(s); 326 return; 327 bad: 328 xs->error = XS_DRIVER_STUFFUP; 329 sr_scsi_done(sd, xs); 330 splx(s); 331 } 332