rf_disks.c revision 1.1 1 /* $NetBSD: rf_disks.c,v 1.1 1998/11/13 04:20:29 oster Exp $ */
2 /*
3 * Copyright (c) 1995 Carnegie-Mellon University.
4 * All rights reserved.
5 *
6 * Author: Mark Holland
7 *
8 * Permission to use, copy, modify and distribute this software and
9 * its documentation is hereby granted, provided that both the copyright
10 * notice and this permission notice appear in all copies of the
11 * software, derivative works or modified versions, and any portions
12 * thereof, and that both notices appear in supporting documentation.
13 *
14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
16 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17 *
18 * Carnegie Mellon requests users of this software to return to
19 *
20 * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU
21 * School of Computer Science
22 * Carnegie Mellon University
23 * Pittsburgh PA 15213-3890
24 *
25 * any improvements or extensions that they make and grant Carnegie the
26 * rights to redistribute these changes.
27 */
28
29 /***************************************************************
30 * rf_disks.c -- code to perform operations on the actual disks
31 ***************************************************************/
32
33 /* :
34 * Log: rf_disks.c,v
35 * Revision 1.32 1996/07/27 18:40:24 jimz
36 * cleanup sweep
37 *
38 * Revision 1.31 1996/07/22 19:52:16 jimz
39 * switched node params to RF_DagParam_t, a union of
40 * a 64-bit int and a void *, for better portability
41 * attempted hpux port, but failed partway through for
42 * lack of a single C compiler capable of compiling all
43 * source files
44 *
45 * Revision 1.30 1996/07/19 16:11:21 jimz
46 * pass devname to DoReadCapacity
47 *
48 * Revision 1.29 1996/07/18 22:57:14 jimz
49 * port simulator to AIX
50 *
51 * Revision 1.28 1996/07/10 22:28:38 jimz
52 * get rid of obsolete row statuses (dead,degraded2)
53 *
54 * Revision 1.27 1996/06/10 12:06:14 jimz
55 * don't do any SCSI op stuff in simulator at all
56 *
57 * Revision 1.26 1996/06/10 11:55:47 jimz
58 * Straightened out some per-array/not-per-array distinctions, fixed
59 * a couple bugs related to confusion. Added shutdown lists. Removed
60 * layout shutdown function (now subsumed by shutdown lists).
61 *
62 * Revision 1.25 1996/06/09 02:36:46 jimz
63 * lots of little crufty cleanup- fixup whitespace
64 * issues, comment #ifdefs, improve typing in some
65 * places (esp size-related)
66 *
67 * Revision 1.24 1996/06/07 21:33:04 jimz
68 * begin using consistent types for sector numbers,
69 * stripe numbers, row+col numbers, recon unit numbers
70 *
71 * Revision 1.23 1996/06/03 23:28:26 jimz
72 * more bugfixes
73 * check in tree to sync for IPDS runs with current bugfixes
74 * there still may be a problem with threads in the script test
75 * getting I/Os stuck- not trivially reproducible (runs ~50 times
76 * in a row without getting stuck)
77 *
78 * Revision 1.22 1996/06/02 17:31:48 jimz
79 * Moved a lot of global stuff into array structure, where it belongs.
80 * Fixed up paritylogging, pss modules in this manner. Some general
81 * code cleanup. Removed lots of dead code, some dead files.
82 *
83 * Revision 1.21 1996/05/30 23:22:16 jimz
84 * bugfixes of serialization, timing problems
85 * more cleanup
86 *
87 * Revision 1.20 1996/05/30 11:29:41 jimz
88 * Numerous bug fixes. Stripe lock release code disagreed with the taking code
89 * about when stripes should be locked (I made it consistent: no parity, no lock)
90 * There was a lot of extra serialization of I/Os which I've removed- a lot of
91 * it was to calculate values for the cache code, which is no longer with us.
92 * More types, function, macro cleanup. Added code to properly quiesce the array
93 * on shutdown. Made a lot of stuff array-specific which was (bogusly) general
94 * before. Fixed memory allocation, freeing bugs.
95 *
96 * Revision 1.19 1996/05/27 18:56:37 jimz
97 * more code cleanup
98 * better typing
99 * compiles in all 3 environments
100 *
101 * Revision 1.18 1996/05/24 22:17:04 jimz
102 * continue code + namespace cleanup
103 * typed a bunch of flags
104 *
105 * Revision 1.17 1996/05/24 01:59:45 jimz
106 * another checkpoint in code cleanup for release
107 * time to sync kernel tree
108 *
109 * Revision 1.16 1996/05/23 21:46:35 jimz
110 * checkpoint in code cleanup (release prep)
111 * lots of types, function names have been fixed
112 *
113 * Revision 1.15 1996/05/23 00:33:23 jimz
114 * code cleanup: move all debug decls to rf_options.c, all extern
115 * debug decls to rf_options.h, all debug vars preceded by rf_
116 *
117 * Revision 1.14 1996/05/18 19:51:34 jimz
118 * major code cleanup- fix syntax, make some types consistent,
119 * add prototypes, clean out dead code, et cetera
120 *
121 * Revision 1.13 1996/05/02 14:57:43 jimz
122 * initialize sectorMask
123 *
124 * Revision 1.12 1995/12/01 15:57:04 root
125 * added copyright info
126 *
127 */
128
129 #include "rf_types.h"
130 #include "rf_raid.h"
131 #include "rf_alloclist.h"
132 #include "rf_utils.h"
133 #include "rf_configure.h"
134 #include "rf_general.h"
135 #if !defined(__NetBSD__)
136 #include "rf_camlayer.h"
137 #endif
138 #include "rf_options.h"
139 #include "rf_sys.h"
140
141 #if defined(__NetBSD__) && defined(_KERNEL)
142 #include <sys/types.h>
143 #include <sys/param.h>
144 #include <sys/systm.h>
145 #include <sys/proc.h>
146 #include <sys/ioctl.h>
147 #include <sys/fcntl.h>
148 #include <sys/vnode.h>
149
150 int raidlookup __P((char *, struct proc *p, struct vnode **));
151 #endif
152
153 #ifdef SIMULATE
154 static char disk_db_file_name[120], disk_type_name[120];
155 static double init_offset;
156 #endif /* SIMULATE */
157
158 #define DPRINTF6(a,b,c,d,e,f) if (rf_diskDebug) printf(a,b,c,d,e,f)
159 #define DPRINTF7(a,b,c,d,e,f,g) if (rf_diskDebug) printf(a,b,c,d,e,f,g)
160
161 #include "rf_ccmn.h"
162
163 /****************************************************************************************
164 *
165 * initialize the disks comprising the array
166 *
167 * We want the spare disks to have regular row,col numbers so that we can easily
168 * substitue a spare for a failed disk. But, the driver code assumes throughout
169 * that the array contains numRow by numCol _non-spare_ disks, so it's not clear
170 * how to fit in the spares. This is an unfortunate holdover from raidSim. The
171 * quick and dirty fix is to make row zero bigger than the rest, and put all the
172 * spares in it. This probably needs to get changed eventually.
173 *
174 ***************************************************************************************/
175 int rf_ConfigureDisks(
176 RF_ShutdownList_t **listp,
177 RF_Raid_t *raidPtr,
178 RF_Config_t *cfgPtr)
179 {
180 RF_RaidDisk_t **disks;
181 RF_SectorCount_t min_numblks = (RF_SectorCount_t)0x7FFFFFFFFFFFLL;
182 RF_RowCol_t r, c;
183 int bs, ret;
184 unsigned i, count, foundone=0, numFailuresThisRow;
185 RF_DiskOp_t *rdcap_op = NULL, *tur_op = NULL;
186 int num_rows_done,num_cols_done;
187
188 #if defined(__NetBSD__) && defined(_KERNEL)
189 struct proc *proc = 0;
190 #endif
191 #ifndef SIMULATE
192 #ifndef __NetBSD__
193 ret = rf_SCSI_AllocReadCapacity(&rdcap_op);
194 if (ret)
195 goto fail;
196 ret = rf_SCSI_AllocTUR(&tur_op);
197 if (ret)
198 goto fail;
199 #endif /* !__NetBSD__ */
200 #endif /* !SIMULATE */
201
202 num_rows_done = 0;
203 num_cols_done = 0;
204
205
206 RF_CallocAndAdd(disks, raidPtr->numRow, sizeof(RF_RaidDisk_t *), (RF_RaidDisk_t **), raidPtr->cleanupList);
207 if (disks == NULL) {
208 ret = ENOMEM;
209 goto fail;
210 }
211 raidPtr->Disks = disks;
212
213 #if defined(__NetBSD__) && defined(_KERNEL)
214
215 proc = raidPtr->proc; /* Blah XXX */
216
217 /* get space for the device-specific stuff... */
218 RF_CallocAndAdd(raidPtr->raid_cinfo, raidPtr->numRow,
219 sizeof(struct raidcinfo *), (struct raidcinfo **),
220 raidPtr->cleanupList);
221 if (raidPtr->raid_cinfo == NULL) {
222 ret = ENOMEM;
223 goto fail;
224 }
225 #endif
226
227 for (r=0; r<raidPtr->numRow; r++) {
228 numFailuresThisRow = 0;
229 RF_CallocAndAdd(disks[r], raidPtr->numCol + ((r==0) ? raidPtr->numSpare : 0), sizeof(RF_RaidDisk_t), (RF_RaidDisk_t *), raidPtr->cleanupList);
230 if (disks[r] == NULL) {
231 ret = ENOMEM;
232 goto fail;
233 }
234
235 /* get more space for device specific stuff.. */
236 RF_CallocAndAdd(raidPtr->raid_cinfo[r],
237 raidPtr->numCol + ((r==0) ? raidPtr->numSpare : 0),
238 sizeof(struct raidcinfo), (struct raidcinfo *),
239 raidPtr->cleanupList);
240 if (raidPtr->raid_cinfo[r] == NULL) {
241 ret = ENOMEM;
242 goto fail;
243 }
244
245
246 for (c=0; c<raidPtr->numCol; c++) {
247 ret = rf_ConfigureDisk(raidPtr,&cfgPtr->devnames[r][c][0],
248 &disks[r][c], rdcap_op, tur_op,
249 cfgPtr->devs[r][c],r,c);
250 if (ret)
251 goto fail;
252 if (disks[r][c].status != rf_ds_optimal) {
253 numFailuresThisRow++;
254 }
255 else {
256 if (disks[r][c].numBlocks < min_numblks)
257 min_numblks = disks[r][c].numBlocks;
258 DPRINTF7("Disk at row %d col %d: dev %s numBlocks %ld blockSize %d (%ld MB)\n",
259 r,c,disks[r][c].devname,
260 (long int) disks[r][c].numBlocks,
261 disks[r][c].blockSize,
262 (long int) disks[r][c].numBlocks * disks[r][c].blockSize / 1024 / 1024);
263 }
264 num_cols_done++;
265 }
266 /* XXX fix for n-fault tolerant */
267 if (numFailuresThisRow > 0)
268 raidPtr->status[r] = rf_rs_degraded;
269 num_rows_done++;
270 }
271 #ifndef SIMULATE
272 #if defined(__NetBSD__) && defined(_KERNEL)
273 /* we do nothing */
274 #else
275 rf_SCSI_FreeDiskOp(rdcap_op, 1); rdcap_op = NULL;
276 rf_SCSI_FreeDiskOp(tur_op, 0); tur_op = NULL;
277 #endif
278 #endif /* !SIMULATE */
279 /* all disks must be the same size & have the same block size, bs must be a power of 2 */
280 bs = 0;
281 for (foundone=r=0; !foundone && r<raidPtr->numRow; r++) {
282 for (c=0; !foundone && c<raidPtr->numCol; c++) {
283 if (disks[r][c].status == rf_ds_optimal) {
284 bs = disks[r][c].blockSize;
285 foundone = 1;
286 }
287 }
288 }
289 if (!foundone) {
290 RF_ERRORMSG("RAIDFRAME: Did not find any live disks in the array.\n");
291 ret = EINVAL;
292 goto fail;
293 }
294 for (count=0,i=1; i; i<<=1) if (bs & i)
295 count++;
296 if (count != 1) {
297 RF_ERRORMSG1("Error: block size on disks (%d) must be a power of 2\n",bs);
298 ret = EINVAL;
299 goto fail;
300 }
301 for (r=0; r<raidPtr->numRow; r++) {
302 for (c=0; c<raidPtr->numCol; c++) {
303 if (disks[r][c].status == rf_ds_optimal) {
304 if (disks[r][c].blockSize != bs) {
305 RF_ERRORMSG2("Error: block size of disk at r %d c %d different from disk at r 0 c 0\n",r,c);
306 ret = EINVAL;
307 goto fail;
308 }
309 if (disks[r][c].numBlocks != min_numblks) {
310 RF_ERRORMSG3("WARNING: truncating disk at r %d c %d to %d blocks\n",
311 r,c,(int) min_numblks);
312 disks[r][c].numBlocks = min_numblks;
313 }
314 }
315 }
316 }
317
318 raidPtr->sectorsPerDisk = min_numblks;
319 raidPtr->logBytesPerSector = ffs(bs) - 1;
320 raidPtr->bytesPerSector = bs;
321 raidPtr->sectorMask = bs-1;
322 return(0);
323
324 fail:
325
326 #ifndef SIMULATE
327 #if defined(__NetBSD__) && defined(_KERNEL)
328
329 for(r=0;r<raidPtr->numRow;r++) {
330 for(c=0;c<raidPtr->numCol;c++) {
331 /* Cleanup.. */
332 #ifdef DEBUG
333 printf("Cleaning up row: %d col: %d\n",r,c);
334 #endif
335 if (raidPtr->raid_cinfo[r][c].ci_vp) {
336 (void)vn_close(raidPtr->raid_cinfo[r][c].ci_vp,
337 FREAD|FWRITE, proc->p_ucred, proc);
338 }
339 }
340 }
341 /* Space allocated for raid_vpp will get cleaned up at some other point */
342 /* XXX Need more #ifdefs in the above... */
343
344 #else
345
346 if (rdcap_op) rf_SCSI_FreeDiskOp(rdcap_op, 1);
347 if (tur_op) rf_SCSI_FreeDiskOp(tur_op, 0);
348
349 #endif
350 #endif /* !SIMULATE */
351 return(ret);
352 }
353
354
355 /****************************************************************************************
356 * set up the data structures describing the spare disks in the array
357 * recall from the above comment that the spare disk descriptors are stored
358 * in row zero, which is specially expanded to hold them.
359 ***************************************************************************************/
360 int rf_ConfigureSpareDisks(
361 RF_ShutdownList_t **listp,
362 RF_Raid_t *raidPtr,
363 RF_Config_t *cfgPtr)
364 {
365 char buf[256];
366 int i, ret;
367 RF_DiskOp_t *rdcap_op = NULL, *tur_op = NULL;
368 unsigned bs;
369 RF_RaidDisk_t *disks;
370 int num_spares_done;
371
372 #if defined(__NetBSD__) && defined(_KERNEL)
373 struct proc *proc;
374 #endif
375
376 #ifndef SIMULATE
377 #ifndef __NetBSD__
378 ret = rf_SCSI_AllocReadCapacity(&rdcap_op);
379 if (ret)
380 goto fail;
381 ret = rf_SCSI_AllocTUR(&tur_op);
382 if (ret)
383 goto fail;
384 #endif /* !__NetBSD__ */
385 #endif /* !SIMULATE */
386
387 num_spares_done = 0;
388
389 #if defined(__NetBSD__) && defined(_KERNEL)
390 proc = raidPtr->proc;
391 /* The space for the spares should have already been
392 allocated by ConfigureDisks() */
393 #endif
394
395 disks = &raidPtr->Disks[0][raidPtr->numCol];
396 for (i=0; i<raidPtr->numSpare; i++) {
397 ret = rf_ConfigureDisk(raidPtr,&cfgPtr->spare_names[i][0],
398 &disks[i], rdcap_op, tur_op,
399 cfgPtr->spare_devs[i],0,raidPtr->numCol+i);
400 if (ret)
401 goto fail;
402 if (disks[i].status != rf_ds_optimal) {
403 RF_ERRORMSG1("Warning: spare disk %s failed TUR\n",buf);
404 } else {
405 disks[i].status = rf_ds_spare; /* change status to spare */
406 DPRINTF6("Spare Disk %d: dev %s numBlocks %ld blockSize %d (%ld MB)\n",i,
407 disks[i].devname,
408 (long int) disks[i].numBlocks,disks[i].blockSize,
409 (long int) disks[i].numBlocks * disks[i].blockSize / 1024 / 1024);
410 }
411 num_spares_done++;
412 }
413 #ifndef SIMULATE
414 #if defined(__NetBSD__) && (_KERNEL)
415
416 #else
417 rf_SCSI_FreeDiskOp(rdcap_op, 1); rdcap_op = NULL;
418 rf_SCSI_FreeDiskOp(tur_op, 0); tur_op = NULL;
419 #endif
420 #endif /* !SIMULATE */
421
422 /* check sizes and block sizes on spare disks */
423 bs = 1 << raidPtr->logBytesPerSector;
424 for (i=0; i<raidPtr->numSpare; i++) {
425 if (disks[i].blockSize != bs) {
426 RF_ERRORMSG3("Block size of %d on spare disk %s is not the same as on other disks (%d)\n",disks[i].blockSize, disks[i].devname, bs);
427 ret = EINVAL;
428 goto fail;
429 }
430 if (disks[i].numBlocks < raidPtr->sectorsPerDisk) {
431 RF_ERRORMSG3("Spare disk %s (%d blocks) is too small to serve as a spare (need %ld blocks)\n",
432 disks[i].devname, disks[i].blockSize, (long int)raidPtr->sectorsPerDisk);
433 ret = EINVAL;
434 goto fail;
435 } else if (disks[i].numBlocks > raidPtr->sectorsPerDisk) {
436 RF_ERRORMSG2("Warning: truncating spare disk %s to %ld blocks\n",disks[i].devname, (long int) raidPtr->sectorsPerDisk);
437
438 disks[i].numBlocks = raidPtr->sectorsPerDisk;
439 }
440 }
441
442 return(0);
443
444 fail:
445 #ifndef SIMULATE
446 #if defined(__NetBSD__) && defined(_KERNEL)
447
448 for(i=0;i<raidPtr->numSpare;i++) {
449 /* Cleanup.. */
450 #ifdef DEBUG
451 printf("Cleaning up spare: %d\n",i);
452 #endif
453 if (raidPtr->raid_cinfo[0][raidPtr->numCol+i].ci_vp) {
454 (void)vn_close(raidPtr->raid_cinfo[0][raidPtr->numCol+i].ci_vp,
455 FREAD|FWRITE, proc->p_ucred, proc);
456 }
457 }
458
459 #else
460
461 if (rdcap_op) rf_SCSI_FreeDiskOp(rdcap_op, 1);
462 if (tur_op) rf_SCSI_FreeDiskOp(tur_op, 0);
463
464 #endif
465
466 #endif /* !SIMULATE */
467 return(ret);
468 }
469
470
471
472 /* configure a single disk in the array */
473 int rf_ConfigureDisk(raidPtr, buf, diskPtr, rdcap_op, tur_op, dev, row, col)
474 RF_Raid_t *raidPtr; /* We need this down here too!! GO */
475 char *buf;
476 RF_RaidDisk_t *diskPtr;
477 RF_DiskOp_t *rdcap_op;
478 RF_DiskOp_t *tur_op;
479 dev_t dev; /* device number used only in kernel */
480 RF_RowCol_t row;
481 RF_RowCol_t col;
482 {
483 char *p;
484 #ifdef SIMULATE
485 double init_offset;
486 #else /* SIMULATE */
487 #if defined(__NetBSD__) && defined(_KERNEL)
488 int retcode;
489 #else
490 int busid, targid, lun, retcode;
491 #endif
492 #endif /* SIMULATE */
493
494 #if defined(__NetBSD__) && defined(_KERNEL)
495 struct partinfo dpart;
496 struct vnode *vp;
497 struct vattr va;
498 struct proc *proc;
499 int error;
500 #endif
501
502 retcode = 0;
503 p = rf_find_non_white(buf);
504 if (p[strlen(p)-1] == '\n') {
505 /* strip off the newline */
506 p[strlen(p)-1] = '\0';
507 }
508 (void) strcpy(diskPtr->devname, p);
509
510 #ifdef SIMULATE
511
512 init_offset = 0.0;
513 rf_InitDisk(&diskPtr->diskState, disk_db_file_name,diskPtr->devname,0,0,init_offset,row,col);
514 rf_GeometryDoReadCapacity(&diskPtr->diskState, &diskPtr->numBlocks, &diskPtr->blockSize);
515 diskPtr->numBlocks = diskPtr->numBlocks * rf_sizePercentage / 100;
516
517 /* we allow the user to specify that only a fraction of the disks should be used
518 * this is just for debug: it speeds up the parity scan
519 */
520
521 #else /* SIMULATE */
522 #ifndef __NetBSD__
523 /* get bus, target, lun */
524 retcode = rf_extract_ids(p, &busid, &targid, &lun);
525 if (retcode)
526 return(retcode);
527
528 /* required in kernel, nop at user level */
529 retcode = rf_SCSI_OpenUnit(dev);
530 if (retcode)
531 return(retcode);
532
533 diskPtr->dev = dev;
534 if (rf_SCSI_DoTUR(tur_op, (u_char)busid, (u_char)targid, (u_char)lun, dev)) {
535 RF_ERRORMSG1("Disk %s failed TUR. Marked as dead.\n",diskPtr->devname);
536 diskPtr->status = rf_ds_failed;
537 } else {
538 diskPtr->status = rf_ds_optimal;
539 retcode = rf_SCSI_DoReadCapacity(raidPtr,rdcap_op, busid, targid, lun, dev,
540 &diskPtr->numBlocks, &diskPtr->blockSize, diskPtr->devname);
541 if (retcode)
542 return(retcode);
543
544 /* we allow the user to specify that only a fraction of the disks should be used
545 * this is just for debug: it speeds up the parity scan
546 */
547 diskPtr->numBlocks = diskPtr->numBlocks * rf_sizePercentage / 100;
548 }
549 #endif
550 #if defined(__NetBSD__) && defined(_KERNEL)
551
552 proc = raidPtr->proc; /* XXX Yes, this is not nice.. */
553
554 /* Let's start by claiming the component is fine and well... */
555 /* XXX not the case if the disk is toast.. */
556 diskPtr->status = rf_ds_optimal;
557
558
559 raidPtr->raid_cinfo[row][col].ci_vp = NULL;
560 raidPtr->raid_cinfo[row][col].ci_dev = NULL;
561
562 error = raidlookup(diskPtr->devname, proc, &vp);
563 if (error) {
564 printf("raidlookup on device: %s failed!\n",diskPtr->devname);
565 if (error == ENXIO) {
566 /* XXX the component isn't there... must be dead :-( */
567 diskPtr->status = rf_ds_failed;
568 } else {
569 return(error);
570 }
571 }
572
573 if (diskPtr->status == rf_ds_optimal) {
574
575 if ((error = VOP_GETATTR(vp, &va, proc->p_ucred, proc)) != 0) {
576 return(error);
577 }
578
579 error = VOP_IOCTL(vp, DIOCGPART, (caddr_t)&dpart,
580 FREAD, proc->p_ucred, proc);
581 if (error) {
582 return(error);
583 }
584
585
586 diskPtr->blockSize = dpart.disklab->d_secsize;
587
588 diskPtr->numBlocks = dpart.part->p_size - rf_protectedSectors;
589
590 raidPtr->raid_cinfo[row][col].ci_vp = vp;
591 raidPtr->raid_cinfo[row][col].ci_dev = va.va_rdev;
592
593 #if 0
594 diskPtr->dev = dev;
595 #endif
596
597 diskPtr->dev = va.va_rdev; /* XXX or the above? */
598
599 /* we allow the user to specify that only a fraction of the disks should be used
600 * this is just for debug: it speeds up the parity scan
601 */
602 diskPtr->numBlocks = diskPtr->numBlocks * rf_sizePercentage / 100;
603
604 }
605
606 #endif /* !__NetBSD__ */
607 #endif /* SIMULATE */
608
609 return(0);
610 }
611
612 #ifdef SIMULATE
613
614 void rf_default_disk_names()
615 {
616 sprintf(disk_db_file_name,"disk.db");
617 sprintf(disk_type_name,"HP2247");
618 }
619
620 void rf_set_disk_db_name(s)
621 char *s;
622 {
623 strcpy(disk_db_file_name,s);
624 }
625
626 void rf_set_disk_type_name(s)
627 char *s;
628 {
629 strcpy(disk_type_name,s);
630 }
631
632 #endif /* SIMULATE */
633