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