softraid_raid0.c revision 1.28
1/* $OpenBSD: softraid_raid0.c,v 1.28 2013/01/15 04:03:01 jsing 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	sr_raid0_create(struct sr_discipline *, struct bioc_createraid *,
48	    int, int64_t);
49int	sr_raid0_assemble(struct sr_discipline *, struct bioc_createraid *,
50	    int, void *);
51int	sr_raid0_alloc_resources(struct sr_discipline *);
52int	sr_raid0_free_resources(struct sr_discipline *);
53int	sr_raid0_rw(struct sr_workunit *);
54void	sr_raid0_intr(struct buf *);
55
56/* Discipline initialisation. */
57void
58sr_raid0_discipline_init(struct sr_discipline *sd)
59{
60
61	/* Fill out discipline members. */
62	sd->sd_type = SR_MD_RAID0;
63	sd->sd_capabilities = SR_CAP_SYSTEM_DISK | SR_CAP_AUTO_ASSEMBLE;
64	sd->sd_max_wu = SR_RAID0_NOWU;
65
66	/* Setup discipline specific function pointers. */
67	sd->sd_alloc_resources = sr_raid0_alloc_resources;
68	sd->sd_assemble = sr_raid0_assemble;
69	sd->sd_create = sr_raid0_create;
70	sd->sd_free_resources = sr_raid0_free_resources;
71	sd->sd_scsi_rw = sr_raid0_rw;
72	sd->sd_scsi_intr = sr_raid0_intr;
73}
74
75int
76sr_raid0_create(struct sr_discipline *sd, struct bioc_createraid *bc,
77    int no_chunk, int64_t coerced_size)
78{
79	if (no_chunk < 2) {
80		sr_error(sd->sd_sc, "RAID 0 requires two or more chunks");
81		return EINVAL;
82        }
83
84	/*
85	 * XXX add variable strip size later even though MAXPHYS is really
86	 * the clever value, users like to tinker with that type of stuff.
87	 */
88	strlcpy(sd->sd_name, "RAID 0", sizeof(sd->sd_name));
89	sd->sd_meta->ssdi.ssd_strip_size = MAXPHYS;
90	sd->sd_meta->ssdi.ssd_size = (coerced_size &
91	    ~((sd->sd_meta->ssdi.ssd_strip_size >> DEV_BSHIFT) - 1)) * no_chunk;
92
93	sd->sd_max_ccb_per_wu =
94	    (MAXPHYS / sd->sd_meta->ssdi.ssd_strip_size + 1) *
95	    SR_RAID0_NOWU * no_chunk;
96
97	return 0;
98}
99
100int
101sr_raid0_assemble(struct sr_discipline *sd, struct bioc_createraid *bc,
102    int no_chunks, void *data)
103{
104
105	sd->sd_max_ccb_per_wu =
106	    (MAXPHYS / sd->sd_meta->ssdi.ssd_strip_size + 1) *
107	    SR_RAID0_NOWU * sd->sd_meta->ssdi.ssd_chunk_no;
108
109	return 0;
110}
111
112int
113sr_raid0_alloc_resources(struct sr_discipline *sd)
114{
115	int			rv = EINVAL;
116
117	if (!sd)
118		return (rv);
119
120	DNPRINTF(SR_D_DIS, "%s: sr_raid0_alloc_resources\n",
121	    DEVNAME(sd->sd_sc));
122
123	if (sr_wu_alloc(sd))
124		goto bad;
125	if (sr_ccb_alloc(sd))
126		goto bad;
127
128	/* setup runtime values */
129	sd->mds.mdd_raid0.sr0_strip_bits =
130	    sr_validate_stripsize(sd->sd_meta->ssdi.ssd_strip_size);
131	if (sd->mds.mdd_raid0.sr0_strip_bits == -1)
132		goto bad;
133
134	rv = 0;
135bad:
136	return (rv);
137}
138
139int
140sr_raid0_free_resources(struct sr_discipline *sd)
141{
142	int			rv = EINVAL;
143
144	if (!sd)
145		return (rv);
146
147	DNPRINTF(SR_D_DIS, "%s: sr_raid0_free_resources\n",
148	    DEVNAME(sd->sd_sc));
149
150	sr_wu_free(sd);
151	sr_ccb_free(sd);
152
153	rv = 0;
154	return (rv);
155}
156
157int
158sr_raid0_rw(struct sr_workunit *wu)
159{
160	struct sr_discipline	*sd = wu->swu_dis;
161	struct scsi_xfer	*xs = wu->swu_xs;
162	struct sr_ccb		*ccb;
163	struct sr_chunk		*scp;
164	int			s;
165	daddr64_t		blk, lbaoffs, strip_no, chunk, stripoffs;
166	daddr64_t		strip_size, no_chunk, chunkoffs, physoffs;
167	daddr64_t		strip_bits, length, leftover;
168	u_int8_t		*data;
169
170	/* blk and scsi error will be handled by sr_validate_io */
171	if (sr_validate_io(wu, &blk, "sr_raid0_rw"))
172		goto bad;
173
174	strip_size = sd->sd_meta->ssdi.ssd_strip_size;
175	strip_bits = sd->mds.mdd_raid0.sr0_strip_bits;
176	no_chunk = sd->sd_meta->ssdi.ssd_chunk_no;
177
178	DNPRINTF(SR_D_DIS, "%s: %s: front end io: lba %lld size %d\n",
179	    DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname,
180	    blk, xs->datalen);
181
182	/* all offs are in bytes */
183	lbaoffs = blk << DEV_BSHIFT;
184	strip_no = lbaoffs >> strip_bits;
185	chunk = strip_no % no_chunk;
186	stripoffs = lbaoffs & (strip_size - 1);
187	chunkoffs = (strip_no / no_chunk) << strip_bits;
188	physoffs = chunkoffs + stripoffs +
189	    (sd->sd_meta->ssd_data_offset << DEV_BSHIFT);
190	length = MIN(xs->datalen, strip_size - stripoffs);
191	leftover = xs->datalen;
192	data = xs->data;
193	for (wu->swu_io_count = 1;; wu->swu_io_count++) {
194		/* make sure chunk is online */
195		scp = sd->sd_vol.sv_chunks[chunk];
196		if (scp->src_meta.scm_status != BIOC_SDONLINE) {
197			goto bad;
198		}
199
200		ccb = sr_ccb_get(sd);
201		if (!ccb) {
202			/* should never happen but handle more gracefully */
203			printf("%s: %s: too many ccbs queued\n",
204			    DEVNAME(sd->sd_sc),
205			    sd->sd_meta->ssd_devname);
206			goto bad;
207		}
208
209		DNPRINTF(SR_D_DIS, "%s: %s raid io: lbaoffs: %lld "
210		    "strip_no: %lld chunk: %lld stripoffs: %lld "
211		    "chunkoffs: %lld physoffs: %lld length: %lld "
212		    "leftover: %lld data: %p\n",
213		    DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, lbaoffs,
214		    strip_no, chunk, stripoffs, chunkoffs, physoffs, length,
215		    leftover, data);
216
217		ccb->ccb_buf.b_flags = B_CALL | B_PHYS;
218		ccb->ccb_buf.b_iodone = sr_raid0_intr;
219		ccb->ccb_buf.b_blkno = physoffs >> DEV_BSHIFT;
220		ccb->ccb_buf.b_bcount = length;
221		ccb->ccb_buf.b_bufsize = length;
222		ccb->ccb_buf.b_resid = length;
223		ccb->ccb_buf.b_data = data;
224		ccb->ccb_buf.b_error = 0;
225		ccb->ccb_buf.b_proc = curproc;
226		ccb->ccb_buf.b_bq = NULL;
227		ccb->ccb_wu = wu;
228		ccb->ccb_buf.b_flags |= xs->flags & SCSI_DATA_IN ?
229		    B_READ : B_WRITE;
230		ccb->ccb_target = chunk;
231		ccb->ccb_buf.b_dev = sd->sd_vol.sv_chunks[chunk]->src_dev_mm;
232		ccb->ccb_buf.b_vp = sd->sd_vol.sv_chunks[chunk]->src_vn;
233		if ((ccb->ccb_buf.b_flags & B_READ) == 0)
234			ccb->ccb_buf.b_vp->v_numoutput++;
235		LIST_INIT(&ccb->ccb_buf.b_dep);
236		TAILQ_INSERT_TAIL(&wu->swu_ccb, ccb, ccb_link);
237
238		DNPRINTF(SR_D_DIS, "%s: %s: sr_raid0: b_bcount: %d "
239		    "b_blkno: %lld b_flags 0x%0x b_data %p\n",
240		    DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname,
241		    ccb->ccb_buf.b_bcount, ccb->ccb_buf.b_blkno,
242		    ccb->ccb_buf.b_flags, ccb->ccb_buf.b_data);
243
244		leftover -= length;
245		if (leftover == 0)
246			break;
247
248		data += length;
249		if (++chunk > no_chunk - 1) {
250			chunk = 0;
251			physoffs += length;
252		} else if (wu->swu_io_count == 1)
253			physoffs -= stripoffs;
254		length = MIN(leftover,strip_size);
255	}
256
257	s = splbio();
258
259	if (sr_check_io_collision(wu))
260		goto queued;
261
262	sr_raid_startwu(wu);
263queued:
264	splx(s);
265	return (0);
266bad:
267	/* wu is unwound by sr_wu_put */
268	return (1);
269}
270
271void
272sr_raid0_intr(struct buf *bp)
273{
274	struct sr_ccb		*ccb = (struct sr_ccb *)bp;
275	struct sr_workunit	*wu = ccb->ccb_wu, *wup;
276	struct sr_discipline	*sd = wu->swu_dis;
277	struct scsi_xfer	*xs = wu->swu_xs;
278	struct sr_softc		*sc = sd->sd_sc;
279	int			s, pend;
280
281	DNPRINTF(SR_D_INTR, "%s: sr_intr bp %x xs %x\n",
282	    DEVNAME(sc), bp, xs);
283
284	DNPRINTF(SR_D_INTR, "%s: sr_intr: b_bcount: %d b_resid: %d"
285	    " b_flags: 0x%0x block: %lld target: %d\n", DEVNAME(sc),
286	    ccb->ccb_buf.b_bcount, ccb->ccb_buf.b_resid, ccb->ccb_buf.b_flags,
287	    ccb->ccb_buf.b_blkno, ccb->ccb_target);
288
289	s = splbio();
290
291	if (ccb->ccb_buf.b_flags & B_ERROR) {
292		printf("%s: i/o error on block %lld target: %d b_error: %d\n",
293		    DEVNAME(sc), ccb->ccb_buf.b_blkno, ccb->ccb_target,
294		    ccb->ccb_buf.b_error);
295		DNPRINTF(SR_D_INTR, "%s: i/o error on block %lld target: %d\n",
296		    DEVNAME(sc), ccb->ccb_buf.b_blkno, ccb->ccb_target);
297		wu->swu_ios_failed++;
298		ccb->ccb_state = SR_CCB_FAILED;
299		if (ccb->ccb_target != -1)
300			sd->sd_set_chunk_state(sd, ccb->ccb_target,
301			    BIOC_SDOFFLINE);
302		else
303			panic("%s: invalid target on wu: %p", DEVNAME(sc), wu);
304	} else {
305		ccb->ccb_state = SR_CCB_OK;
306		wu->swu_ios_succeeded++;
307	}
308	wu->swu_ios_complete++;
309
310	DNPRINTF(SR_D_INTR, "%s: sr_intr: comp: %d count: %d failed: %d\n",
311	    DEVNAME(sc), wu->swu_ios_complete, wu->swu_io_count,
312	    wu->swu_ios_failed);
313
314	if (wu->swu_ios_complete >= wu->swu_io_count) {
315		if (wu->swu_ios_failed)
316			goto bad;
317
318		xs->error = XS_NOERROR;
319		xs->resid = 0;
320
321		pend = 0;
322		TAILQ_FOREACH(wup, &sd->sd_wu_pendq, swu_link) {
323			if (wu == wup) {
324				/* wu on pendq, remove */
325				TAILQ_REMOVE(&sd->sd_wu_pendq, wu, swu_link);
326				pend = 1;
327
328				if (wu->swu_collider) {
329					/* restart deferred wu */
330					wu->swu_collider->swu_state =
331					    SR_WU_INPROGRESS;
332					TAILQ_REMOVE(&sd->sd_wu_defq,
333					    wu->swu_collider, swu_link);
334					sr_raid_startwu(wu->swu_collider);
335				}
336				break;
337			}
338		}
339
340		if (!pend)
341			printf("%s: wu: %p not on pending queue\n",
342			    DEVNAME(sc), wu);
343
344		sr_scsi_done(sd, xs);
345
346		if (sd->sd_sync && sd->sd_wu_pending == 0)
347			wakeup(sd);
348	}
349
350	splx(s);
351	return;
352bad:
353	xs->error = XS_DRIVER_STUFFUP;
354	sr_scsi_done(sd, xs);
355	splx(s);
356}
357