xref: /openbsd/sys/dev/softraid_concat.c (revision cca36db2)
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