xref: /openbsd-src/sys/dev/softraid_raid6.c (revision 0f9e9ec23bb2b65cc62a3d17df12827a45dae80c)
1*0f9e9ec2Sjsg /* $OpenBSD: softraid_raid6.c,v 1.73 2024/05/13 01:15:50 jsg Exp $ */
2f3b9c4eeSjordan /*
3f3b9c4eeSjordan  * Copyright (c) 2009 Marco Peereboom <marco@peereboom.us>
4f3b9c4eeSjordan  * Copyright (c) 2009 Jordan Hargrave <jordan@openbsd.org>
5f3b9c4eeSjordan  *
6f3b9c4eeSjordan  * Permission to use, copy, modify, and distribute this software for any
7f3b9c4eeSjordan  * purpose with or without fee is hereby granted, provided that the above
8f3b9c4eeSjordan  * copyright notice and this permission notice appear in all copies.
9f3b9c4eeSjordan  *
10f3b9c4eeSjordan  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11f3b9c4eeSjordan  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12f3b9c4eeSjordan  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13f3b9c4eeSjordan  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14f3b9c4eeSjordan  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15f3b9c4eeSjordan  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16f3b9c4eeSjordan  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17f3b9c4eeSjordan  */
18f3b9c4eeSjordan 
19f3b9c4eeSjordan #include "bio.h"
20f3b9c4eeSjordan 
21f3b9c4eeSjordan #include <sys/param.h>
22f3b9c4eeSjordan #include <sys/systm.h>
23f3b9c4eeSjordan #include <sys/buf.h>
24f3b9c4eeSjordan #include <sys/device.h>
25f3b9c4eeSjordan #include <sys/ioctl.h>
26f3b9c4eeSjordan #include <sys/malloc.h>
27f3b9c4eeSjordan #include <sys/kernel.h>
28f3b9c4eeSjordan #include <sys/disk.h>
29f3b9c4eeSjordan #include <sys/rwlock.h>
30f3b9c4eeSjordan #include <sys/queue.h>
31f3b9c4eeSjordan #include <sys/fcntl.h>
32f3b9c4eeSjordan #include <sys/mount.h>
33f3b9c4eeSjordan #include <sys/sensors.h>
34f3b9c4eeSjordan #include <sys/stat.h>
35e328a933Sjsing #include <sys/task.h>
36f3b9c4eeSjordan #include <sys/conf.h>
37f3b9c4eeSjordan #include <sys/uio.h>
38f3b9c4eeSjordan 
39f3b9c4eeSjordan #include <scsi/scsi_all.h>
40f3b9c4eeSjordan #include <scsi/scsiconf.h>
41f3b9c4eeSjordan #include <scsi/scsi_disk.h>
42f3b9c4eeSjordan 
43f3b9c4eeSjordan #include <dev/softraidvar.h>
44f3b9c4eeSjordan 
45b0a939a9Sjordan uint8_t *gf_map[256];
46c8cd47bbSjordan uint8_t	gf_pow[768];
47c8cd47bbSjordan int	gf_log[256];
48f3b9c4eeSjordan 
49f3b9c4eeSjordan /* RAID 6 functions. */
509847360bSjsing int	sr_raid6_create(struct sr_discipline *, struct bioc_createraid *,
519847360bSjsing 	    int, int64_t);
529847360bSjsing int	sr_raid6_assemble(struct sr_discipline *, struct bioc_createraid *,
537c003ea3Sjsing 	    int, void *);
542cb1dc0aSjsing int	sr_raid6_init(struct sr_discipline *);
55f3b9c4eeSjordan int	sr_raid6_rw(struct sr_workunit *);
56f3b9c4eeSjordan int	sr_raid6_openings(struct sr_discipline *);
57f3b9c4eeSjordan void	sr_raid6_intr(struct buf *);
586c61f4caSjsing int	sr_raid6_wu_done(struct sr_workunit *);
59f3b9c4eeSjordan void	sr_raid6_set_chunk_state(struct sr_discipline *, int, int);
60f3b9c4eeSjordan void	sr_raid6_set_vol_state(struct sr_discipline *);
61f3b9c4eeSjordan 
62f3b9c4eeSjordan void	sr_raid6_xorp(void *, void *, int);
63f3b9c4eeSjordan void	sr_raid6_xorq(void *, void *, int, int);
64c804f705Skrw int	sr_raid6_addio(struct sr_workunit *wu, int, daddr_t, long,
65f3b9c4eeSjordan 	    void *, int, int, void *, void *, int);
6634678c05Sjordan int	sr_failio(struct sr_workunit *);
67f3b9c4eeSjordan 
68f3b9c4eeSjordan void	gf_init(void);
69f3b9c4eeSjordan uint8_t gf_inv(uint8_t);
70b0a939a9Sjordan int	gf_premul(uint8_t);
71cc5d9fcbSjordan uint8_t gf_mul(uint8_t, uint8_t);
72f3b9c4eeSjordan 
73f3b9c4eeSjordan #define SR_NOFAIL		0x00
74f3b9c4eeSjordan #define SR_FAILX		(1L << 0)
75f3b9c4eeSjordan #define SR_FAILY		(1L << 1)
76f3b9c4eeSjordan #define SR_FAILP		(1L << 2)
77f3b9c4eeSjordan #define SR_FAILQ		(1L << 3)
78f3b9c4eeSjordan 
79f3b9c4eeSjordan struct sr_raid6_opaque {
80f3b9c4eeSjordan 	int	gn;
81f3b9c4eeSjordan 	void	*pbuf;
82f3b9c4eeSjordan 	void	*qbuf;
83f3b9c4eeSjordan };
84f3b9c4eeSjordan 
85f3b9c4eeSjordan /* discipline initialisation. */
86f3b9c4eeSjordan void
sr_raid6_discipline_init(struct sr_discipline * sd)87f3b9c4eeSjordan sr_raid6_discipline_init(struct sr_discipline *sd)
88f3b9c4eeSjordan {
895c66ec22Sjsing 	/* Initialize GF256 tables. */
90f3b9c4eeSjordan 	gf_init();
91f3b9c4eeSjordan 
925c66ec22Sjsing 	/* Fill out discipline members. */
93e12373adSjsing 	sd->sd_type = SR_MD_RAID6;
946d81b338Sjsing 	strlcpy(sd->sd_name, "RAID 6", sizeof(sd->sd_name));
954848162aSjsing 	sd->sd_capabilities = SR_CAP_SYSTEM_DISK | SR_CAP_AUTO_ASSEMBLE |
964848162aSjsing 	    SR_CAP_REDUNDANT;
97f3b9c4eeSjordan 	sd->sd_max_wu = SR_RAID6_NOWU;
98f3b9c4eeSjordan 
995c66ec22Sjsing 	/* Setup discipline specific function pointers. */
1005c66ec22Sjsing 	sd->sd_assemble = sr_raid6_assemble;
1015c66ec22Sjsing 	sd->sd_create = sr_raid6_create;
1025c66ec22Sjsing 	sd->sd_openings = sr_raid6_openings;
103f3b9c4eeSjordan 	sd->sd_scsi_rw = sr_raid6_rw;
104148cc3e1Sjsing 	sd->sd_scsi_intr = sr_raid6_intr;
1056c61f4caSjsing 	sd->sd_scsi_wu_done = sr_raid6_wu_done;
106f3b9c4eeSjordan 	sd->sd_set_chunk_state = sr_raid6_set_chunk_state;
107f3b9c4eeSjordan 	sd->sd_set_vol_state = sr_raid6_set_vol_state;
108f3b9c4eeSjordan }
109f3b9c4eeSjordan 
110f3b9c4eeSjordan int
sr_raid6_create(struct sr_discipline * sd,struct bioc_createraid * bc,int no_chunk,int64_t coerced_size)1119847360bSjsing sr_raid6_create(struct sr_discipline *sd, struct bioc_createraid *bc,
1129847360bSjsing     int no_chunk, int64_t coerced_size)
1139847360bSjsing {
114984c6b2dSjsing 	if (no_chunk < 4) {
115984c6b2dSjsing 		sr_error(sd->sd_sc, "%s requires four or more chunks",
116984c6b2dSjsing 		    sd->sd_name);
1179847360bSjsing 		return EINVAL;
118984c6b2dSjsing 	}
1199847360bSjsing 
1209847360bSjsing 	/*
1219847360bSjsing 	 * XXX add variable strip size later even though MAXPHYS is really
1229847360bSjsing 	 * the clever value, users like * to tinker with that type of stuff.
1239847360bSjsing 	 */
1249847360bSjsing 	sd->sd_meta->ssdi.ssd_strip_size = MAXPHYS;
1259847360bSjsing 	sd->sd_meta->ssdi.ssd_size = (coerced_size &
126571fcd44Sreyk 	    ~(((u_int64_t)sd->sd_meta->ssdi.ssd_strip_size >>
127571fcd44Sreyk 	    DEV_BSHIFT) - 1)) * (no_chunk - 2);
1289847360bSjsing 
1292cb1dc0aSjsing 	return sr_raid6_init(sd);
1309847360bSjsing }
1319847360bSjsing 
1329847360bSjsing int
sr_raid6_assemble(struct sr_discipline * sd,struct bioc_createraid * bc,int no_chunk,void * data)1339847360bSjsing sr_raid6_assemble(struct sr_discipline *sd, struct bioc_createraid *bc,
1347c003ea3Sjsing     int no_chunk, void *data)
1359847360bSjsing {
1362cb1dc0aSjsing 	return sr_raid6_init(sd);
1372cb1dc0aSjsing }
1382cb1dc0aSjsing 
1392cb1dc0aSjsing int
sr_raid6_init(struct sr_discipline * sd)1402cb1dc0aSjsing sr_raid6_init(struct sr_discipline *sd)
1412cb1dc0aSjsing {
1422cb1dc0aSjsing 	/* Initialise runtime values. */
1432cb1dc0aSjsing 	sd->mds.mdd_raid6.sr6_strip_bits =
1442cb1dc0aSjsing 	    sr_validate_stripsize(sd->sd_meta->ssdi.ssd_strip_size);
1452cb1dc0aSjsing 	if (sd->mds.mdd_raid6.sr6_strip_bits == -1) {
1462cb1dc0aSjsing 		sr_error(sd->sd_sc, "invalid strip size");
1472cb1dc0aSjsing 		return EINVAL;
1482cb1dc0aSjsing 	}
1499847360bSjsing 
1509847360bSjsing 	/* only if stripsize <= MAXPHYS */
1519847360bSjsing 	sd->sd_max_ccb_per_wu = max(6, 2 * sd->sd_meta->ssdi.ssd_chunk_no);
1529847360bSjsing 
1539847360bSjsing 	return 0;
1549847360bSjsing }
1559847360bSjsing 
1569847360bSjsing int
sr_raid6_openings(struct sr_discipline * sd)157f3b9c4eeSjordan sr_raid6_openings(struct sr_discipline *sd)
158f3b9c4eeSjordan {
159f3b9c4eeSjordan 	return (sd->sd_max_wu >> 1); /* 2 wu's per IO */
160f3b9c4eeSjordan }
161f3b9c4eeSjordan 
162f3b9c4eeSjordan void
sr_raid6_set_chunk_state(struct sr_discipline * sd,int c,int new_state)163f3b9c4eeSjordan sr_raid6_set_chunk_state(struct sr_discipline *sd, int c, int new_state)
164f3b9c4eeSjordan {
165f3b9c4eeSjordan 	int			old_state, s;
166f3b9c4eeSjordan 
167f3b9c4eeSjordan 	/* XXX this is for RAID 0 */
168f3b9c4eeSjordan 	DNPRINTF(SR_D_STATE, "%s: %s: %s: sr_raid_set_chunk_state %d -> %d\n",
169f3b9c4eeSjordan 	    DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname,
170f3b9c4eeSjordan 	    sd->sd_vol.sv_chunks[c]->src_meta.scmi.scm_devname, c, new_state);
171f3b9c4eeSjordan 
172f3b9c4eeSjordan 	/* ok to go to splbio since this only happens in error path */
173f3b9c4eeSjordan 	s = splbio();
174f3b9c4eeSjordan 	old_state = sd->sd_vol.sv_chunks[c]->src_meta.scm_status;
175f3b9c4eeSjordan 
176f3b9c4eeSjordan 	/* multiple IOs to the same chunk that fail will come through here */
177f3b9c4eeSjordan 	if (old_state == new_state)
178f3b9c4eeSjordan 		goto done;
179f3b9c4eeSjordan 
180f3b9c4eeSjordan 	switch (old_state) {
181f3b9c4eeSjordan 	case BIOC_SDONLINE:
182f3b9c4eeSjordan 		switch (new_state) {
183f3b9c4eeSjordan 		case BIOC_SDOFFLINE:
184f3b9c4eeSjordan 		case BIOC_SDSCRUB:
185f3b9c4eeSjordan 			break;
186f3b9c4eeSjordan 		default:
187f3b9c4eeSjordan 			goto die;
188f3b9c4eeSjordan 		}
189f3b9c4eeSjordan 		break;
190f3b9c4eeSjordan 
191f3b9c4eeSjordan 	case BIOC_SDOFFLINE:
192f3b9c4eeSjordan 		if (new_state == BIOC_SDREBUILD) {
193f3b9c4eeSjordan 			;
194f3b9c4eeSjordan 		} else
195f3b9c4eeSjordan 			goto die;
196f3b9c4eeSjordan 		break;
197f3b9c4eeSjordan 
198f3b9c4eeSjordan 	case BIOC_SDSCRUB:
199f3b9c4eeSjordan 		switch (new_state) {
200f3b9c4eeSjordan 		case BIOC_SDONLINE:
201f3b9c4eeSjordan 		case BIOC_SDOFFLINE:
202f3b9c4eeSjordan 			break;
203f3b9c4eeSjordan 		default:
204f3b9c4eeSjordan 			goto die;
205f3b9c4eeSjordan 		}
206f3b9c4eeSjordan 		break;
207f3b9c4eeSjordan 
208f3b9c4eeSjordan 	case BIOC_SDREBUILD:
209f3b9c4eeSjordan 		switch (new_state) {
210f3b9c4eeSjordan 		case BIOC_SDONLINE:
211f3b9c4eeSjordan 		case BIOC_SDOFFLINE:
212f3b9c4eeSjordan 			break;
213f3b9c4eeSjordan 		default:
214f3b9c4eeSjordan 			goto die;
215f3b9c4eeSjordan 		}
216f3b9c4eeSjordan 		break;
217f3b9c4eeSjordan 
218f3b9c4eeSjordan 	default:
219f3b9c4eeSjordan die:
220f3b9c4eeSjordan 		splx(s); /* XXX */
221f6d8fcaeSderaadt 		panic("%s: %s: %s: invalid chunk state transition %d -> %d",
222f6d8fcaeSderaadt 		    DEVNAME(sd->sd_sc),
223f3b9c4eeSjordan 		    sd->sd_meta->ssd_devname,
224f3b9c4eeSjordan 		    sd->sd_vol.sv_chunks[c]->src_meta.scmi.scm_devname,
225f3b9c4eeSjordan 		    old_state, new_state);
226f3b9c4eeSjordan 		/* NOTREACHED */
227f3b9c4eeSjordan 	}
228f3b9c4eeSjordan 
229f3b9c4eeSjordan 	sd->sd_vol.sv_chunks[c]->src_meta.scm_status = new_state;
230f3b9c4eeSjordan 	sd->sd_set_vol_state(sd);
231f3b9c4eeSjordan 
232f3b9c4eeSjordan 	sd->sd_must_flush = 1;
233e328a933Sjsing 	task_add(systq, &sd->sd_meta_save_task);
234f3b9c4eeSjordan done:
235f3b9c4eeSjordan 	splx(s);
236f3b9c4eeSjordan }
237f3b9c4eeSjordan 
238f3b9c4eeSjordan void
sr_raid6_set_vol_state(struct sr_discipline * sd)239f3b9c4eeSjordan sr_raid6_set_vol_state(struct sr_discipline *sd)
240f3b9c4eeSjordan {
241f3b9c4eeSjordan 	int			states[SR_MAX_STATES];
242f3b9c4eeSjordan 	int			new_state, i, s, nd;
243f3b9c4eeSjordan 	int			old_state = sd->sd_vol_status;
244f3b9c4eeSjordan 
245f3b9c4eeSjordan 	/* XXX this is for RAID 0 */
246f3b9c4eeSjordan 
247f3b9c4eeSjordan 	DNPRINTF(SR_D_STATE, "%s: %s: sr_raid_set_vol_state\n",
248f3b9c4eeSjordan 	    DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname);
249f3b9c4eeSjordan 
250f3b9c4eeSjordan 	nd = sd->sd_meta->ssdi.ssd_chunk_no;
251f3b9c4eeSjordan 
252f3b9c4eeSjordan 	for (i = 0; i < SR_MAX_STATES; i++)
253f3b9c4eeSjordan 		states[i] = 0;
254f3b9c4eeSjordan 
255f3b9c4eeSjordan 	for (i = 0; i < nd; i++) {
256f3b9c4eeSjordan 		s = sd->sd_vol.sv_chunks[i]->src_meta.scm_status;
257f3b9c4eeSjordan 		if (s >= SR_MAX_STATES)
258f3b9c4eeSjordan 			panic("%s: %s: %s: invalid chunk state",
259f3b9c4eeSjordan 			    DEVNAME(sd->sd_sc),
260f3b9c4eeSjordan 			    sd->sd_meta->ssd_devname,
261f3b9c4eeSjordan 			    sd->sd_vol.sv_chunks[i]->src_meta.scmi.scm_devname);
262f3b9c4eeSjordan 		states[s]++;
263f3b9c4eeSjordan 	}
264f3b9c4eeSjordan 
265f3b9c4eeSjordan 	if (states[BIOC_SDONLINE] == nd)
266f3b9c4eeSjordan 		new_state = BIOC_SVONLINE;
267f3b9c4eeSjordan 	else if (states[BIOC_SDONLINE] < nd - 2)
268f3b9c4eeSjordan 		new_state = BIOC_SVOFFLINE;
269f3b9c4eeSjordan 	else if (states[BIOC_SDSCRUB] != 0)
270f3b9c4eeSjordan 		new_state = BIOC_SVSCRUB;
271f3b9c4eeSjordan 	else if (states[BIOC_SDREBUILD] != 0)
272f3b9c4eeSjordan 		new_state = BIOC_SVREBUILD;
2738ddfc858Sjordan 	else if (states[BIOC_SDONLINE] < nd)
2748ddfc858Sjordan 		new_state = BIOC_SVDEGRADED;
275f3b9c4eeSjordan 	else {
276f3b9c4eeSjordan 		printf("old_state = %d, ", old_state);
277f3b9c4eeSjordan 		for (i = 0; i < nd; i++)
278f3b9c4eeSjordan 			printf("%d = %d, ", i,
279f3b9c4eeSjordan 			    sd->sd_vol.sv_chunks[i]->src_meta.scm_status);
280f3b9c4eeSjordan 		panic("invalid new_state");
281f3b9c4eeSjordan 	}
282f3b9c4eeSjordan 
283f3b9c4eeSjordan 	DNPRINTF(SR_D_STATE, "%s: %s: sr_raid_set_vol_state %d -> %d\n",
284f3b9c4eeSjordan 	    DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname,
285f3b9c4eeSjordan 	    old_state, new_state);
286f3b9c4eeSjordan 
287f3b9c4eeSjordan 	switch (old_state) {
288f3b9c4eeSjordan 	case BIOC_SVONLINE:
289f3b9c4eeSjordan 		switch (new_state) {
290f3b9c4eeSjordan 		case BIOC_SVONLINE: /* can go to same state */
291f3b9c4eeSjordan 		case BIOC_SVOFFLINE:
292f3b9c4eeSjordan 		case BIOC_SVDEGRADED:
293f3b9c4eeSjordan 		case BIOC_SVREBUILD: /* happens on boot */
294f3b9c4eeSjordan 			break;
295f3b9c4eeSjordan 		default:
296f3b9c4eeSjordan 			goto die;
297f3b9c4eeSjordan 		}
298f3b9c4eeSjordan 		break;
299f3b9c4eeSjordan 
300f3b9c4eeSjordan 	case BIOC_SVOFFLINE:
301f3b9c4eeSjordan 		/* XXX this might be a little too much */
302f3b9c4eeSjordan 		goto die;
303f3b9c4eeSjordan 
304f3b9c4eeSjordan 	case BIOC_SVDEGRADED:
30597e41644Sjsing 		switch (new_state) {
30697e41644Sjsing 		case BIOC_SVOFFLINE:
30797e41644Sjsing 		case BIOC_SVREBUILD:
30897e41644Sjsing 		case BIOC_SVDEGRADED: /* can go to the same state */
309f3b9c4eeSjordan 			break;
310f3b9c4eeSjordan 		default:
311f3b9c4eeSjordan 			goto die;
312f3b9c4eeSjordan 		}
313f3b9c4eeSjordan 		break;
314f3b9c4eeSjordan 
315f3b9c4eeSjordan 	case BIOC_SVBUILDING:
316f3b9c4eeSjordan 		switch (new_state) {
317f3b9c4eeSjordan 		case BIOC_SVONLINE:
318f3b9c4eeSjordan 		case BIOC_SVOFFLINE:
319f3b9c4eeSjordan 		case BIOC_SVBUILDING: /* can go to the same state */
320f3b9c4eeSjordan 			break;
321f3b9c4eeSjordan 		default:
322f3b9c4eeSjordan 			goto die;
323f3b9c4eeSjordan 		}
324f3b9c4eeSjordan 		break;
325f3b9c4eeSjordan 
32697e41644Sjsing 	case BIOC_SVSCRUB:
327f3b9c4eeSjordan 		switch (new_state) {
328f3b9c4eeSjordan 		case BIOC_SVONLINE:
329f3b9c4eeSjordan 		case BIOC_SVOFFLINE:
330f3b9c4eeSjordan 		case BIOC_SVDEGRADED:
33197e41644Sjsing 		case BIOC_SVSCRUB: /* can go to same state */
332f3b9c4eeSjordan 			break;
333f3b9c4eeSjordan 		default:
334f3b9c4eeSjordan 			goto die;
335f3b9c4eeSjordan 		}
336f3b9c4eeSjordan 		break;
337f3b9c4eeSjordan 
338f3b9c4eeSjordan 	case BIOC_SVREBUILD:
33997e41644Sjsing 		switch (new_state) {
34097e41644Sjsing 		case BIOC_SVONLINE:
34197e41644Sjsing 		case BIOC_SVOFFLINE:
34297e41644Sjsing 		case BIOC_SVDEGRADED:
34397e41644Sjsing 		case BIOC_SVREBUILD: /* can go to the same state */
344f3b9c4eeSjordan 			break;
345f3b9c4eeSjordan 		default:
346f3b9c4eeSjordan 			goto die;
347f3b9c4eeSjordan 		}
348f3b9c4eeSjordan 		break;
349f3b9c4eeSjordan 
350f3b9c4eeSjordan 	default:
351f3b9c4eeSjordan die:
352859d5ed4Skrw 		panic("%s: %s: invalid volume state transition %d -> %d",
353f3b9c4eeSjordan 		    DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname,
354f3b9c4eeSjordan 		    old_state, new_state);
355f3b9c4eeSjordan 		/* NOTREACHED */
356f3b9c4eeSjordan 	}
357f3b9c4eeSjordan 
358f3b9c4eeSjordan 	sd->sd_vol_status = new_state;
359f3b9c4eeSjordan }
360f3b9c4eeSjordan 
361f3b9c4eeSjordan /*  modes:
362f3b9c4eeSjordan  *   readq: sr_raid6_addio(i, lba, length, NULL, SCSI_DATA_IN,
3639beda929Sjsing  *		0, qbuf, NULL, 0);
364f3b9c4eeSjordan  *   readp: sr_raid6_addio(i, lba, length, NULL, SCSI_DATA_IN,
3659beda929Sjsing  *		0, pbuf, NULL, 0);
366f3b9c4eeSjordan  *   readx: sr_raid6_addio(i, lba, length, NULL, SCSI_DATA_IN,
3679beda929Sjsing  *		0, pbuf, qbuf, gf_pow[i]);
368f3b9c4eeSjordan  */
369f3b9c4eeSjordan 
370f3b9c4eeSjordan int
sr_raid6_rw(struct sr_workunit * wu)371f3b9c4eeSjordan sr_raid6_rw(struct sr_workunit *wu)
372f3b9c4eeSjordan {
373abbe85f0Sjordan 	struct sr_workunit	*wu_r = NULL;
374f3b9c4eeSjordan 	struct sr_discipline	*sd = wu->swu_dis;
375f3b9c4eeSjordan 	struct scsi_xfer	*xs = wu->swu_xs;
376f3b9c4eeSjordan 	struct sr_chunk		*scp;
3776b0880f7Sjordan 	int			s, fail, i, gxinv, pxinv;
378d9ec6765Skrw 	daddr_t			blkno, lba;
379d9ec6765Skrw 	int64_t			chunk_offs, lbaoffs, offset, strip_offs;
380c804f705Skrw 	int64_t			strip_no, strip_size, strip_bits, row_size;
3813fb9c90bSkrw 	int64_t			fchunk, no_chunk, chunk, qchunk, pchunk;
382c804f705Skrw 	long			length, datalen;
383f3b9c4eeSjordan 	void			*pbuf, *data, *qbuf;
384f3b9c4eeSjordan 
385d9ec6765Skrw 	/* blkno and scsi error will be handled by sr_validate_io */
386d9ec6765Skrw 	if (sr_validate_io(wu, &blkno, "sr_raid6_rw"))
387f3b9c4eeSjordan 		goto bad;
388f3b9c4eeSjordan 
389f3b9c4eeSjordan 	strip_size = sd->sd_meta->ssdi.ssd_strip_size;
390f3b9c4eeSjordan 	strip_bits = sd->mds.mdd_raid6.sr6_strip_bits;
391f3b9c4eeSjordan 	no_chunk = sd->sd_meta->ssdi.ssd_chunk_no - 2;
392008ac4b7Sjordan 	row_size = (no_chunk << strip_bits) >> DEV_BSHIFT;
393f3b9c4eeSjordan 
394f3b9c4eeSjordan 	data = xs->data;
395f3b9c4eeSjordan 	datalen = xs->datalen;
396d9ec6765Skrw 	lbaoffs	= blkno << DEV_BSHIFT;
397f3b9c4eeSjordan 
39819d58073Sjsing 	if (xs->flags & SCSI_DATA_OUT) {
3997bf8e677Sjsing 		if ((wu_r = sr_scsi_wu_get(sd, SCSI_NOSLEEP)) == NULL){
400abbe85f0Sjordan 			printf("%s: can't get wu_r", DEVNAME(sd->sd_sc));
401f3b9c4eeSjordan 			goto bad;
402f3b9c4eeSjordan 		}
403fde344d9Sjsing 		wu_r->swu_state = SR_WU_INPROGRESS;
40419d58073Sjsing 		wu_r->swu_flags |= SR_WUF_DISCIPLINE;
40519d58073Sjsing 	}
406f3b9c4eeSjordan 
407f3b9c4eeSjordan 	wu->swu_blk_start = 0;
408f3b9c4eeSjordan 	while (datalen != 0) {
409f3b9c4eeSjordan 		strip_no = lbaoffs >> strip_bits;
410f3b9c4eeSjordan 		strip_offs = lbaoffs & (strip_size - 1);
411f3b9c4eeSjordan 		chunk_offs = (strip_no / no_chunk) << strip_bits;
412d9ec6765Skrw 		offset = chunk_offs + strip_offs;
413f3b9c4eeSjordan 
414f3b9c4eeSjordan 		/* get size remaining in this stripe */
415f3b9c4eeSjordan 		length = MIN(strip_size - strip_offs, datalen);
416f3b9c4eeSjordan 
417f3b9c4eeSjordan 		/* map disk offset to parity/data drive */
418f3b9c4eeSjordan 		chunk = strip_no % no_chunk;
419f3b9c4eeSjordan 
420f3b9c4eeSjordan 		qchunk = (no_chunk + 1) - ((strip_no / no_chunk) % (no_chunk+2));
421f3b9c4eeSjordan 		if (qchunk == 0)
422f3b9c4eeSjordan 			pchunk = no_chunk + 1;
423f3b9c4eeSjordan 		else
424f3b9c4eeSjordan 			pchunk = qchunk - 1;
425f3b9c4eeSjordan 		if (chunk >= pchunk)
426f3b9c4eeSjordan 			chunk++;
427f3b9c4eeSjordan 		if (chunk >= qchunk)
428f3b9c4eeSjordan 			chunk++;
429f3b9c4eeSjordan 
430d9ec6765Skrw 		lba = offset >> DEV_BSHIFT;
431f3b9c4eeSjordan 
432f3b9c4eeSjordan 		/* XXX big hammer.. exclude I/O from entire stripe */
433f3b9c4eeSjordan 		if (wu->swu_blk_start == 0)
434008ac4b7Sjordan 			wu->swu_blk_start = (strip_no / no_chunk) * row_size;
435008ac4b7Sjordan 		wu->swu_blk_end = (strip_no / no_chunk) * row_size + (row_size - 1);
436f3b9c4eeSjordan 
437f3b9c4eeSjordan 		fail = 0;
43834678c05Sjordan 		fchunk = -1;
439f3b9c4eeSjordan 
44034678c05Sjordan 		/* Get disk-fail flags */
44134678c05Sjordan 		for (i=0; i< no_chunk+2; i++) {
44234678c05Sjordan 			scp = sd->sd_vol.sv_chunks[i];
443f3b9c4eeSjordan 			switch (scp->src_meta.scm_status) {
444f3b9c4eeSjordan 			case BIOC_SDOFFLINE:
445f3b9c4eeSjordan 			case BIOC_SDREBUILD:
446f3b9c4eeSjordan 			case BIOC_SDHOTSPARE:
44734678c05Sjordan 				if (i == qchunk)
448f3b9c4eeSjordan 					fail |= SR_FAILQ;
44934678c05Sjordan 				else if (i == pchunk)
45034678c05Sjordan 					fail |= SR_FAILP;
45134678c05Sjordan 				else if (i == chunk)
452f3b9c4eeSjordan 					fail |= SR_FAILX;
45334678c05Sjordan 				else {
45434678c05Sjordan 					/* dual data-disk failure */
455f3b9c4eeSjordan 					fail |= SR_FAILY;
45634678c05Sjordan 					fchunk = i;
45734678c05Sjordan 				}
458f3b9c4eeSjordan 				break;
459f3b9c4eeSjordan 			}
46034678c05Sjordan 		}
461f3b9c4eeSjordan 		if (xs->flags & SCSI_DATA_IN) {
46234678c05Sjordan 			if (!(fail & SR_FAILX)) {
463f3b9c4eeSjordan 				/* drive is good. issue single read request */
464f3b9c4eeSjordan 				if (sr_raid6_addio(wu, chunk, lba, length,
465f3b9c4eeSjordan 				    data, xs->flags, 0, NULL, NULL, 0))
466f3b9c4eeSjordan 					goto bad;
46734678c05Sjordan 			} else if (fail & SR_FAILP) {
468f3b9c4eeSjordan 				/* Dx, P failed */
469f3b9c4eeSjordan 				printf("Disk %llx offline, "
470f3b9c4eeSjordan 				    "regenerating Dx+P\n", chunk);
471f3b9c4eeSjordan 
472cc5d9fcbSjordan 				gxinv = gf_inv(gf_pow[chunk]);
473cc5d9fcbSjordan 
474995b30d7Sjordan 				/* Calculate: Dx = (Q^Dz*gz)*inv(gx) */
475995b30d7Sjordan 				memset(data, 0, length);
4769beda929Sjsing 				if (sr_raid6_addio(wu, qchunk, lba, length,
4779beda929Sjsing 				    NULL, SCSI_DATA_IN, 0, NULL, data, gxinv))
478f3b9c4eeSjordan 					goto bad;
479f3b9c4eeSjordan 
480995b30d7Sjordan 				/* Read Dz * gz * inv(gx) */
481f3b9c4eeSjordan 				for (i = 0; i < no_chunk+2; i++) {
482995b30d7Sjordan 					if  (i == qchunk || i == pchunk || i == chunk)
483995b30d7Sjordan 						continue;
484995b30d7Sjordan 
4859beda929Sjsing 					if (sr_raid6_addio(wu, i, lba, length,
4869beda929Sjsing 					    NULL, SCSI_DATA_IN, 0, NULL, data,
4879beda929Sjsing 					    gf_mul(gf_pow[i], gxinv)))
488f3b9c4eeSjordan 						goto bad;
489f3b9c4eeSjordan 				}
490f3b9c4eeSjordan 
491995b30d7Sjordan 				/* data will contain correct value on completion */
49234678c05Sjordan 			} else if (fail & SR_FAILY) {
493f3b9c4eeSjordan 				/* Dx, Dy failed */
494f3b9c4eeSjordan 				printf("Disk %llx & %llx offline, "
495f3b9c4eeSjordan 				    "regenerating Dx+Dy\n", chunk, fchunk);
496f3b9c4eeSjordan 
497cc5d9fcbSjordan 				gxinv = gf_inv(gf_pow[chunk] ^ gf_pow[fchunk]);
4983c9224ebSjordan 				pxinv = gf_mul(gf_pow[fchunk], gxinv);
499cc5d9fcbSjordan 
500cc5d9fcbSjordan 				/* read Q * inv(gx + gy) */
501cc5d9fcbSjordan 				memset(data, 0, length);
5029beda929Sjsing 				if (sr_raid6_addio(wu, qchunk, lba, length,
5039beda929Sjsing 				    NULL, SCSI_DATA_IN, 0, NULL, data, gxinv))
504cc5d9fcbSjordan 					goto bad;
505cc5d9fcbSjordan 
5063c9224ebSjordan 				/* read P * gy * inv(gx + gy) */
5079beda929Sjsing 				if (sr_raid6_addio(wu, pchunk, lba, length,
5089beda929Sjsing 				    NULL, SCSI_DATA_IN, 0, NULL, data, pxinv))
509cc5d9fcbSjordan 					goto bad;
510cc5d9fcbSjordan 
511f3b9c4eeSjordan 				/* Calculate: Dx*gx^Dy*gy = Q^(Dz*gz) ; Dx^Dy = P^Dz
512f3b9c4eeSjordan 				 *   Q:  sr_raid6_xorp(qbuf, --, length);
513f3b9c4eeSjordan 				 *   P:  sr_raid6_xorp(pbuf, --, length);
514f3b9c4eeSjordan 				 *   Dz: sr_raid6_xorp(pbuf, --, length);
515f3b9c4eeSjordan 				 *	 sr_raid6_xorq(qbuf, --, length, gf_pow[i]);
516f3b9c4eeSjordan 				 */
517f3b9c4eeSjordan 				for (i = 0; i < no_chunk+2; i++) {
518cc5d9fcbSjordan 					if (i == qchunk || i == pchunk ||
519cc5d9fcbSjordan 					    i == chunk || i == fchunk)
520cc5d9fcbSjordan 						continue;
521cc5d9fcbSjordan 
5223c9224ebSjordan 					/* read Dz * (gz + gy) * inv(gx + gy) */
5239beda929Sjsing 					if (sr_raid6_addio(wu, i, lba, length,
5249beda929Sjsing 					    NULL, SCSI_DATA_IN, 0, NULL, data,
5253c9224ebSjordan 					    pxinv ^ gf_mul(gf_pow[i], gxinv)))
526f3b9c4eeSjordan 						goto bad;
527f3b9c4eeSjordan 				}
52834678c05Sjordan 			} else {
52934678c05Sjordan 				/* Two cases: single disk (Dx) or (Dx+Q)
53034678c05Sjordan 				 *   Dx = Dz ^ P (same as RAID5)
53134678c05Sjordan 				 */
53234678c05Sjordan 				printf("Disk %llx offline, "
53334678c05Sjordan 				    "regenerating Dx%s\n", chunk,
53434678c05Sjordan 				    fail & SR_FAILQ ? "+Q" : " single");
53534678c05Sjordan 
53634678c05Sjordan 				/* Calculate: Dx = P^Dz
53734678c05Sjordan 				 *   P:  sr_raid6_xorp(data, ---, length);
53834678c05Sjordan 				 *   Dz: sr_raid6_xorp(data, ---, length);
53934678c05Sjordan 				 */
54034678c05Sjordan 				memset(data, 0, length);
54134678c05Sjordan 				for (i = 0; i < no_chunk+2; i++) {
54234678c05Sjordan 					if (i != chunk && i != qchunk) {
54334678c05Sjordan 						/* Read Dz */
54434678c05Sjordan 						if (sr_raid6_addio(wu, i, lba,
54534678c05Sjordan 						    length, NULL, SCSI_DATA_IN,
5469beda929Sjsing 						    0, data, NULL, 0))
54734678c05Sjordan 							goto bad;
54834678c05Sjordan 					}
549f3b9c4eeSjordan 				}
550f3b9c4eeSjordan 
55134678c05Sjordan 				/* data will contain correct value on completion */
552f3b9c4eeSjordan 			}
553f3b9c4eeSjordan 		} else {
554f3b9c4eeSjordan 			/* XXX handle writes to failed/offline disk? */
55534678c05Sjordan 			if (fail & (SR_FAILX|SR_FAILQ|SR_FAILP))
556f3b9c4eeSjordan 				goto bad;
557f3b9c4eeSjordan 
558f3b9c4eeSjordan 			/*
559f3b9c4eeSjordan 			 * initialize pbuf with contents of new data to be
560f3b9c4eeSjordan 			 * written. This will be XORed with old data and old
561f3b9c4eeSjordan 			 * parity in the intr routine. The result in pbuf
562f3b9c4eeSjordan 			 * is the new parity data.
563f3b9c4eeSjordan 			 */
5644c26ac15Sjsing 			qbuf = sr_block_get(sd, length);
565f3b9c4eeSjordan 			if (qbuf == NULL)
566f3b9c4eeSjordan 				goto bad;
567f3b9c4eeSjordan 
5684c26ac15Sjsing 			pbuf = sr_block_get(sd, length);
569f3b9c4eeSjordan 			if (pbuf == NULL)
570f3b9c4eeSjordan 				goto bad;
571f3b9c4eeSjordan 
572f5fc0dceSmiod 			/* Calculate P = Dn; Q = gn * Dn */
573b0a939a9Sjordan 			if (gf_premul(gf_pow[chunk]))
574b0a939a9Sjordan 				goto bad;
575f3b9c4eeSjordan 			sr_raid6_xorp(pbuf, data, length);
576f3b9c4eeSjordan 			sr_raid6_xorq(qbuf, data, length, gf_pow[chunk]);
577f3b9c4eeSjordan 
578f3b9c4eeSjordan 			/* Read old data: P ^= Dn' ; Q ^= (gn * Dn') */
579abbe85f0Sjordan 			if (sr_raid6_addio(wu_r, chunk, lba, length, NULL,
5809beda929Sjsing 				SCSI_DATA_IN, 0, pbuf, qbuf, gf_pow[chunk]))
581f3b9c4eeSjordan 				goto bad;
582f3b9c4eeSjordan 
583f3b9c4eeSjordan 			/* Read old xor-parity: P ^= P' */
584abbe85f0Sjordan 			if (sr_raid6_addio(wu_r, pchunk, lba, length, NULL,
5859beda929Sjsing 				SCSI_DATA_IN, 0, pbuf, NULL, 0))
586f3b9c4eeSjordan 				goto bad;
587f3b9c4eeSjordan 
588f3b9c4eeSjordan 			/* Read old q-parity: Q ^= Q' */
589abbe85f0Sjordan 			if (sr_raid6_addio(wu_r, qchunk, lba, length, NULL,
5909beda929Sjsing 				SCSI_DATA_IN, 0, qbuf, NULL, 0))
591f3b9c4eeSjordan 				goto bad;
592f3b9c4eeSjordan 
593f3b9c4eeSjordan 			/* write new data */
594abbe85f0Sjordan 			if (sr_raid6_addio(wu, chunk, lba, length, data,
595f3b9c4eeSjordan 			    xs->flags, 0, NULL, NULL, 0))
596f3b9c4eeSjordan 				goto bad;
597f3b9c4eeSjordan 
598f3b9c4eeSjordan 			/* write new xor-parity */
599abbe85f0Sjordan 			if (sr_raid6_addio(wu, pchunk, lba, length, pbuf,
600f3b9c4eeSjordan 			    xs->flags, SR_CCBF_FREEBUF, NULL, NULL, 0))
601f3b9c4eeSjordan 				goto bad;
602f3b9c4eeSjordan 
603f3b9c4eeSjordan 			/* write new q-parity */
604abbe85f0Sjordan 			if (sr_raid6_addio(wu, qchunk, lba, length, qbuf,
605f3b9c4eeSjordan 			    xs->flags, SR_CCBF_FREEBUF, NULL, NULL, 0))
606f3b9c4eeSjordan 				goto bad;
607f3b9c4eeSjordan 		}
608f3b9c4eeSjordan 
609f3b9c4eeSjordan 		/* advance to next block */
610f3b9c4eeSjordan 		lbaoffs += length;
611f3b9c4eeSjordan 		datalen -= length;
612f3b9c4eeSjordan 		data += length;
613f3b9c4eeSjordan 	}
614f3b9c4eeSjordan 
615f3b9c4eeSjordan 	s = splbio();
616abbe85f0Sjordan 	if (wu_r) {
617f3b9c4eeSjordan 		/* collide write request with reads */
618abbe85f0Sjordan 		wu_r->swu_blk_start = wu->swu_blk_start;
619abbe85f0Sjordan 		wu_r->swu_blk_end = wu->swu_blk_end;
620f3b9c4eeSjordan 
621abbe85f0Sjordan 		wu->swu_state = SR_WU_DEFERRED;
622abbe85f0Sjordan 		wu_r->swu_collider = wu;
623abbe85f0Sjordan 		TAILQ_INSERT_TAIL(&sd->sd_wu_defq, wu, swu_link);
624f3b9c4eeSjordan 
625abbe85f0Sjordan 		wu = wu_r;
626f3b9c4eeSjordan 	}
627f3b9c4eeSjordan 	splx(s);
62860781f8cSjsing 
62960781f8cSjsing 	sr_schedule_wu(wu);
63060781f8cSjsing 
631f3b9c4eeSjordan 	return (0);
632f3b9c4eeSjordan bad:
6339beda929Sjsing 	/* XXX - can leak pbuf/qbuf on error. */
634f3b9c4eeSjordan 	/* wu is unwound by sr_wu_put */
635abbe85f0Sjordan 	if (wu_r)
6367bf8e677Sjsing 		sr_scsi_wu_put(sd, wu_r);
637f3b9c4eeSjordan 	return (1);
638f3b9c4eeSjordan }
639f3b9c4eeSjordan 
64034678c05Sjordan /* Handle failure I/O completion */
64134678c05Sjordan int
sr_failio(struct sr_workunit * wu)64234678c05Sjordan sr_failio(struct sr_workunit *wu)
64334678c05Sjordan {
64434678c05Sjordan 	struct sr_discipline	*sd = wu->swu_dis;
64534678c05Sjordan 	struct sr_ccb		*ccb;
64634678c05Sjordan 
64734678c05Sjordan 	if (!(wu->swu_flags & SR_WUF_FAIL))
64834678c05Sjordan 		return (0);
64934678c05Sjordan 
65034678c05Sjordan 	/* Wu is a 'fake'.. don't do real I/O just intr */
65134678c05Sjordan 	TAILQ_INSERT_TAIL(&sd->sd_wu_pendq, wu, swu_link);
65234678c05Sjordan 	TAILQ_FOREACH(ccb, &wu->swu_ccb, ccb_link)
65334678c05Sjordan 		sr_raid6_intr(&ccb->ccb_buf);
65434678c05Sjordan 	return (1);
65534678c05Sjordan }
65634678c05Sjordan 
657f3b9c4eeSjordan void
sr_raid6_intr(struct buf * bp)658f3b9c4eeSjordan sr_raid6_intr(struct buf *bp)
659f3b9c4eeSjordan {
660f3b9c4eeSjordan 	struct sr_ccb		*ccb = (struct sr_ccb *)bp;
6616c61f4caSjsing 	struct sr_workunit	*wu = ccb->ccb_wu;
662f3b9c4eeSjordan 	struct sr_discipline	*sd = wu->swu_dis;
663f3b9c4eeSjordan 	struct sr_raid6_opaque  *pq = ccb->ccb_opaque;
6642b14bff0Sjsing 	int			s;
665f3b9c4eeSjordan 
666ebb823aaSjsing 	DNPRINTF(SR_D_INTR, "%s: sr_raid6_intr bp %p xs %p\n",
6676c61f4caSjsing 	    DEVNAME(sd->sd_sc), bp, wu->swu_xs);
668f3b9c4eeSjordan 
669f3b9c4eeSjordan 	s = splbio();
670ebb823aaSjsing 	sr_ccb_done(ccb);
671f3b9c4eeSjordan 
672ebb823aaSjsing 	/* XOR data to result. */
673ebb823aaSjsing 	if (ccb->ccb_state == SR_CCB_OK && pq) {
674f3b9c4eeSjordan 		if (pq->pbuf)
675f3b9c4eeSjordan 			/* Calculate xor-parity */
676f3b9c4eeSjordan 			sr_raid6_xorp(pq->pbuf, ccb->ccb_buf.b_data,
677f3b9c4eeSjordan 			    ccb->ccb_buf.b_bcount);
678f3b9c4eeSjordan 		if (pq->qbuf)
679f3b9c4eeSjordan 			/* Calculate q-parity */
680f3b9c4eeSjordan 			sr_raid6_xorq(pq->qbuf, ccb->ccb_buf.b_data,
681f3b9c4eeSjordan 			    ccb->ccb_buf.b_bcount, pq->gn);
682df2ac69fStedu 		free(pq, M_DEVBUF, 0);
683f3b9c4eeSjordan 		ccb->ccb_opaque = NULL;
684f3b9c4eeSjordan 	}
685f3b9c4eeSjordan 
686ebb823aaSjsing 	/* Free allocated data buffer. */
687b8c2831aSjsing 	if (ccb->ccb_flags & SR_CCBF_FREEBUF) {
6884c26ac15Sjsing 		sr_block_put(sd, ccb->ccb_buf.b_data, ccb->ccb_buf.b_bcount);
689f3b9c4eeSjordan 		ccb->ccb_buf.b_data = NULL;
690f3b9c4eeSjordan 	}
691f3b9c4eeSjordan 
6926c61f4caSjsing 	sr_wu_done(wu);
6936c61f4caSjsing 	splx(s);
6946c61f4caSjsing }
695f3b9c4eeSjordan 
6966c61f4caSjsing int
sr_raid6_wu_done(struct sr_workunit * wu)6976c61f4caSjsing sr_raid6_wu_done(struct sr_workunit *wu)
6986c61f4caSjsing {
6996c61f4caSjsing 	struct sr_discipline	*sd = wu->swu_dis;
7006c61f4caSjsing 	struct scsi_xfer	*xs = wu->swu_xs;
7012b14bff0Sjsing 
7026c61f4caSjsing 	/* XXX - we have no way of propagating errors... */
7036c61f4caSjsing 	if (wu->swu_flags & SR_WUF_DISCIPLINE)
7046c61f4caSjsing 		return SR_WU_OK;
7056c61f4caSjsing 
7066c61f4caSjsing 	/* XXX - This is insufficient for RAID 6. */
7076c61f4caSjsing 	if (wu->swu_ios_succeeded > 0) {
7082b14bff0Sjsing 		xs->error = XS_NOERROR;
7096c61f4caSjsing 		return SR_WU_OK;
7106c61f4caSjsing 	}
711f3b9c4eeSjordan 
712f3b9c4eeSjordan 	if (xs->flags & SCSI_DATA_IN) {
713f3b9c4eeSjordan 		printf("%s: retrying read on block %lld\n",
714bb4f4faeSkrw 		    sd->sd_meta->ssd_devname, (long long)wu->swu_blk_start);
715f4e69389Sjsing 		sr_wu_release_ccbs(wu);
716f3b9c4eeSjordan 		wu->swu_state = SR_WU_RESTART;
7172b14bff0Sjsing 		if (sd->sd_scsi_rw(wu) == 0)
7186c61f4caSjsing 			return SR_WU_RESTART;
7192b14bff0Sjsing 	} else {
7202b14bff0Sjsing 		printf("%s: permanently fail write on block %lld\n",
721bb4f4faeSkrw 		    sd->sd_meta->ssd_devname, (long long)wu->swu_blk_start);
7226c61f4caSjsing 	}
7236c61f4caSjsing 
7246c61f4caSjsing 	wu->swu_state = SR_WU_FAILED;
7252b14bff0Sjsing 	xs->error = XS_DRIVER_STUFFUP;
726f3b9c4eeSjordan 
7276c61f4caSjsing 	return SR_WU_FAILED;
728f3b9c4eeSjordan }
729f3b9c4eeSjordan 
730f3b9c4eeSjordan int
sr_raid6_addio(struct sr_workunit * wu,int chunk,daddr_t blkno,long len,void * data,int xsflags,int ccbflags,void * pbuf,void * qbuf,int gn)7311abdbfdeSderaadt sr_raid6_addio(struct sr_workunit *wu, int chunk, daddr_t blkno,
732c804f705Skrw     long len, void *data, int xsflags, int ccbflags, void *pbuf,
733f490be88Sjsing     void *qbuf, int gn)
734f3b9c4eeSjordan {
735f3b9c4eeSjordan 	struct sr_discipline	*sd = wu->swu_dis;
736f3b9c4eeSjordan 	struct sr_ccb		*ccb;
737f3b9c4eeSjordan 	struct sr_raid6_opaque  *pqbuf;
738f3b9c4eeSjordan 
739d9ec6765Skrw 	DNPRINTF(SR_D_DIS, "sr_raid6_addio: %s %d.%lld %ld %p:%p\n",
740413aee89Skrw 	    (xsflags & SCSI_DATA_IN) ? "read" : "write", chunk,
741d9ec6765Skrw 	    (long long)blkno, len, pbuf, qbuf);
742f3b9c4eeSjordan 
743f490be88Sjsing 	/* Allocate temporary buffer. */
744f3b9c4eeSjordan 	if (data == NULL) {
7454c26ac15Sjsing 		data = sr_block_get(sd, len);
746f3b9c4eeSjordan 		if (data == NULL)
747f3b9c4eeSjordan 			return (-1);
7489beda929Sjsing 		ccbflags |= SR_CCBF_FREEBUF;
749f3b9c4eeSjordan 	}
750f3b9c4eeSjordan 
751f490be88Sjsing 	ccb = sr_ccb_rw(sd, chunk, blkno, len, data, xsflags, ccbflags);
752f490be88Sjsing 	if (ccb == NULL) {
753f490be88Sjsing 		if (ccbflags & SR_CCBF_FREEBUF)
7544c26ac15Sjsing 			sr_block_put(sd, data, len);
755f490be88Sjsing 		return (-1);
756f3b9c4eeSjordan 	}
757f3b9c4eeSjordan 	if (pbuf || qbuf) {
758f490be88Sjsing 		/* XXX - can leak data and ccb on failure. */
759b0a939a9Sjordan 		if (qbuf && gf_premul(gn))
760b0a939a9Sjordan 			return (-1);
761b0a939a9Sjordan 
762f490be88Sjsing 		/* XXX - should be preallocated? */
763f490be88Sjsing 		pqbuf = malloc(sizeof(struct sr_raid6_opaque),
764f490be88Sjsing 		    M_DEVBUF, M_ZERO | M_NOWAIT);
765f3b9c4eeSjordan 		if (pqbuf == NULL) {
766f3b9c4eeSjordan 			sr_ccb_put(ccb);
767f3b9c4eeSjordan 			return (-1);
768f3b9c4eeSjordan 		}
769f3b9c4eeSjordan 		pqbuf->pbuf = pbuf;
770f3b9c4eeSjordan 		pqbuf->qbuf = qbuf;
771f3b9c4eeSjordan 		pqbuf->gn = gn;
772f3b9c4eeSjordan 		ccb->ccb_opaque = pqbuf;
773f3b9c4eeSjordan 	}
774f490be88Sjsing 	sr_wu_enqueue_ccb(wu, ccb);
775f3b9c4eeSjordan 
776f3b9c4eeSjordan 	return (0);
777f3b9c4eeSjordan }
778f3b9c4eeSjordan 
779f3b9c4eeSjordan /* Perform RAID6 parity calculation.
780f3b9c4eeSjordan  *   P=xor parity, Q=GF256 parity, D=data, gn=disk# */
781f3b9c4eeSjordan void
sr_raid6_xorp(void * p,void * d,int len)782f3b9c4eeSjordan sr_raid6_xorp(void *p, void *d, int len)
783f3b9c4eeSjordan {
784da4b5245Sjordan 	uint32_t *pbuf = p, *data = d;
785f3b9c4eeSjordan 
786da4b5245Sjordan 	len >>= 2;
787f3b9c4eeSjordan 	while (len--)
788da4b5245Sjordan 		*pbuf++ ^= *data++;
789f3b9c4eeSjordan }
790f3b9c4eeSjordan 
791f3b9c4eeSjordan void
sr_raid6_xorq(void * q,void * d,int len,int gn)792f3b9c4eeSjordan sr_raid6_xorq(void *q, void *d, int len, int gn)
793f3b9c4eeSjordan {
794da4b5245Sjordan 	uint32_t	*qbuf = q, *data = d, x;
795b0a939a9Sjordan 	uint8_t		*gn_map = gf_map[gn];
796f3b9c4eeSjordan 
797da4b5245Sjordan 	len >>= 2;
798da4b5245Sjordan 	while (len--) {
799da4b5245Sjordan 		x = *data++;
800da4b5245Sjordan 		*qbuf++ ^= (((uint32_t)gn_map[x & 0xff]) |
801da4b5245Sjordan 			    ((uint32_t)gn_map[(x >> 8) & 0xff] << 8) |
802da4b5245Sjordan 			    ((uint32_t)gn_map[(x >> 16) & 0xff] << 16) |
803da4b5245Sjordan 			    ((uint32_t)gn_map[(x >> 24) & 0xff] << 24));
804da4b5245Sjordan 	}
805f3b9c4eeSjordan }
806f3b9c4eeSjordan 
807f3b9c4eeSjordan /* Create GF256 log/pow tables: polynomial = 0x11D */
808f3b9c4eeSjordan void
gf_init(void)809f3b9c4eeSjordan gf_init(void)
810f3b9c4eeSjordan {
811f3b9c4eeSjordan 	int i;
812f3b9c4eeSjordan 	uint8_t p = 1;
813f3b9c4eeSjordan 
814f3b9c4eeSjordan 	/* use 2N pow table to avoid using % in multiply */
815f3b9c4eeSjordan 	for (i=0; i<256; i++) {
816f3b9c4eeSjordan 		gf_log[p] = i;
817f3b9c4eeSjordan 		gf_pow[i] = gf_pow[i+255] = p;
818f3b9c4eeSjordan 		p = ((p << 1) ^ ((p & 0x80) ? 0x1D : 0x00));
819f3b9c4eeSjordan 	}
820c8cd47bbSjordan 	gf_log[0] = 512;
821f3b9c4eeSjordan }
822f3b9c4eeSjordan 
823f3b9c4eeSjordan uint8_t
gf_inv(uint8_t a)824f3b9c4eeSjordan gf_inv(uint8_t a)
825f3b9c4eeSjordan {
826f3b9c4eeSjordan 	return gf_pow[255 - gf_log[a]];
827f3b9c4eeSjordan }
828f3b9c4eeSjordan 
829cc5d9fcbSjordan uint8_t
gf_mul(uint8_t a,uint8_t b)830cc5d9fcbSjordan gf_mul(uint8_t a, uint8_t b)
831cc5d9fcbSjordan {
832cc5d9fcbSjordan 	return gf_pow[gf_log[a] + gf_log[b]];
833cc5d9fcbSjordan }
834cc5d9fcbSjordan 
835b0a939a9Sjordan /* Precalculate multiplication tables for drive gn */
836b0a939a9Sjordan int
gf_premul(uint8_t gn)837b0a939a9Sjordan gf_premul(uint8_t gn)
838b0a939a9Sjordan {
839b0a939a9Sjordan 	int i;
840b0a939a9Sjordan 
841b0a939a9Sjordan 	if (gf_map[gn] != NULL)
842b0a939a9Sjordan 		return (0);
843b0a939a9Sjordan 
844abbe85f0Sjordan 	if ((gf_map[gn] = malloc(256, M_DEVBUF, M_ZERO | M_NOWAIT)) == NULL)
845b0a939a9Sjordan 		return (-1);
846b0a939a9Sjordan 
847b0a939a9Sjordan 	for (i=0; i<256; i++)
848b0a939a9Sjordan 		gf_map[gn][i] = gf_pow[gf_log[i] + gf_log[gn]];
849b0a939a9Sjordan 	return (0);
850b0a939a9Sjordan }
851