1 /* @(#)repair.c 1.22 11/08/03 Copyright 1988-2011 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)repair.c 1.22 11/08/03 Copyright 1988-2011 J. Schilling";
6 #endif
7 /*
8 * Repair SCSI disks
9 *
10 * Copyright (c) 1988-2011 J. Schilling
11 */
12 /*
13 * The contents of this file are subject to the terms of the
14 * Common Development and Distribution License, Version 1.0 only
15 * (the "License"). You may not use this file except in compliance
16 * with the License.
17 *
18 * See the file CDDL.Schily.txt in this distribution for details.
19 * A copy of the CDDL is also available via the Internet at
20 * http://www.opensource.org/licenses/cddl1.txt
21 *
22 * When distributing Covered Code, include this CDDL HEADER in each
23 * file and include the License file CDDL.Schily.txt from this distribution.
24 */
25
26 #include <schily/stdio.h>
27 #include <schily/unistd.h>
28 #include <schily/standard.h>
29 #include <schily/signal.h>
30 #include <schily/sigset.h>
31 #include <schily/schily.h>
32 #include <schily/libport.h>
33
34 #include <scg/scgcmd.h>
35 #include <scg/scsireg.h>
36 #include <scg/scsidefs.h>
37 #include <scg/scsitransp.h>
38
39 #include "scsicmds.h"
40 #include "fmt.h"
41
42 #undef min
43 #define min(a, b) ((a) < (b) ? (a) : (b))
44 #undef max
45 #define max(a, b) ((a) > (b) ? (a) : (b))
46
47 extern char *Sbuf;
48 extern long Sbufsize;
49 extern int ask;
50 extern int ign_not_found;
51
52 LOCAL int soft_errs;
53
54 extern int autoformat;
55 extern int veri;
56 extern int wrveri;
57 extern int format_done;
58
59 extern int refresh_only;
60 extern int n_test_patterns;
61 #define NWVERI n_test_patterns
62 extern int Nveri;
63 extern int Cveri;
64 extern int CWveri;
65 extern long MAXbad;
66
67 EXPORT int verify_and_repair_disk __PR((SCSI *scgp, struct disk *dp));
68 EXPORT void verify_disk __PR((SCSI *scgp, struct disk *dp, int pass, long first, long last, long maxbad));
69 LOCAL int wr_verify __PR((SCSI *scgp, struct disk *dp,
70 long start, int count, long *bad_block,
71 int (*wv_func) (SCSI *, caddr_t, long, int, long *)));
72
73
74 LOCAL int read_old_data __PR((SCSI *scgp, long n));
75 LOCAL int refresh_block __PR((SCSI *scgp, long n));
76 LOCAL void reassign_bad_block __PR((SCSI *scgp, long n, int status));
77 LOCAL int check_stable __PR((SCSI *scgp, long n));
78 EXPORT void ext_reassign_block __PR((SCSI *scgp, long n));
79
80 LOCAL void fill_buf __PR((int n));
81 LOCAL void refill_buf __PR((void));
82
83 #define MAXVERI 100
84 int vlist[MAXVERI];
85
86 EXPORT int
verify_and_repair_disk(scgp,dp)87 verify_and_repair_disk(scgp, dp)
88 SCSI *scgp;
89 struct disk *dp;
90 {
91 int ret = 0;
92 int i;
93 int total;
94 int oldi;
95 int odef;
96 int ndef;
97
98 clear_bad();
99 odef = 0;
100
101 if (Nveri < 0) {
102 Nveri = wrveri ? NWVERI : NVERI;
103 if (wrveri)
104 Nveri *= 2;
105 }
106 if (dp->veri_time > 0)
107 printf("Estimated time: %ld minutes\n",
108 (Nveri * dp->veri_time + 30)/60);
109 prdate();
110 getstarttime();
111
112 soft_errs = 0;
113 ndef = 0;
114 oldi = 0;
115 total = 0;
116 newveri:
117 for (i = 0; i < Nveri; ) {
118 if (i > oldi)
119 oldi = i;
120 printf("Number of faultless passes: %d (up to now max: %d)\n",
121 i, oldi);
122 if (!scgp->silent) {
123 int j; for (j = 0; j < 10; j++) printf("%d: %d ", j, vlist[j]);
124 printf("\n");
125 }
126 #ifdef __old__
127 verify_disk(scgp, dp, i, 0L, -1L, 32L); /* XXX Test fuer Sony SMO */
128 verify_disk(scgp, dp, i, 0L, -1L, 127L); /* dies ist das absolute Maximum */
129 #endif
130 verify_disk(scgp, dp, i, 0L, -1L, (MAXbad <= 0L) ? 32L : MAXbad);
131 ndef += print_bad();
132
133 if (!is_ccs(scgp->dev)) {
134 int r;
135
136 r = bad_to_def(scgp);
137 if (r <= 0)
138 clear_bad();
139 if (odef + r < ndef)
140 ndef = odef + r;
141 }
142 i++;
143 if (ndef > odef)
144 break;
145 }
146 if (i < MAXVERI)
147 vlist[i]++;
148 total += i;
149
150 printf("Total of %2d defects.\n", ndef);
151 printf("Total of %2d soft errors.\n", soft_errs);
152 printf("Total of %2d verify loops done.\n", total);
153 if (ndef > odef)
154 odef = ndef;
155 else if (i == Nveri)
156 goto done;
157
158 if (!is_ccs(scgp->dev)) {
159 clear_bad();
160 if (acb_format_disk(scgp, dp, TRUE) >= 0) {
161 goto newveri;
162 }
163 } else {
164 reassign_bad(scgp);
165 clear_bad();
166 if ((!autoformat || ndef <= 10) && reformat_disk(scgp, dp)) {
167 goto newveri;
168 }
169 }
170
171 done:
172 if (ndef > 10) {
173 if (autoformat) {
174 error("A C H T U N G\n");
175 error("Diese Platte ist nicht brauchbar\n");
176 error("Bitte Entwicklung benachrichtigen\n");
177 } else {
178 error("Excessive bad blocks on disk.\n");
179 }
180 ret = 1;
181 }
182 if (soft_errs > total) {
183 if (autoformat) {
184 error("A C H T U N G\n");
185 error("Diese Platte hat zu viele soft errors\n");
186 error("Bitte Entwicklung benachrichtigen\n");
187 } else {
188 error("Excessive soft errors on disk.\n");
189 }
190 ret = 1;
191 }
192 return (ret);
193 }
194
195 EXPORT void
verify_disk(scgp,dp,pass,first,last,maxbad)196 verify_disk(scgp, dp, pass, first, last, maxbad)
197 SCSI *scgp;
198 struct disk *dp;
199 int pass;
200 long first;
201 long last;
202 long maxbad;
203 {
204 long bad_block;
205 long bb2;
206 long start = first;
207 long end;
208 int count;
209 long maxcount;
210 int i;
211 long bads = 0L;
212 int ret;
213 BOOL do_wrveri;
214 int (*wv_func) __PR((SCSI *, caddr_t, long, int, long *));
215 static BOOL force_wrveri = FALSE;
216
217 if (Cveri < 0)
218 Cveri = CVERI;
219 if (CWveri < 0)
220 CWveri = CWVERI;
221 maxcount = Cveri;
222
223 if (wrveri && !format_done && !force_wrveri) {
224 printf("WARNING: Disk has not been formatted.\n");
225 printf("WARNING: Write/verify will destroy disk data.\n");
226 force_wrveri = yes("Do you want to force write/verify? ");
227 if (force_wrveri) {
228 read_sinfo(scgp, dp, FALSE);
229 if (dp->formatted < 0) {
230 testformat(scgp, dp);
231 read_primary_label(scgp, dp);
232 }
233 }
234 }
235
236 if (force_wrveri)
237 do_wrveri = (pass & 1) == 0;
238 else
239 do_wrveri = wrveri && format_done && (pass & 1);
240
241 if (veri) {
242 prdate();
243 getstarttime();
244 }
245 if (read_capacity(scgp) < 0) {
246 error("Cannot read Capacity\n");
247 return;
248 }
249 end = scgp->cap->c_baddr;
250 if (last > 0 && last < end)
251 end = last;
252 if (start > end)
253 start = first = 0L;
254 if (do_wrveri) {
255 maxcount = Sbufsize/scgp->cap->c_bsize;
256 if (maxcount > CWveri)
257 maxcount = CWveri;
258 maxcount /= 10;
259 maxcount *= 10;
260 fill_buf(pass>>1);
261 printf("Verify pass %d Test Pattern: %lX\n",
262 pass, *(long *)Sbuf);
263 if (dp->split_wv_cmd > 0)
264 wv_func = write_verify_split;
265 else
266 wv_func = write_verify;
267
268 } else {
269 /* printf("no write\n");*/
270 /* printf("\n");*/
271 wv_func = write_verify; /* Tell lint: it's initialized */
272 }
273
274 /* for(start = 0; start < end; ) {*/
275 while (start <= end) {
276 printf("\r%ld", start); flush();
277 if (scgp->dev == DEV_ACB40X0 ||
278 scgp->dev == DEV_ACB4000 || scgp->dev == DEV_ACB4010 || scgp->dev == DEV_ACB4070)
279 xdelay(); /*usleep(10000);*/ /* allow other procs*/
280 /* to run if disre */
281 /* not enabled */
282
283 count = maxcount;
284 count -= start % maxcount;
285 count = min(count, end - start + 1);
286 wait_unit_ready(scgp, 100);
287 if (do_wrveri)
288 ret = wr_verify(scgp, dp, start, count, &bad_block, wv_func);
289 else
290 ret = verify(scgp, start, count, &bad_block);
291 if (scgp->debug)
292 error("SCG->error: %d\n", scgp->scmd->error);
293 if (ret < 0) {
294 if (scgp->scmd->error <= SCG_RETRYABLE ||
295 scgp->scmd->error == SCG_TIMEOUT) {
296 printf("\rBad Block # %ld found at: %ld\n", bads+1, bad_block);
297 if (!scgp->scmd->sense.adr_val) {
298 printf("Not valid!!! start: %ld\n",
299 start);
300
301 wait_unit_ready(scgp, 100);
302 if (verify(scgp, start, 1, &bad_block) >= 0 ||
303 !scgp->scmd->sense.adr_val) {
304
305 /*
306 * Wer weis, was da fuer Schrott
307 * kommt ??? vielleicht 0 !
308 * Darum sicher ist sicher !!
309 */
310 start++;
311 continue;
312 }
313 }
314 if ((ign_not_found & scg_sense_code(scgp)) == 0x14) {
315 /* error("Record not found: %ld\n",*/
316 printf("Record not found: %ld\n",
317 bad_block);
318 if (bad_block < start)
319 start++;
320 else
321 start = bad_block + 1;
322 continue;
323 }
324 /* Wait for settle down */
325 usleep(200000);
326 wait_unit_ready(scgp, 100);
327 if (scgp->scmd->error == SCG_TIMEOUT) {
328 error("TIMEOUT, sleeping for drive to calm dowm");
329 sleep(20);
330 }
331 scgp->silent++;
332 for (i = 0; i < 16; i++)
333 if (verify(scgp, bad_block, 1, &bb2) < 0)
334 break;
335 scgp->silent--;
336 if (scgp->scmd->error == SCG_TIMEOUT) {
337 sleep(20);
338 }
339 /*error("i : %d\n", i);*/
340 if (i < 16) {
341 insert_bad(bad_block);
342 #ifdef OLD
343 /*XXX*/ if (maxbad > 0 && ++bads >= maxbad)
344 #else
345 bads++;
346 /*XXX*/ if (maxbad > 0 && bads >= maxbad)
347 #endif
348 return;
349 } else {
350 /* error("SOFT ERROR !!!! at: %ld\n",*/
351 printf("SOFT ERROR !!!! at: %ld\n",
352 bad_block);
353 soft_errs++;
354 }
355 if (bad_block < start)
356 start++;
357 else
358 start = bad_block + 1;
359 continue;
360 }
361 scg_printerr(scgp);
362 if (scgp->debug) {
363 error("SCG->error: %d\n", scgp->scmd->error);
364 error("RETURN\n");
365 }
366 return;
367 }
368 start += count;
369 }
370 printf("\r%ld\nVerify done.\n", start);
371 if (veri) {
372 int nsec;
373
374 nsec = prstats();
375 printf("Verify speed: %.1f kB/sec\n",
376 ((start - first)/(1024.0/scgp->cap->c_bsize)) / nsec);
377 }
378 }
379
380
381 /*---------------------------------------------------------------------------
382 |
383 | Darf nur aufgerufen werden, wenn die Platte formatiert ist, sonst
384 | sind eventuell das Label sowie sinfo nicht initialisiert,
385 | sowie wie moeglicherweise wichtige Daten auf der Platte.
386 |
387 +---------------------------------------------------------------------------*/
388 LOCAL int
wr_verify(scgp,dp,start,count,bad_block,wv_func)389 wr_verify(scgp, dp, start, count, bad_block, wv_func)
390 SCSI *scgp;
391 struct disk *dp;
392 long start;
393 int count;
394 long *bad_block;
395 int (*wv_func) __PR((SCSI *, caddr_t, long, int, long *));
396 {
397 struct scg_cmd cmdsave;
398 int ret;
399 sigset_t oldmask;
400
401 block_sigs(oldmask);
402 ret = (*wv_func)(scgp, (caddr_t)Sbuf, start, count, bad_block);
403 if (start == 0 || (start + count) >= scgp->cap->c_baddr) {
404 movebytes((caddr_t)scgp->scmd, (caddr_t)&cmdsave, sizeof (cmdsave));
405 scgp->silent++;
406 write_sinfo(scgp, dp);
407 label_disk(scgp, dp);
408 convert_def_blk(scgp);
409 write_def_blk(scgp, FALSE);
410 scgp->silent--;
411 movebytes((caddr_t)&cmdsave, (caddr_t)scgp->scmd, sizeof (cmdsave));
412 refill_buf(); /* Solange 'Sbuf' vielfach benutzt wird */
413 }
414 restore_sigs(oldmask);
415 return (ret);
416 }
417
418 #define RECOVERED 0x01
419 #define READ_OK 0x02
420 #define REFRESHED 0x04
421 #define UNSTABLE 0x08
422
423 LOCAL int
read_old_data(scgp,n)424 read_old_data(scgp, n)
425 SCSI *scgp;
426 long n;
427 {
428 int ret = 0;
429 int i;
430
431 for (i = 0; i < 100; i++) {
432 if (read_scsi(scgp, Sbuf, n, 1) >= 0 && scg_getresid(scgp) == 0) {
433 ret |= READ_OK;
434 break;
435 } else {
436 if (scg_sense_key(scgp) == SC_RECOVERABLE_ERROR &&
437 scg_getresid(scgp) == 0) {
438 ret |= RECOVERED;
439 break;
440 }
441 }
442 }
443 if (i < 100) {
444 printf("successful%s (%d).\n", (ret & RECOVERED) ?
445 " recovered" : "", i);
446 if (ret & RECOVERED) {
447 printf("\n");
448 scg_printerr(scgp);
449 printf("\n");
450 }
451 } else {
452 printf("\nCannot read data from block %ld.\n\n", n);
453 scg_printerr(scgp);
454 printf("\n");
455 }
456 return (ret);
457 }
458
459 LOCAL int
refresh_block(scgp,n)460 refresh_block(scgp, n)
461 SCSI *scgp;
462 long n;
463 {
464 if (ask && !yes("Refresh block %ld ? ", n))
465 return (0);
466
467 if (write_scsi(scgp, Sbuf, n, 1) >= 0 && scg_getresid(scgp) == 0) {
468 return (REFRESHED);
469 } else {
470 printf("Cannot refresh block %ld.\n\n", n);
471 scg_printerr(scgp);
472 printf("\n");
473 }
474 return (0);
475 }
476
477 LOCAL void
reassign_bad_block(scgp,n,status)478 reassign_bad_block(scgp, n, status)
479 SCSI *scgp;
480 long n;
481 int status;
482 {
483 struct scsi_def_list d;
484 sigset_t oldmask;
485
486 block_sigs(oldmask);
487
488 i_to_4_byte(d.def_list.list_block[0], n);
489
490 if (reassign_block(scgp, &d, 1) < 0) {
491 printf("Cannot reassign block %ld\n\n", n);
492 scg_printerr(scgp);
493 printf("\n");
494 } else {
495 if (!(status & (READ_OK|RECOVERED)))
496 fillbytes((caddr_t) Sbuf, (int)(2 * scgp->cap->c_bsize), '\0');
497
498 if (write_scsi(scgp, Sbuf, n, 1) < 0 || scg_getresid(scgp) != 0) {
499 printf("Alternate sector (%ld) not usable.\n\n", n);
500 scg_printerr(scgp);
501 goto out;
502 }
503 printf("Block is reassigned %s",
504 (status & (READ_OK|RECOVERED)) ?
505 "and old data is written on alternate sector.\n" :
506 "and old data is lost.\n");
507
508 printf("Verifying data... ");
509 flush();
510 if (read_scsi(scgp, Sbuf + MAX_SECSIZE, n, 1) < 0 || scg_getresid(scgp) != 0) {
511 printf("Alternate sector (%ld) not usable.\n\n", n);
512 scg_printerr(scgp);
513 goto out;
514 }
515 if (cmpbytes(Sbuf, Sbuf + MAX_SECSIZE, scgp->cap->c_bsize) <
516 scgp->cap->c_bsize)
517 printf("could not read back same data from disk\n");
518 else {
519 printf("OK\n");
520 restore_sigs(oldmask);
521
522 (void) check_stable(scgp, n);
523 }
524 }
525 out:
526 restore_sigs(oldmask);
527 }
528
529 LOCAL int
check_stable(scgp,n)530 check_stable(scgp, n)
531 SCSI *scgp;
532 long n;
533 {
534 int i;
535 long altaddr;
536 long dummy;
537
538 #define UNSTABLE 0x08
539
540 altaddr = (n - 1000L) > 0L ? n - 1000L : n + 1000L;
541
542 for (i = 0; i < 1000; i++) {
543 if (verify(scgp, n, 1, &dummy) < 0) {
544 printf("Block is unstable (%d).\n\n", i);
545 scg_printerr(scgp);
546 printf("\n");
547 return (UNSTABLE);
548 }
549 if (i % 200 == 0)
550 read_scsi(scgp, &((char *)Sbuf)[MAX_SECSIZE], altaddr, 1);
551 }
552 return (0);
553 }
554
555 EXPORT void
ext_reassign_block(scgp,n)556 ext_reassign_block(scgp, n)
557 SCSI *scgp;
558 long n;
559 {
560 int status = 0;
561
562 scgp->silent++;
563 printf("Trying to read old data... ");
564 flush();
565 status |= read_old_data(scgp, n);
566 printf("Trying to refresh block...\n");
567 status |= refresh_block(scgp, n);
568 if (status & REFRESHED) {
569 printf("Block %ld is refreshed %s\n", n,
570 ((status & READ_OK) || (status & RECOVERED)) ?
571 "and old data is written on it." :
572 "and old data is lost.");
573 status |= check_stable(scgp, n);
574 if (!(status & UNSTABLE) &&
575 (refresh_only ||
576 !yes("Do you still want to reassign block? "))) {
577 scgp->silent--;
578 return;
579 }
580 }
581 if (refresh_only) {
582 printf("Block %ld is %s, but will not be reassigned!\n", n,
583 (status&UNSTABLE)?"unstable":"defect");
584 scgp->silent--;
585 return;
586 }
587 if (ask && !yes("Reassign block %ld ? ", n)) {
588 scgp->silent--;
589 return;
590 }
591 reassign_bad_block(scgp, n, status);
592 scgp->silent--;
593 }
594
595 unsigned long test_patterns[] = {
596 0xc6dec6de,
597 0x6db6db6d,
598 0x00000000,
599 0xffffffff,
600 0xaaaaaaaa,
601 0x55555555,
602 0x4A536368,
603 };
604 int n_test_patterns = sizeof (test_patterns)/sizeof (test_patterns[0]);
605 int last_pattern = 0; /* Solange 'Sbuf' vielfach benutzt wird */
606
607 LOCAL void
fill_buf(n)608 fill_buf(n)
609 int n;
610 {
611 register int i = Sbufsize/sizeof (unsigned long);
612 register unsigned long pattern = test_patterns[n%n_test_patterns];
613 register unsigned long *lp = (unsigned long *)Sbuf;
614
615 last_pattern = n; /* Solange 'Sbuf' vielfach benutzt wird */
616
617 while (--i >= 0)
618 *lp++ = pattern;
619 }
620
621 /* Solange 'Sbuf' vielfach benutzt wird */
622 LOCAL void
refill_buf()623 refill_buf()
624 {
625 fill_buf(last_pattern);
626 }
627