File: | dev/softraid_raid5.c |
Warning: | line 729, column 29 Access to field 'swu_dis' results in a dereference of a null pointer (loaded from variable 'wu') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: softraid_raid5.c,v 1.32 2021/05/16 15:12:37 deraadt Exp $ */ | ||||
2 | /* | ||||
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | ||||
4 | * Copyright (c) 2009 Marco Peereboom <marco@peereboom.us> | ||||
5 | * Copyright (c) 2009 Jordan Hargrave <jordan@openbsd.org> | ||||
6 | * | ||||
7 | * Permission to use, copy, modify, and distribute this software for any | ||||
8 | * purpose with or without fee is hereby granted, provided that the above | ||||
9 | * copyright notice and this permission notice appear in all copies. | ||||
10 | * | ||||
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
18 | */ | ||||
19 | |||||
20 | #include "bio.h" | ||||
21 | |||||
22 | #include <sys/param.h> | ||||
23 | #include <sys/systm.h> | ||||
24 | #include <sys/buf.h> | ||||
25 | #include <sys/device.h> | ||||
26 | #include <sys/ioctl.h> | ||||
27 | #include <sys/malloc.h> | ||||
28 | #include <sys/kernel.h> | ||||
29 | #include <sys/disk.h> | ||||
30 | #include <sys/rwlock.h> | ||||
31 | #include <sys/queue.h> | ||||
32 | #include <sys/fcntl.h> | ||||
33 | #include <sys/mount.h> | ||||
34 | #include <sys/sensors.h> | ||||
35 | #include <sys/stat.h> | ||||
36 | #include <sys/task.h> | ||||
37 | #include <sys/pool.h> | ||||
38 | #include <sys/conf.h> | ||||
39 | #include <sys/uio.h> | ||||
40 | |||||
41 | #include <scsi/scsi_all.h> | ||||
42 | #include <scsi/scsiconf.h> | ||||
43 | #include <scsi/scsi_disk.h> | ||||
44 | |||||
45 | #include <dev/softraidvar.h> | ||||
46 | |||||
47 | /* RAID 5 functions. */ | ||||
48 | int sr_raid5_create(struct sr_discipline *, struct bioc_createraid *, | ||||
49 | int, int64_t); | ||||
50 | int sr_raid5_assemble(struct sr_discipline *, struct bioc_createraid *, | ||||
51 | int, void *); | ||||
52 | int sr_raid5_init(struct sr_discipline *); | ||||
53 | int sr_raid5_rw(struct sr_workunit *); | ||||
54 | int sr_raid5_openings(struct sr_discipline *); | ||||
55 | void sr_raid5_intr(struct buf *); | ||||
56 | int sr_raid5_wu_done(struct sr_workunit *); | ||||
57 | void sr_raid5_set_chunk_state(struct sr_discipline *, int, int); | ||||
58 | void sr_raid5_set_vol_state(struct sr_discipline *); | ||||
59 | |||||
60 | int sr_raid5_addio(struct sr_workunit *wu, int, daddr_t, long, | ||||
61 | void *, int, int, void *); | ||||
62 | int sr_raid5_regenerate(struct sr_workunit *, int, daddr_t, long, | ||||
63 | void *); | ||||
64 | int sr_raid5_write(struct sr_workunit *, struct sr_workunit *, int, int, | ||||
65 | daddr_t, long, void *, int, int); | ||||
66 | void sr_raid5_xor(void *, void *, int); | ||||
67 | |||||
68 | void sr_raid5_rebuild(struct sr_discipline *); | ||||
69 | void sr_raid5_scrub(struct sr_discipline *); | ||||
70 | |||||
71 | /* discipline initialisation. */ | ||||
72 | void | ||||
73 | sr_raid5_discipline_init(struct sr_discipline *sd) | ||||
74 | { | ||||
75 | /* Fill out discipline members. */ | ||||
76 | sd->sd_type = SR_MD_RAID52; | ||||
77 | strlcpy(sd->sd_name, "RAID 5", sizeof(sd->sd_name)); | ||||
78 | sd->sd_capabilities = SR_CAP_SYSTEM_DISK0x00000001 | SR_CAP_AUTO_ASSEMBLE0x00000002 | | ||||
79 | SR_CAP_REBUILD0x00000004 | SR_CAP_REDUNDANT0x00000010; | ||||
80 | sd->sd_max_wu = SR_RAID5_NOWU16 + 2; /* Two for scrub/rebuild. */ | ||||
81 | |||||
82 | /* Setup discipline specific function pointers. */ | ||||
83 | sd->sd_assemble = sr_raid5_assemble; | ||||
84 | sd->sd_create = sr_raid5_create; | ||||
85 | sd->sd_openings = sr_raid5_openings; | ||||
86 | sd->sd_rebuild = sr_raid5_rebuild; | ||||
87 | sd->sd_scsi_rw = sr_raid5_rw; | ||||
88 | sd->sd_scsi_intr = sr_raid5_intr; | ||||
89 | sd->sd_scsi_wu_done = sr_raid5_wu_done; | ||||
90 | sd->sd_set_chunk_state = sr_raid5_set_chunk_state; | ||||
91 | sd->sd_set_vol_state = sr_raid5_set_vol_state; | ||||
92 | } | ||||
93 | |||||
94 | int | ||||
95 | sr_raid5_create(struct sr_discipline *sd, struct bioc_createraid *bc, | ||||
96 | int no_chunk, int64_t coerced_size) | ||||
97 | { | ||||
98 | if (no_chunk < 3) { | ||||
99 | sr_error(sd->sd_sc, "%s requires three or more chunks", | ||||
100 | sd->sd_name); | ||||
101 | return EINVAL22; | ||||
102 | } | ||||
103 | |||||
104 | /* | ||||
105 | * XXX add variable strip size later even though MAXPHYS is really | ||||
106 | * the clever value, users like to tinker with that type of stuff. | ||||
107 | */ | ||||
108 | sd->sd_meta->ssdi_sdd_invariant.ssd_strip_size = MAXPHYS(64 * 1024); | ||||
109 | sd->sd_meta->ssdi_sdd_invariant.ssd_size = (coerced_size & | ||||
110 | ~(((u_int64_t)sd->sd_meta->ssdi_sdd_invariant.ssd_strip_size >> | ||||
111 | DEV_BSHIFT9) - 1)) * (no_chunk - 1); | ||||
112 | |||||
113 | return sr_raid5_init(sd); | ||||
114 | } | ||||
115 | |||||
116 | int | ||||
117 | sr_raid5_assemble(struct sr_discipline *sd, struct bioc_createraid *bc, | ||||
118 | int no_chunk, void *data) | ||||
119 | { | ||||
120 | return sr_raid5_init(sd); | ||||
121 | } | ||||
122 | |||||
123 | int | ||||
124 | sr_raid5_init(struct sr_discipline *sd) | ||||
125 | { | ||||
126 | /* Initialise runtime values. */ | ||||
127 | sd->mdssd_dis_specific.mdd_raid5.sr5_strip_bits = | ||||
128 | sr_validate_stripsize(sd->sd_meta->ssdi_sdd_invariant.ssd_strip_size); | ||||
129 | if (sd->mdssd_dis_specific.mdd_raid5.sr5_strip_bits == -1) { | ||||
130 | sr_error(sd->sd_sc, "invalid strip size"); | ||||
131 | return EINVAL22; | ||||
132 | } | ||||
133 | |||||
134 | sd->sd_max_ccb_per_wu = sd->sd_meta->ssdi_sdd_invariant.ssd_chunk_no; | ||||
135 | |||||
136 | return 0; | ||||
137 | } | ||||
138 | |||||
139 | int | ||||
140 | sr_raid5_openings(struct sr_discipline *sd) | ||||
141 | { | ||||
142 | /* Two work units per I/O, two for rebuild/scrub. */ | ||||
143 | return ((sd->sd_max_wu - 2) >> 1); | ||||
144 | } | ||||
145 | |||||
146 | void | ||||
147 | sr_raid5_set_chunk_state(struct sr_discipline *sd, int c, int new_state) | ||||
148 | { | ||||
149 | int old_state, s; | ||||
150 | |||||
151 | DNPRINTF(SR_D_STATE, "%s: %s: %s: sr_raid_set_chunk_state %d -> %d\n", | ||||
152 | DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, | ||||
153 | sd->sd_vol.sv_chunks[c]->src_meta.scmi.scm_devname, c, new_state); | ||||
154 | |||||
155 | /* ok to go to splbio since this only happens in error path */ | ||||
156 | s = splbio()splraise(0x3); | ||||
157 | old_state = sd->sd_vol.sv_chunks[c]->src_meta.scm_status; | ||||
158 | |||||
159 | /* multiple IOs to the same chunk that fail will come through here */ | ||||
160 | if (old_state == new_state) | ||||
161 | goto done; | ||||
162 | |||||
163 | switch (old_state) { | ||||
164 | case BIOC_SDONLINE0x00: | ||||
165 | switch (new_state) { | ||||
166 | case BIOC_SDOFFLINE0x01: | ||||
167 | case BIOC_SDSCRUB0x06: | ||||
168 | break; | ||||
169 | default: | ||||
170 | goto die; | ||||
171 | } | ||||
172 | break; | ||||
173 | |||||
174 | case BIOC_SDOFFLINE0x01: | ||||
175 | if (new_state == BIOC_SDREBUILD0x03) { | ||||
176 | ; | ||||
177 | } else | ||||
178 | goto die; | ||||
179 | break; | ||||
180 | |||||
181 | case BIOC_SDSCRUB0x06: | ||||
182 | switch (new_state) { | ||||
183 | case BIOC_SDONLINE0x00: | ||||
184 | case BIOC_SDOFFLINE0x01: | ||||
185 | break; | ||||
186 | default: | ||||
187 | goto die; | ||||
188 | } | ||||
189 | break; | ||||
190 | |||||
191 | case BIOC_SDREBUILD0x03: | ||||
192 | switch (new_state) { | ||||
193 | case BIOC_SDONLINE0x00: | ||||
194 | case BIOC_SDOFFLINE0x01: | ||||
195 | break; | ||||
196 | default: | ||||
197 | goto die; | ||||
198 | } | ||||
199 | break; | ||||
200 | |||||
201 | default: | ||||
202 | die: | ||||
203 | splx(s)spllower(s); /* XXX */ | ||||
204 | panic("%s: %s: %s: invalid chunk state transition %d -> %d", | ||||
205 | DEVNAME(sd->sd_sc)((sd->sd_sc)->sc_dev.dv_xname), | ||||
206 | sd->sd_meta->ssd_devname, | ||||
207 | sd->sd_vol.sv_chunks[c]->src_meta.scmi_scm_invariant.scm_devname, | ||||
208 | old_state, new_state); | ||||
209 | /* NOTREACHED */ | ||||
210 | } | ||||
211 | |||||
212 | sd->sd_vol.sv_chunks[c]->src_meta.scm_status = new_state; | ||||
213 | sd->sd_set_vol_state(sd); | ||||
214 | |||||
215 | sd->sd_must_flush = 1; | ||||
216 | task_add(systq, &sd->sd_meta_save_task); | ||||
217 | done: | ||||
218 | splx(s)spllower(s); | ||||
219 | } | ||||
220 | |||||
221 | void | ||||
222 | sr_raid5_set_vol_state(struct sr_discipline *sd) | ||||
223 | { | ||||
224 | int states[SR_MAX_STATES7]; | ||||
225 | int new_state, i, s, nd; | ||||
226 | int old_state = sd->sd_vol_status; | ||||
227 | |||||
228 | DNPRINTF(SR_D_STATE, "%s: %s: sr_raid_set_vol_state\n", | ||||
229 | DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname); | ||||
230 | |||||
231 | nd = sd->sd_meta->ssdi_sdd_invariant.ssd_chunk_no; | ||||
232 | |||||
233 | for (i = 0; i < SR_MAX_STATES7; i++) | ||||
234 | states[i] = 0; | ||||
235 | |||||
236 | for (i = 0; i < nd; i++) { | ||||
237 | s = sd->sd_vol.sv_chunks[i]->src_meta.scm_status; | ||||
238 | if (s >= SR_MAX_STATES7) | ||||
239 | panic("%s: %s: %s: invalid chunk state", | ||||
240 | DEVNAME(sd->sd_sc)((sd->sd_sc)->sc_dev.dv_xname), | ||||
241 | sd->sd_meta->ssd_devname, | ||||
242 | sd->sd_vol.sv_chunks[i]->src_meta.scmi_scm_invariant.scm_devname); | ||||
243 | states[s]++; | ||||
244 | } | ||||
245 | |||||
246 | if (states[BIOC_SDONLINE0x00] == nd) | ||||
247 | new_state = BIOC_SVONLINE0x00; | ||||
248 | else if (states[BIOC_SDONLINE0x00] < nd - 1) | ||||
249 | new_state = BIOC_SVOFFLINE0x01; | ||||
250 | else if (states[BIOC_SDSCRUB0x06] != 0) | ||||
251 | new_state = BIOC_SVSCRUB0x04; | ||||
252 | else if (states[BIOC_SDREBUILD0x03] != 0) | ||||
253 | new_state = BIOC_SVREBUILD0x05; | ||||
254 | else if (states[BIOC_SDONLINE0x00] == nd - 1) | ||||
255 | new_state = BIOC_SVDEGRADED0x02; | ||||
256 | else { | ||||
257 | #ifdef SR_DEBUG | ||||
258 | DNPRINTF(SR_D_STATE, "%s: invalid volume state, old state " | ||||
259 | "was %d\n", DEVNAME(sd->sd_sc), old_state); | ||||
260 | for (i = 0; i < nd; i++) | ||||
261 | DNPRINTF(SR_D_STATE, "%s: chunk %d status = %d\n", | ||||
262 | DEVNAME(sd->sd_sc), i, | ||||
263 | sd->sd_vol.sv_chunks[i]->src_meta.scm_status); | ||||
264 | #endif | ||||
265 | panic("invalid volume state"); | ||||
266 | } | ||||
267 | |||||
268 | DNPRINTF(SR_D_STATE, "%s: %s: sr_raid5_set_vol_state %d -> %d\n", | ||||
269 | DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, | ||||
270 | old_state, new_state); | ||||
271 | |||||
272 | switch (old_state) { | ||||
273 | case BIOC_SVONLINE0x00: | ||||
274 | switch (new_state) { | ||||
275 | case BIOC_SVONLINE0x00: /* can go to same state */ | ||||
276 | case BIOC_SVOFFLINE0x01: | ||||
277 | case BIOC_SVDEGRADED0x02: | ||||
278 | case BIOC_SVREBUILD0x05: /* happens on boot */ | ||||
279 | break; | ||||
280 | default: | ||||
281 | goto die; | ||||
282 | } | ||||
283 | break; | ||||
284 | |||||
285 | case BIOC_SVOFFLINE0x01: | ||||
286 | /* XXX this might be a little too much */ | ||||
287 | goto die; | ||||
288 | |||||
289 | case BIOC_SVDEGRADED0x02: | ||||
290 | switch (new_state) { | ||||
291 | case BIOC_SVOFFLINE0x01: | ||||
292 | case BIOC_SVREBUILD0x05: | ||||
293 | case BIOC_SVDEGRADED0x02: /* can go to the same state */ | ||||
294 | break; | ||||
295 | default: | ||||
296 | goto die; | ||||
297 | } | ||||
298 | break; | ||||
299 | |||||
300 | case BIOC_SVBUILDING0x03: | ||||
301 | switch (new_state) { | ||||
302 | case BIOC_SVONLINE0x00: | ||||
303 | case BIOC_SVOFFLINE0x01: | ||||
304 | case BIOC_SVBUILDING0x03: /* can go to the same state */ | ||||
305 | break; | ||||
306 | default: | ||||
307 | goto die; | ||||
308 | } | ||||
309 | break; | ||||
310 | |||||
311 | case BIOC_SVSCRUB0x04: | ||||
312 | switch (new_state) { | ||||
313 | case BIOC_SVONLINE0x00: | ||||
314 | case BIOC_SVOFFLINE0x01: | ||||
315 | case BIOC_SVDEGRADED0x02: | ||||
316 | case BIOC_SVSCRUB0x04: /* can go to same state */ | ||||
317 | break; | ||||
318 | default: | ||||
319 | goto die; | ||||
320 | } | ||||
321 | break; | ||||
322 | |||||
323 | case BIOC_SVREBUILD0x05: | ||||
324 | switch (new_state) { | ||||
325 | case BIOC_SVONLINE0x00: | ||||
326 | case BIOC_SVOFFLINE0x01: | ||||
327 | case BIOC_SVDEGRADED0x02: | ||||
328 | case BIOC_SVREBUILD0x05: /* can go to the same state */ | ||||
329 | break; | ||||
330 | default: | ||||
331 | goto die; | ||||
332 | } | ||||
333 | break; | ||||
334 | |||||
335 | default: | ||||
336 | die: | ||||
337 | panic("%s: %s: invalid volume state transition %d -> %d", | ||||
338 | DEVNAME(sd->sd_sc)((sd->sd_sc)->sc_dev.dv_xname), sd->sd_meta->ssd_devname, | ||||
339 | old_state, new_state); | ||||
340 | /* NOTREACHED */ | ||||
341 | } | ||||
342 | |||||
343 | sd->sd_vol_status = new_state; | ||||
344 | } | ||||
345 | |||||
346 | static inline int | ||||
347 | sr_raid5_chunk_online(struct sr_discipline *sd, int chunk) | ||||
348 | { | ||||
349 | switch (sd->sd_vol.sv_chunks[chunk]->src_meta.scm_status) { | ||||
350 | case BIOC_SDONLINE0x00: | ||||
351 | case BIOC_SDSCRUB0x06: | ||||
352 | return 1; | ||||
353 | default: | ||||
354 | return 0; | ||||
355 | } | ||||
356 | } | ||||
357 | |||||
358 | static inline int | ||||
359 | sr_raid5_chunk_rebuild(struct sr_discipline *sd, int chunk) | ||||
360 | { | ||||
361 | switch (sd->sd_vol.sv_chunks[chunk]->src_meta.scm_status) { | ||||
362 | case BIOC_SDREBUILD0x03: | ||||
363 | return 1; | ||||
364 | default: | ||||
365 | return 0; | ||||
366 | } | ||||
367 | } | ||||
368 | |||||
369 | int | ||||
370 | sr_raid5_rw(struct sr_workunit *wu) | ||||
371 | { | ||||
372 | struct sr_workunit *wu_r = NULL((void *)0); | ||||
| |||||
373 | struct sr_discipline *sd = wu->swu_dis; | ||||
374 | struct scsi_xfer *xs = wu->swu_xs; | ||||
375 | struct sr_chunk *scp; | ||||
376 | daddr_t blkno, lba; | ||||
377 | int64_t chunk_offs, lbaoffs, offset, strip_offs; | ||||
378 | int64_t strip_bits, strip_no, strip_size; | ||||
379 | int64_t chunk, no_chunk; | ||||
380 | int64_t parity, row_size; | ||||
381 | long length, datalen; | ||||
382 | void *data; | ||||
383 | int s; | ||||
384 | |||||
385 | /* blkno and scsi error will be handled by sr_validate_io */ | ||||
386 | if (sr_validate_io(wu, &blkno, "sr_raid5_rw")) | ||||
387 | goto bad; | ||||
388 | |||||
389 | DNPRINTF(SR_D_DIS, "%s: %s sr_raid5_rw %s: blkno %lld size %d\n", | ||||
390 | DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, | ||||
391 | (xs->flags & SCSI_DATA_IN) ? "read" : "write", | ||||
392 | (long long)blkno, xs->datalen); | ||||
393 | |||||
394 | strip_size = sd->sd_meta->ssdi_sdd_invariant.ssd_strip_size; | ||||
395 | strip_bits = sd->mdssd_dis_specific.mdd_raid5.sr5_strip_bits; | ||||
396 | no_chunk = sd->sd_meta->ssdi_sdd_invariant.ssd_chunk_no - 1; | ||||
397 | row_size = (no_chunk << strip_bits) >> DEV_BSHIFT9; | ||||
398 | |||||
399 | data = xs->data; | ||||
400 | datalen = xs->datalen; | ||||
401 | lbaoffs = blkno << DEV_BSHIFT9; | ||||
402 | |||||
403 | if (xs->flags & SCSI_DATA_OUT0x01000) { | ||||
404 | if ((wu_r = sr_scsi_wu_get(sd, SCSI_NOSLEEP0x00001)) == NULL((void *)0)){ | ||||
405 | printf("%s: %s failed to get read work unit", | ||||
406 | DEVNAME(sd->sd_sc)((sd->sd_sc)->sc_dev.dv_xname), sd->sd_meta->ssd_devname); | ||||
407 | goto bad; | ||||
408 | } | ||||
409 | wu_r->swu_state = SR_WU_INPROGRESS1; | ||||
410 | wu_r->swu_flags |= SR_WUF_DISCIPLINE(1<<5); | ||||
411 | } | ||||
412 | |||||
413 | wu->swu_blk_start = 0; | ||||
414 | while (datalen != 0) { | ||||
415 | strip_no = lbaoffs >> strip_bits; | ||||
416 | strip_offs = lbaoffs & (strip_size - 1); | ||||
417 | chunk_offs = (strip_no / no_chunk) << strip_bits; | ||||
418 | offset = chunk_offs + strip_offs; | ||||
419 | |||||
420 | /* get size remaining in this stripe */ | ||||
421 | length = MIN(strip_size - strip_offs, datalen)(((strip_size - strip_offs)<(datalen))?(strip_size - strip_offs ):(datalen)); | ||||
422 | |||||
423 | /* | ||||
424 | * Map disk offset to data and parity chunks, using a left | ||||
425 | * asymmetric algorithm for the parity assignment. | ||||
426 | */ | ||||
427 | chunk = strip_no % no_chunk; | ||||
428 | parity = no_chunk - ((strip_no / no_chunk) % (no_chunk + 1)); | ||||
429 | if (chunk >= parity) | ||||
430 | chunk++; | ||||
431 | |||||
432 | lba = offset >> DEV_BSHIFT9; | ||||
433 | |||||
434 | /* XXX big hammer.. exclude I/O from entire stripe */ | ||||
435 | if (wu->swu_blk_start
| ||||
436 | wu->swu_blk_start = (strip_no / no_chunk) * row_size; | ||||
437 | wu->swu_blk_end = (strip_no / no_chunk) * row_size + | ||||
438 | (row_size - 1); | ||||
439 | |||||
440 | scp = sd->sd_vol.sv_chunks[chunk]; | ||||
441 | if (xs->flags & SCSI_DATA_IN0x00800) { | ||||
442 | switch (scp->src_meta.scm_status) { | ||||
443 | case BIOC_SDONLINE0x00: | ||||
444 | case BIOC_SDSCRUB0x06: | ||||
445 | /* | ||||
446 | * Chunk is online, issue a single read | ||||
447 | * request. | ||||
448 | */ | ||||
449 | if (sr_raid5_addio(wu, chunk, lba, length, | ||||
450 | data, xs->flags, 0, NULL((void *)0))) | ||||
451 | goto bad; | ||||
452 | break; | ||||
453 | case BIOC_SDOFFLINE0x01: | ||||
454 | case BIOC_SDREBUILD0x03: | ||||
455 | case BIOC_SDHOTSPARE0x04: | ||||
456 | if (sr_raid5_regenerate(wu, chunk, lba, | ||||
457 | length, data)) | ||||
458 | goto bad; | ||||
459 | break; | ||||
460 | default: | ||||
461 | printf("%s: is offline, can't read\n", | ||||
462 | DEVNAME(sd->sd_sc)((sd->sd_sc)->sc_dev.dv_xname)); | ||||
463 | goto bad; | ||||
464 | } | ||||
465 | } else { | ||||
466 | if (sr_raid5_write(wu, wu_r, chunk, parity, lba, | ||||
467 | length, data, xs->flags, 0)) | ||||
468 | goto bad; | ||||
469 | } | ||||
470 | |||||
471 | /* advance to next block */ | ||||
472 | lbaoffs += length; | ||||
473 | datalen -= length; | ||||
474 | data += length; | ||||
475 | } | ||||
476 | |||||
477 | s = splbio()splraise(0x3); | ||||
478 | if (wu_r) { | ||||
479 | if (wu_r->swu_io_count > 0) { | ||||
480 | /* collide write request with reads */ | ||||
481 | wu_r->swu_blk_start = wu->swu_blk_start; | ||||
482 | wu_r->swu_blk_end = wu->swu_blk_end; | ||||
483 | |||||
484 | wu->swu_state = SR_WU_DEFERRED5; | ||||
485 | wu_r->swu_collider = wu; | ||||
486 | TAILQ_INSERT_TAIL(&sd->sd_wu_defq, wu, swu_link)do { (wu)->swu_link.tqe_next = ((void *)0); (wu)->swu_link .tqe_prev = (&sd->sd_wu_defq)->tqh_last; *(&sd-> sd_wu_defq)->tqh_last = (wu); (&sd->sd_wu_defq)-> tqh_last = &(wu)->swu_link.tqe_next; } while (0); | ||||
487 | |||||
488 | wu = wu_r; | ||||
489 | } else { | ||||
490 | sr_scsi_wu_put(sd, wu_r); | ||||
491 | } | ||||
492 | } | ||||
493 | splx(s)spllower(s); | ||||
494 | |||||
495 | sr_schedule_wu(wu); | ||||
496 | |||||
497 | return (0); | ||||
498 | |||||
499 | bad: | ||||
500 | /* wu is unwound by sr_wu_put */ | ||||
501 | if (wu_r) | ||||
502 | sr_scsi_wu_put(sd, wu_r); | ||||
503 | return (1); | ||||
504 | } | ||||
505 | |||||
506 | int | ||||
507 | sr_raid5_regenerate(struct sr_workunit *wu, int chunk, daddr_t blkno, | ||||
508 | long len, void *data) | ||||
509 | { | ||||
510 | struct sr_discipline *sd = wu->swu_dis; | ||||
511 | int i; | ||||
512 | |||||
513 | /* | ||||
514 | * Regenerate a block on a RAID 5 volume by xoring the data and parity | ||||
515 | * from all of the remaining online chunks. This requires the parity | ||||
516 | * to already be correct. | ||||
517 | */ | ||||
518 | |||||
519 | DNPRINTF(SR_D_DIS, "%s: %s sr_raid5_regenerate chunk %d offline, " | ||||
520 | "regenerating block %llu\n", | ||||
521 | DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, chunk, blkno); | ||||
522 | |||||
523 | memset(data, 0, len)__builtin_memset((data), (0), (len)); | ||||
524 | for (i = 0; i < sd->sd_meta->ssdi_sdd_invariant.ssd_chunk_no; i++) { | ||||
525 | if (i == chunk) | ||||
526 | continue; | ||||
527 | if (!sr_raid5_chunk_online(sd, i)) | ||||
528 | goto bad; | ||||
529 | if (sr_raid5_addio(wu, i, blkno, len, NULL((void *)0), SCSI_DATA_IN0x00800, | ||||
530 | 0, data)) | ||||
531 | goto bad; | ||||
532 | } | ||||
533 | return (0); | ||||
534 | |||||
535 | bad: | ||||
536 | return (1); | ||||
537 | } | ||||
538 | |||||
539 | int | ||||
540 | sr_raid5_write(struct sr_workunit *wu, struct sr_workunit *wu_r, int chunk, | ||||
541 | int parity, daddr_t blkno, long len, void *data, int xsflags, | ||||
542 | int ccbflags) | ||||
543 | { | ||||
544 | struct sr_discipline *sd = wu->swu_dis; | ||||
545 | struct scsi_xfer *xs = wu->swu_xs; | ||||
546 | void *xorbuf; | ||||
547 | int chunk_online, chunk_rebuild; | ||||
548 | int parity_online, parity_rebuild; | ||||
549 | int other_offline = 0, other_rebuild = 0; | ||||
550 | int i; | ||||
551 | |||||
552 | /* | ||||
553 | * Perform a write to a RAID 5 volume. This write routine does not | ||||
554 | * require the parity to already be correct and will operate on a | ||||
555 | * uninitialised volume. | ||||
556 | * | ||||
557 | * There are four possible cases: | ||||
558 | * | ||||
559 | * 1) All data chunks and parity are online. In this case we read the | ||||
560 | * data from all data chunks, except the one we are writing to, in | ||||
561 | * order to calculate and write the new parity. | ||||
562 | * | ||||
563 | * 2) The parity chunk is offline. In this case we only need to write | ||||
564 | * to the data chunk. No parity calculation is required. | ||||
565 | * | ||||
566 | * 3) The data chunk is offline. In this case we read the data from all | ||||
567 | * online chunks in order to calculate and write the new parity. | ||||
568 | * This is the same as (1) except we do not write the data chunk. | ||||
569 | * | ||||
570 | * 4) A different data chunk is offline. The new parity is calculated | ||||
571 | * by taking the existing parity, xoring the original data and | ||||
572 | * xoring in the new data. This requires that the parity already be | ||||
573 | * correct, which it will be if any of the data chunks has | ||||
574 | * previously been written. | ||||
575 | * | ||||
576 | * There is an additional complication introduced by a chunk that is | ||||
577 | * being rebuilt. If this is the data or parity chunk, then we want | ||||
578 | * to write to it as per normal. If it is another data chunk then we | ||||
579 | * need to presume that it has not yet been regenerated and use the | ||||
580 | * same method as detailed in (4) above. | ||||
581 | */ | ||||
582 | |||||
583 | DNPRINTF(SR_D_DIS, "%s: %s sr_raid5_write chunk %i parity %i " | ||||
584 | "blkno %llu\n", DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, | ||||
585 | chunk, parity, (unsigned long long)blkno); | ||||
586 | |||||
587 | chunk_online = sr_raid5_chunk_online(sd, chunk); | ||||
588 | chunk_rebuild = sr_raid5_chunk_rebuild(sd, chunk); | ||||
589 | parity_online = sr_raid5_chunk_online(sd, parity); | ||||
590 | parity_rebuild = sr_raid5_chunk_rebuild(sd, parity); | ||||
591 | |||||
592 | for (i = 0; i < sd->sd_meta->ssdi_sdd_invariant.ssd_chunk_no; i++) { | ||||
593 | if (i == chunk || i == parity) | ||||
594 | continue; | ||||
595 | if (sr_raid5_chunk_rebuild(sd, i)) | ||||
596 | other_rebuild = 1; | ||||
597 | else if (!sr_raid5_chunk_online(sd, i)) | ||||
598 | other_offline = 1; | ||||
599 | } | ||||
600 | |||||
601 | DNPRINTF(SR_D_DIS, "%s: %s chunk online %d, parity online %d, " | ||||
602 | "other offline %d\n", DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, | ||||
603 | chunk_online, parity_online, other_offline); | ||||
604 | |||||
605 | if (!parity_online
| ||||
606 | goto data_write; | ||||
607 | |||||
608 | xorbuf = sr_block_get(sd, len); | ||||
609 | if (xorbuf == NULL((void *)0)) | ||||
610 | goto bad; | ||||
611 | memcpy(xorbuf, data, len)__builtin_memcpy((xorbuf), (data), (len)); | ||||
612 | |||||
613 | if (other_offline
| ||||
614 | |||||
615 | /* | ||||
616 | * XXX - If we can guarantee that this LBA has been scrubbed | ||||
617 | * then we can also take this faster path. | ||||
618 | */ | ||||
619 | |||||
620 | /* Read in existing data and existing parity. */ | ||||
621 | if (sr_raid5_addio(wu_r, chunk, blkno, len, NULL((void *)0), | ||||
622 | SCSI_DATA_IN0x00800, 0, xorbuf)) | ||||
623 | goto bad; | ||||
624 | if (sr_raid5_addio(wu_r, parity, blkno, len, NULL((void *)0), | ||||
625 | SCSI_DATA_IN0x00800, 0, xorbuf)) | ||||
626 | goto bad; | ||||
627 | |||||
628 | } else { | ||||
629 | |||||
630 | /* Read in existing data from all other chunks. */ | ||||
631 | for (i = 0; i < sd->sd_meta->ssdi_sdd_invariant.ssd_chunk_no; i++) { | ||||
632 | if (i == chunk || i == parity) | ||||
633 | continue; | ||||
634 | if (sr_raid5_addio(wu_r, i, blkno, len, NULL((void *)0), | ||||
635 | SCSI_DATA_IN0x00800, 0, xorbuf)) | ||||
636 | goto bad; | ||||
637 | } | ||||
638 | |||||
639 | } | ||||
640 | |||||
641 | /* Write new parity. */ | ||||
642 | if (sr_raid5_addio(wu, parity, blkno, len, xorbuf, xs->flags, | ||||
643 | SR_CCBF_FREEBUF(1<<0), NULL((void *)0))) | ||||
644 | goto bad; | ||||
645 | |||||
646 | data_write: | ||||
647 | /* Write new data. */ | ||||
648 | if (chunk_online || chunk_rebuild) | ||||
649 | if (sr_raid5_addio(wu, chunk, blkno, len, data, xs->flags, | ||||
650 | 0, NULL((void *)0))) | ||||
651 | goto bad; | ||||
652 | |||||
653 | return (0); | ||||
654 | |||||
655 | bad: | ||||
656 | return (1); | ||||
657 | } | ||||
658 | |||||
659 | void | ||||
660 | sr_raid5_intr(struct buf *bp) | ||||
661 | { | ||||
662 | struct sr_ccb *ccb = (struct sr_ccb *)bp; | ||||
663 | struct sr_workunit *wu = ccb->ccb_wu; | ||||
664 | struct sr_discipline *sd = wu->swu_dis; | ||||
665 | int s; | ||||
666 | |||||
667 | DNPRINTF(SR_D_INTR, "%s: sr_raid5_intr bp %p xs %p\n", | ||||
668 | DEVNAME(sd->sd_sc), bp, wu->swu_xs); | ||||
669 | |||||
670 | s = splbio()splraise(0x3); | ||||
671 | sr_ccb_done(ccb); | ||||
672 | |||||
673 | /* XXX - Should this be done via the taskq? */ | ||||
674 | |||||
675 | /* XOR data to result. */ | ||||
676 | if (ccb->ccb_state == SR_CCB_OK2 && ccb->ccb_opaque) | ||||
677 | sr_raid5_xor(ccb->ccb_opaque, ccb->ccb_buf.b_data, | ||||
678 | ccb->ccb_buf.b_bcount); | ||||
679 | |||||
680 | /* Free allocated data buffer. */ | ||||
681 | if (ccb->ccb_flags & SR_CCBF_FREEBUF(1<<0)) { | ||||
682 | sr_block_put(sd, ccb->ccb_buf.b_data, ccb->ccb_buf.b_bcount); | ||||
683 | ccb->ccb_buf.b_data = NULL((void *)0); | ||||
684 | } | ||||
685 | |||||
686 | sr_wu_done(wu); | ||||
687 | splx(s)spllower(s); | ||||
688 | } | ||||
689 | |||||
690 | int | ||||
691 | sr_raid5_wu_done(struct sr_workunit *wu) | ||||
692 | { | ||||
693 | struct sr_discipline *sd = wu->swu_dis; | ||||
694 | struct scsi_xfer *xs = wu->swu_xs; | ||||
695 | |||||
696 | /* XXX - we have no way of propagating errors... */ | ||||
697 | if (wu->swu_flags & (SR_WUF_DISCIPLINE(1<<5) | SR_WUF_REBUILD(1<<0))) | ||||
698 | return SR_WU_OK2; | ||||
699 | |||||
700 | /* XXX - This is insufficient for RAID 5. */ | ||||
701 | if (wu->swu_ios_succeeded > 0) { | ||||
702 | xs->error = XS_NOERROR0; | ||||
703 | return SR_WU_OK2; | ||||
704 | } | ||||
705 | |||||
706 | if (xs->flags & SCSI_DATA_IN0x00800) { | ||||
707 | printf("%s: retrying read on block %lld\n", | ||||
708 | sd->sd_meta->ssd_devname, (long long)wu->swu_blk_start); | ||||
709 | sr_wu_release_ccbs(wu); | ||||
710 | wu->swu_state = SR_WU_RESTART7; | ||||
711 | if (sd->sd_scsi_rw(wu) == 0) | ||||
712 | return SR_WU_RESTART7; | ||||
713 | } else { | ||||
714 | /* XXX - retry write if we just went from online to degraded. */ | ||||
715 | printf("%s: permanently fail write on block %lld\n", | ||||
716 | sd->sd_meta->ssd_devname, (long long)wu->swu_blk_start); | ||||
717 | } | ||||
718 | |||||
719 | wu->swu_state = SR_WU_FAILED3; | ||||
720 | xs->error = XS_DRIVER_STUFFUP2; | ||||
721 | |||||
722 | return SR_WU_FAILED3; | ||||
723 | } | ||||
724 | |||||
725 | int | ||||
726 | sr_raid5_addio(struct sr_workunit *wu, int chunk, daddr_t blkno, | ||||
727 | long len, void *data, int xsflags, int ccbflags, void *xorbuf) | ||||
728 | { | ||||
729 | struct sr_discipline *sd = wu->swu_dis; | ||||
| |||||
730 | struct sr_ccb *ccb; | ||||
731 | |||||
732 | DNPRINTF(SR_D_DIS, "sr_raid5_addio: %s chunk %d block %lld " | ||||
733 | "length %ld %s\n", (xsflags & SCSI_DATA_IN) ? "read" : "write", | ||||
734 | chunk, (long long)blkno, len, xorbuf ? "X0R" : "-"); | ||||
735 | |||||
736 | /* Allocate temporary buffer. */ | ||||
737 | if (data == NULL((void *)0)) { | ||||
738 | data = sr_block_get(sd, len); | ||||
739 | if (data == NULL((void *)0)) | ||||
740 | return (-1); | ||||
741 | ccbflags |= SR_CCBF_FREEBUF(1<<0); | ||||
742 | } | ||||
743 | |||||
744 | ccb = sr_ccb_rw(sd, chunk, blkno, len, data, xsflags, ccbflags); | ||||
745 | if (ccb == NULL((void *)0)) { | ||||
746 | if (ccbflags & SR_CCBF_FREEBUF(1<<0)) | ||||
747 | sr_block_put(sd, data, len); | ||||
748 | return (-1); | ||||
749 | } | ||||
750 | ccb->ccb_opaque = xorbuf; | ||||
751 | sr_wu_enqueue_ccb(wu, ccb); | ||||
752 | |||||
753 | return (0); | ||||
754 | } | ||||
755 | |||||
756 | void | ||||
757 | sr_raid5_xor(void *a, void *b, int len) | ||||
758 | { | ||||
759 | uint32_t *xa = a, *xb = b; | ||||
760 | |||||
761 | len >>= 2; | ||||
762 | while (len--) | ||||
763 | *xa++ ^= *xb++; | ||||
764 | } | ||||
765 | |||||
766 | void | ||||
767 | sr_raid5_rebuild(struct sr_discipline *sd) | ||||
768 | { | ||||
769 | int64_t strip_no, strip_size, strip_bits, i, restart; | ||||
770 | int64_t chunk_count, chunk_strips, chunk_lba, chunk_size, row_size; | ||||
771 | struct sr_workunit *wu_r, *wu_w; | ||||
772 | int s, slept, percent = 0, old_percent = -1; | ||||
773 | int rebuild_chunk = -1; | ||||
774 | void *xorbuf; | ||||
775 | |||||
776 | /* Find the rebuild chunk. */ | ||||
777 | for (i = 0; i < sd->sd_meta->ssdi_sdd_invariant.ssd_chunk_no; i++) { | ||||
778 | if (sr_raid5_chunk_rebuild(sd, i)) { | ||||
779 | rebuild_chunk = i; | ||||
780 | break; | ||||
781 | } | ||||
782 | } | ||||
783 | if (rebuild_chunk == -1) | ||||
784 | goto bad; | ||||
785 | |||||
786 | strip_size = sd->sd_meta->ssdi_sdd_invariant.ssd_strip_size; | ||||
787 | strip_bits = sd->mdssd_dis_specific.mdd_raid5.sr5_strip_bits; | ||||
788 | chunk_count = sd->sd_meta->ssdi_sdd_invariant.ssd_chunk_no - 1; | ||||
789 | chunk_size = sd->sd_meta->ssdi_sdd_invariant.ssd_size / chunk_count; | ||||
790 | chunk_strips = (chunk_size << DEV_BSHIFT9) >> strip_bits; | ||||
791 | row_size = (chunk_count << strip_bits) >> DEV_BSHIFT9; | ||||
792 | |||||
793 | DNPRINTF(SR_D_REBUILD, "%s: %s sr_raid5_rebuild volume size = %lld, " | ||||
794 | "chunk count = %lld, chunk size = %lld, chunk strips = %lld, " | ||||
795 | "row size = %lld\n", DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, | ||||
796 | sd->sd_meta->ssdi.ssd_size, chunk_count, chunk_size, chunk_strips, | ||||
797 | row_size); | ||||
798 | |||||
799 | restart = sd->sd_meta->ssd_rebuild / row_size; | ||||
800 | if (restart > chunk_strips) { | ||||
801 | printf("%s: bogus rebuild restart offset, starting from 0\n", | ||||
802 | DEVNAME(sd->sd_sc)((sd->sd_sc)->sc_dev.dv_xname)); | ||||
803 | restart = 0; | ||||
804 | } | ||||
805 | if (restart != 0) { | ||||
806 | percent = sr_rebuild_percent(sd); | ||||
807 | printf("%s: resuming rebuild on %s at %d%%\n", | ||||
808 | DEVNAME(sd->sd_sc)((sd->sd_sc)->sc_dev.dv_xname), sd->sd_meta->ssd_devname, percent); | ||||
809 | } | ||||
810 | |||||
811 | for (strip_no = restart; strip_no < chunk_strips; strip_no++) { | ||||
812 | chunk_lba = (strip_size >> DEV_BSHIFT9) * strip_no; | ||||
813 | |||||
814 | DNPRINTF(SR_D_REBUILD, "%s: %s rebuild strip %lld, " | ||||
815 | "chunk lba = %lld\n", DEVNAME(sd->sd_sc), | ||||
816 | sd->sd_meta->ssd_devname, strip_no, chunk_lba); | ||||
817 | |||||
818 | wu_w = sr_scsi_wu_get(sd, 0); | ||||
819 | wu_r = sr_scsi_wu_get(sd, 0); | ||||
820 | |||||
821 | xorbuf = sr_block_get(sd, strip_size); | ||||
822 | if (xorbuf == NULL((void *)0)) | ||||
823 | goto bad; | ||||
824 | if (sr_raid5_regenerate(wu_r, rebuild_chunk, chunk_lba, | ||||
825 | strip_size, xorbuf)) | ||||
826 | goto bad; | ||||
827 | if (sr_raid5_addio(wu_w, rebuild_chunk, chunk_lba, strip_size, | ||||
828 | xorbuf, SCSI_DATA_OUT0x01000, SR_CCBF_FREEBUF(1<<0), NULL((void *)0))) | ||||
829 | goto bad; | ||||
830 | |||||
831 | /* Collide write work unit with read work unit. */ | ||||
832 | wu_r->swu_state = SR_WU_INPROGRESS1; | ||||
833 | wu_r->swu_flags |= SR_WUF_REBUILD(1<<0); | ||||
834 | wu_w->swu_state = SR_WU_DEFERRED5; | ||||
835 | wu_w->swu_flags |= SR_WUF_REBUILD(1<<0) | SR_WUF_WAKEUP(1<<4); | ||||
836 | wu_r->swu_collider = wu_w; | ||||
837 | |||||
838 | /* Block I/O to this strip while we rebuild it. */ | ||||
839 | wu_r->swu_blk_start = (strip_no / chunk_count) * row_size; | ||||
840 | wu_r->swu_blk_end = wu_r->swu_blk_start + row_size - 1; | ||||
841 | wu_w->swu_blk_start = wu_r->swu_blk_start; | ||||
842 | wu_w->swu_blk_end = wu_r->swu_blk_end; | ||||
843 | |||||
844 | DNPRINTF(SR_D_REBUILD, "%s: %s rebuild swu_blk_start = %lld, " | ||||
845 | "swu_blk_end = %lld\n", DEVNAME(sd->sd_sc), | ||||
846 | sd->sd_meta->ssd_devname, | ||||
847 | wu_r->swu_blk_start, wu_r->swu_blk_end); | ||||
848 | |||||
849 | s = splbio()splraise(0x3); | ||||
850 | TAILQ_INSERT_TAIL(&sd->sd_wu_defq, wu_w, swu_link)do { (wu_w)->swu_link.tqe_next = ((void *)0); (wu_w)->swu_link .tqe_prev = (&sd->sd_wu_defq)->tqh_last; *(&sd-> sd_wu_defq)->tqh_last = (wu_w); (&sd->sd_wu_defq)-> tqh_last = &(wu_w)->swu_link.tqe_next; } while (0); | ||||
851 | splx(s)spllower(s); | ||||
852 | |||||
853 | sr_schedule_wu(wu_r); | ||||
854 | |||||
855 | slept = 0; | ||||
856 | while ((wu_w->swu_flags & SR_WUF_REBUILDIOCOMP(1<<1)) == 0) { | ||||
857 | tsleep_nsec(wu_w, PRIBIO16, "sr_rebuild", INFSLP0xffffffffffffffffULL); | ||||
858 | slept = 1; | ||||
859 | } | ||||
860 | if (!slept) { | ||||
861 | tsleep_nsec(sd->sd_sc, PWAIT32, "sr_yield", | ||||
862 | MSEC_TO_NSEC(1)); | ||||
863 | } | ||||
864 | |||||
865 | sr_scsi_wu_put(sd, wu_r); | ||||
866 | sr_scsi_wu_put(sd, wu_w); | ||||
867 | |||||
868 | sd->sd_meta->ssd_rebuild = chunk_lba * chunk_count; | ||||
869 | |||||
870 | percent = sr_rebuild_percent(sd); | ||||
871 | if (percent != old_percent && strip_no != chunk_strips - 1) { | ||||
872 | if (sr_meta_save(sd, SR_META_DIRTY0x1)) | ||||
873 | printf("%s: could not save metadata to %s\n", | ||||
874 | DEVNAME(sd->sd_sc)((sd->sd_sc)->sc_dev.dv_xname), | ||||
875 | sd->sd_meta->ssd_devname); | ||||
876 | old_percent = percent; | ||||
877 | } | ||||
878 | |||||
879 | if (sd->sd_reb_abort) | ||||
880 | goto abort; | ||||
881 | } | ||||
882 | |||||
883 | DNPRINTF(SR_D_REBUILD, "%s: %s rebuild complete\n", DEVNAME(sd->sd_sc), | ||||
884 | sd->sd_meta->ssd_devname); | ||||
885 | |||||
886 | /* all done */ | ||||
887 | sd->sd_meta->ssd_rebuild = 0; | ||||
888 | for (i = 0; i < sd->sd_meta->ssdi_sdd_invariant.ssd_chunk_no; i++) { | ||||
889 | if (sd->sd_vol.sv_chunks[i]->src_meta.scm_status == | ||||
890 | BIOC_SDREBUILD0x03) { | ||||
891 | sd->sd_set_chunk_state(sd, i, BIOC_SDONLINE0x00); | ||||
892 | break; | ||||
893 | } | ||||
894 | } | ||||
895 | |||||
896 | return; | ||||
897 | |||||
898 | abort: | ||||
899 | if (sr_meta_save(sd, SR_META_DIRTY0x1)) | ||||
900 | printf("%s: could not save metadata to %s\n", | ||||
901 | DEVNAME(sd->sd_sc)((sd->sd_sc)->sc_dev.dv_xname), sd->sd_meta->ssd_devname); | ||||
902 | bad: | ||||
903 | return; | ||||
904 | } | ||||
905 | |||||
906 | #if 0 | ||||
907 | void | ||||
908 | sr_raid5_scrub(struct sr_discipline *sd) | ||||
909 | { | ||||
910 | int64_t strip_no, strip_size, no_chunk, parity, max_strip, strip_bits; | ||||
911 | int64_t i; | ||||
912 | struct sr_workunit *wu_r, *wu_w; | ||||
913 | int s, slept; | ||||
914 | void *xorbuf; | ||||
915 | |||||
916 | wu_w = sr_scsi_wu_get(sd, 0); | ||||
917 | wu_r = sr_scsi_wu_get(sd, 0); | ||||
918 | |||||
919 | no_chunk = sd->sd_meta->ssdi_sdd_invariant.ssd_chunk_no - 1; | ||||
920 | strip_size = sd->sd_meta->ssdi_sdd_invariant.ssd_strip_size; | ||||
921 | strip_bits = sd->mdssd_dis_specific.mdd_raid5.sr5_strip_bits; | ||||
922 | max_strip = sd->sd_meta->ssdi_sdd_invariant.ssd_size >> strip_bits; | ||||
923 | |||||
924 | for (strip_no = 0; strip_no < max_strip; strip_no++) { | ||||
925 | parity = no_chunk - ((strip_no / no_chunk) % (no_chunk + 1)); | ||||
926 | |||||
927 | xorbuf = sr_block_get(sd, strip_size); | ||||
928 | for (i = 0; i <= no_chunk; i++) { | ||||
929 | if (i != parity) | ||||
930 | sr_raid5_addio(wu_r, i, 0xBADCAFE, strip_size, | ||||
931 | NULL((void *)0), SCSI_DATA_IN0x00800, 0, xorbuf); | ||||
932 | } | ||||
933 | sr_raid5_addio(wu_w, parity, 0xBADCAFE, strip_size, xorbuf, | ||||
934 | SCSI_DATA_OUT0x01000, SR_CCBF_FREEBUF(1<<0), NULL((void *)0)); | ||||
935 | |||||
936 | wu_r->swu_flags |= SR_WUF_REBUILD(1<<0); | ||||
937 | |||||
938 | /* Collide wu_w with wu_r */ | ||||
939 | wu_w->swu_state = SR_WU_DEFERRED5; | ||||
940 | wu_w->swu_flags |= SR_WUF_REBUILD(1<<0) | SR_WUF_WAKEUP(1<<4); | ||||
941 | wu_r->swu_collider = wu_w; | ||||
942 | |||||
943 | s = splbio()splraise(0x3); | ||||
944 | TAILQ_INSERT_TAIL(&sd->sd_wu_defq, wu_w, swu_link)do { (wu_w)->swu_link.tqe_next = ((void *)0); (wu_w)->swu_link .tqe_prev = (&sd->sd_wu_defq)->tqh_last; *(&sd-> sd_wu_defq)->tqh_last = (wu_w); (&sd->sd_wu_defq)-> tqh_last = &(wu_w)->swu_link.tqe_next; } while (0); | ||||
945 | splx(s)spllower(s); | ||||
946 | |||||
947 | wu_r->swu_state = SR_WU_INPROGRESS1; | ||||
948 | sr_schedule_wu(wu_r); | ||||
949 | |||||
950 | slept = 0; | ||||
951 | while ((wu_w->swu_flags & SR_WUF_REBUILDIOCOMP(1<<1)) == 0) { | ||||
952 | tsleep_nsec(wu_w, PRIBIO16, "sr_scrub", INFSLP0xffffffffffffffffULL); | ||||
953 | slept = 1; | ||||
954 | } | ||||
955 | if (!slept) { | ||||
956 | tsleep_nsec(sd->sd_sc, PWAIT32, "sr_yield", | ||||
957 | MSEC_TO_NSEC(1)); | ||||
958 | } | ||||
959 | } | ||||
960 | } | ||||
961 | #endif |