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