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