2 * FLUSH13 -- Device mod utility for INT13 memory cache
4 * FLUSH13 [/s|/sx|/sr] [/d|/e] [/l|/u] [/i] [/f] [/wt:on|/wt:off]
5 * [/wc:on|/wc:off] [/t:nnnnn] [/r:on|/r:off] [/c:on|/c:off]
7 * No arguments - This causes FLUSH13 to flush out any "dirty"
8 * tracks in the INT13 cache. A "dirty" track is one
9 * which has been written into the cache, but not yet
10 * written to the disk. This invokation causes all dirty tracks
11 * to be written out to the disk so that the system can
12 * be re-booted or turned off. NOTE: FAILURE TO FLUSH
13 * THE CACHE BEFORE A RE-BOOT OR POWER OFF CAN CAUSE THE
14 * INFORMATION ON THE HARDFILE TO BE CORRUPTED.
16 * /f - Flush. Same as the no arguments case, but allows you to
17 * perform the flush and do something else (like /s).
19 * /i - Flush and invalidate. This is the same as the no argument
20 * case except that all of the information in the cache
21 * is also discarded. This makes the cache EMPTY.
23 * /d - Disable caching. This causes all dirty cache information
24 * to be flushed and all caching to stop.
26 * /e - Enable caching. This causes caching to be enabled after
27 * a previous /d disable. When INT13 is started it is enabled.
29 * /l - Lock the cache. This causes all dirty information to be
30 * flushed, and the cache contents to be locked in the cache.
31 * When in this mode the locked elements will not be discarded
32 * to make room for new tracks. This can be used
33 * to "load" the cache with desired things. For instance if
34 * you use the "foobar" program a lot, you can run foobar,
35 * causing it to be loaded into the cache, then lock the cache.
36 * This causes the foobar program to always be in the cache.
37 * You may lock the cache as many times as you want. Each lock
38 * causes the current information (including any previously
39 * locked information) to be locked.
40 * NOTE: Information in a locked cache is READ ONLY!! Any write
41 * operation on information in a locked cache causes the
42 * information to be unlocked.
44 * /u - Unlock the cache. This undoes a previous /l and returns
45 * the cache to normal operation.
47 * /s - Print status. This displays the settings of the setable
49 * /sx - Print extended status. Same as /s, only additional
50 * Statistical information is also given.
51 * /sr - Reset statistics. Same as /sx, only the additional
52 * Statistical information is reset to 0.
54 * /wt:on off - Enable or Disable write through. When INT13 is caching
55 * write information, it is a good idea to imply a flush of
56 * the cache on some operations so that in case of a crash or
57 * power failure the information in the cache which is not on
58 * the disk will not be lost. /wt:on enables write through on full
59 * track INT 13s which are to tracks not currently in the cache.
60 * /wt:off disables it. INT13 is faster with write through
61 * off, at the expense of there being a bigger risk of
62 * loosing data. /wt:on IS NOT a substitute for flushing before
63 * a re-boot!!!! This write through mechanism is far from perfect,
64 * all it is is a risk REDUCER, not a risk eliminator. /wt:off
65 * is the setting when INT13 is started.
67 * /wc:on off - Enable or Disable write caching. There is risk when
68 * caching "dirty" information that the system will crash,
69 * or be re-booted, or be turned off before this information
70 * can be written to the disk. This may corrupt the disk.
71 * This risk can be ELIMINATED, at the expense of cache
72 * performance, by NOT caching any dirty information.
73 * /wc:off disables the caching of dirty information,
74 * eliminating the risk. /wc:on enables the caching of dirty
75 * information. /wc:on is the default when INT13 is started.
77 * WARNING: You must be careful to flush the cache before
78 * re-booting the system, or turning it off if /wc:on is selected.
79 * You should also be careful to disable the cache (/d), or do
80 * /wc:off before running any program under development which
81 * has a chance of crashing due to bugs.
83 * NOTE: When /wc:off is selected, write info CAN get into
84 * the cache (when the write is to a track which is currently
85 * in the cache). The difference is that this "dirty" information
86 * is IMMEDIATELY written out to the disk instead of being
87 * held in the cache in the "dirty" state. When the write is
88 * to a track that is not in the cache, it will be passed
89 * through to the disk without being cached.
91 * /t:nnnnn - Set the auto flush interval. INT13 listens on the system
92 * timer to note the passage of time and "age" the dirty
93 * information in the cache. Every nnnnn ticks, the cache is
94 * flushed. The timer ticks 18.2 times a second.
97 * ===========================================
98 * 18 | Flush every second
99 * 1092 | Flush every minute
100 * 5460 | Flush every 5 minutes
101 * 10920 | Flush every 10 minutes
102 * 21840 | Flush every 20 minutes
103 * 32760 | Flush every 30 minutes
104 * 65520 | Flush every hour
106 * The default setting of nnnnn is 1092 or every minute.
107 * NOTE: There is no way to "disable" this tick aging. Setting
108 * nnnnn = 0 causes a wait for 65536 ticks which is a
109 * little over an hour. The max value for nnnnn is 65535.
110 * Disabling the cache (/d), or turning write caching
111 * off (/wc:off) effectively prevents the aging from
112 * doing anything as there is never anything to flush
113 * in these cases. Setting very low values of nnnnn
114 * should be avoided as it places a lot of overhead into
115 * the timer interrupt service. Rather than set low values,
116 * it is better to just turn off write caching (/wc:off).
117 * NOTE: As stated above, the max value for nnnnn is 65535. It
118 * should be noted however that FLUSH13 DOES NOT object if
119 * you specify a number larger than this! It will simply
120 * use only the low 16 bits of the number.
122 * /r:on off - En/Disable reboot flush.
123 * INT13 has a provision for detecting Ctrl-Alt-Del user
124 * reboots. /r:on enables a flush of the cache at this time
125 * to prevent the disks from being corrupted. The default
126 * setting is /r:off. NOTE WARNING DANGER!!!!! Enabling
127 * this feature can prevent disks from being damaged BUT
128 * the mechanism has flaws. For one, you will have to hit
129 * Ctrl-Alt-Del a second time to get the system to reboot.
130 * YOU MUST NOT POUND ON THE KEY. You will crash the system if
131 * you do. Hit the key ONCE, if the system re-boots, fine. If
132 * there is info to flush out of the cache, the drive light
133 * will come on and the system will probably NOT reboot. WAIT
134 * until the drive light is OFF before hitting Ctrl-Alt-Del
135 * again. This feature of INT13 MAY NOT WORK with other
136 * software in the system. USER BEWARE!!!!!!!!!!!!!!!!!!!
138 * /c:on off - En/Disable all cache on reads.
139 * Normally INT13 does not cache EVERY I/O. Whenever
140 * it sees a full track I/O which is not currently in
141 * the cache, it DOES NOT cache that track. This is
142 * an optimization for "typical" operation, and actually
143 * increases performance. This is the default setting
144 * (/c:off). There may be some cases where it is desirable
145 * that ALL reads be cached. One example is that you are
146 * "loading" the cache prior to locking it with FLUSH13 /l.
147 * With /c:off, some pieces of what you're trying to load
148 * may not get into the cache. Another example is that
149 * you continually access in a sequential manner (like
150 * program load) some large file which happens to be
151 * contiguous on the disk. Again, there may be some "piece"
152 * of the file which does not get into the cache with
153 * /c:off. /c:on enables the caching of ALL reads.
154 * NOTE: The same "don't bother caching operations which
155 * are full track and not in the cache" applies
156 * to writes as well. /c has NO EFFECT on this
157 * behavior however. /c only effects read operations.
159 * MODIFICATION HISTORY
161 * 1.10 5/26/86 ARR First version in assembler
162 * 1.20 5/27/86 ARR Lock cache function added.
163 * 1.22 5/30/86 ARR /r reboot flush code added
164 * 1.23 6/03/86 ARR Cache statistics added
165 * 1.24 6/05/86 ARR Added /a "all cache" code
166 * 1.25 6/10/86 ARR Added total used, total locked to status
169 * 1.26 6/12/86 ARR /wb changed to /wc. Some status report wording
170 * changed. This was to align the behavior with the
171 * documentation a little better.
172 * 1.27 1/22/87 ARR Change to format of status information.
178 * Messages in flmes.asm
180 extern char NO_DEV_MESS[], IOCTL_BAD_MESS[], STATUS_MES2[], SWTCH_CONF[];
181 extern char BAD_PARM[], STATUS_MES1[], DISSTRING[], ENSTRING[];
182 extern char LOCKSTRING[], UNLSTRING[], REBOOT_MES[];
183 extern char STATUS_3R[], STATUS_3W[], STATUS_3T[];
184 extern char CACHE_MES[], WT_MES[], WB_MES[], L_MES[], C_MES[], T_MES[];
185 extern char STATUS_4[], ONSTRING[], OFFSTRING[], STATUS_5[];
188 * Structure of the data returned by the status call to INT13
191 unsigned char write_through;
192 unsigned char write_buff;
193 unsigned char enable_13;
194 unsigned char nuldev;
195 unsigned int ticksetting;
196 unsigned char lock_cache;
197 unsigned char reboot_flush;
198 unsigned char all_cache;
200 unsigned long total_writes;
201 unsigned long write_hits;
202 unsigned long total_reads;
203 unsigned long read_hits;
204 unsigned int ttracks;
205 unsigned int total_used;
206 unsigned int total_locked;
207 unsigned int total_dirty;
208 unsigned int current_size;
209 unsigned int initial_size;
210 unsigned int minimum_size;
214 * Assembler routines in fl13.asm
216 extern int IOCTLOpen(char *);
217 extern int IOCTLWrite(int,char *,int);
218 extern int IOCTLRead(int,status *,int);
219 extern int IOCTLClose(int);
222 * GetNum - Read an unsigned 16 bit decimal number
224 * ENTRY: cptr points to string where decimal number is
225 * iptr points to unsigned int where number goes
227 * NOTES: Calls Fatal (which doesn't return) if no number is present.
228 * No error if number is > 16 bits, only low 16 bits are returned.
230 * EXIT: returns cptr advanced past number
231 * iptr contains number found
234 char *GetNum(cptr,iptr)
239 if((*cptr < '0') || (*cptr > '9'))
241 while((*cptr >= '0') && (*cptr <= '9'))
242 *iptr = (*iptr * 10) + ((unsigned int) (*cptr++ - '0'));
247 * GetOnOff - Check for :on or :off string
249 * ENTRY: cptr points to string where :on or :off is supposed to be
250 * iptr points to unsigned int which is a boolean
252 * NOTES: Calls Fatal (which doesn't return) if :on or :off is not found.
255 * EXIT: returns cptr advanced past :on or :off
256 * iptr contains 1 if :on was found
257 * iptr contains 0 if :off was found
260 char *GetOnOff(cptr,iptr)
274 else if(*cptr == 'f'){
294 * EXIT: exit(0) if OK, exit(-1) if error.
297 main(argc, argv, envp)
306 unsigned long total_hits,total_ops;
307 unsigned int minutes,seconds;
309 unsigned SWITCH_S : 1;
310 unsigned SWITCH_I : 1;
311 unsigned SWITCH_D : 1;
312 unsigned SWITCH_E : 1;
313 unsigned SWITCH_L : 1;
314 unsigned SWITCH_U : 1;
315 unsigned SWITCH_T : 1;
316 unsigned SWITCH_WCON : 1;
317 unsigned SWITCH_WCOFF : 1;
318 unsigned SWITCH_WTON : 1;
319 unsigned SWITCH_WTOFF : 1;
320 unsigned SWITCH_ROFF : 1;
321 unsigned SWITCH_RON : 1;
322 unsigned SWITCH_SX : 1;
323 unsigned SWITCH_SR : 1;
324 unsigned SWITCH_CON : 1;
325 unsigned SWITCH_COFF : 1;
326 unsigned SWITCH_F : 1;
330 unsigned char tickvall; /* this is actually an unsigned int */
331 unsigned char tickvalh; /* but we have to declare it this way */
332 } tickpacket; /* so that the compiler doesn't word align */
335 /* Check for no arguments case and process if found */
338 if (argc == 1) { /* no arguments */
339 if((handle = IOCTLOpen("SMARTAAR")) == -1)
341 if(IOCTLWrite(handle,"\x00",1) == -1)
342 Fatal(IOCTL_BAD_MESS);
347 /* Initialize data associated with the argument parse */
349 switches.SWITCH_S = switches.SWITCH_I = switches.SWITCH_D = 0;
350 switches.SWITCH_E = switches.SWITCH_L = switches.SWITCH_U = 0;
351 switches.SWITCH_T = switches.SWITCH_WCON = switches.SWITCH_WCOFF = 0;
352 switches.SWITCH_WTON = switches.SWITCH_WTOFF = switches.SWITCH_ROFF = 0;
353 switches.SWITCH_RON = switches.SWITCH_SX = switches.SWITCH_SR = 0;
354 switches.SWITCH_CON = switches.SWITCH_COFF = switches.SWITCH_F = 0;
356 /* Parse the arguments */
358 ++argv; /* Skip argv[0] */
359 while(--argc) { /* While arguments */
361 if(*cptr++ != '/') /* all arguments are switches */
363 if(*cptr == '\0') /* trailing / error? */
365 *cptr |= 0x20; /* lower case */
370 if(switches.SWITCH_S || switches.SWITCH_SX || switches.SWITCH_SR)
373 switches.SWITCH_S = 1;
377 switches.SWITCH_SR = 1;
378 else if(*cptr == 'x')
379 switches.SWITCH_SX = 1;
388 if(switches.SWITCH_CON || switches.SWITCH_COFF)
390 cptr = GetOnOff(cptr,&boolval);
392 switches.SWITCH_CON = 1;
394 switches.SWITCH_COFF = 1;
397 /* t set tick value */
399 if(switches.SWITCH_T)
403 cptr = GetNum(cptr,&tickpacket.tickvall);
404 tickpacket.Tchar = '\x0B'; /* set tick is call 5 */
405 switches.SWITCH_T = 1;
408 /* wt or wb on or off */
413 if(switches.SWITCH_WCOFF || switches.SWITCH_WCON)
415 cptr = GetOnOff(cptr,&boolval);
417 switches.SWITCH_WCON = 1;
419 switches.SWITCH_WCOFF = 1;
421 else if(*cptr == 't') {
423 if(switches.SWITCH_WTOFF || switches.SWITCH_WTON)
425 cptr = GetOnOff(cptr,&boolval);
427 switches.SWITCH_WTON = 1;
429 switches.SWITCH_WTOFF = 1;
437 if(switches.SWITCH_D || switches.SWITCH_E)
439 switches.SWITCH_D = 1;
444 if(switches.SWITCH_D || switches.SWITCH_E)
446 switches.SWITCH_E = 1;
451 if(switches.SWITCH_L || switches.SWITCH_U)
453 switches.SWITCH_L = 1;
458 if(switches.SWITCH_L || switches.SWITCH_U)
460 switches.SWITCH_U = 1;
465 if(switches.SWITCH_I)
467 switches.SWITCH_I = 1;
472 if(switches.SWITCH_F)
474 switches.SWITCH_F = 1;
479 if(switches.SWITCH_RON || switches.SWITCH_ROFF)
481 cptr = GetOnOff(cptr,&boolval);
483 switches.SWITCH_RON = 1;
485 switches.SWITCH_ROFF = 1;
492 if(*cptr != '\0') /* must be at end of argument */
494 ++argv; /* next argument */
497 /* Open the device */
499 if((handle = IOCTLOpen("SMARTAAR")) == -1)
502 /* Perform the actions indicated by the arguments */
504 if(switches.SWITCH_I) {
505 if(IOCTLWrite(handle,"\x01",1) == -1)
506 FatalC(handle,IOCTL_BAD_MESS);
509 if(switches.SWITCH_F) {
510 if(IOCTLWrite(handle,"\x00",1) == -1)
511 FatalC(handle,IOCTL_BAD_MESS);
514 if(switches.SWITCH_WTON) {
515 if(IOCTLWrite(handle,"\x04\x01",2) == -1)
516 FatalC(handle,IOCTL_BAD_MESS);
518 else if(switches.SWITCH_WTOFF) {
519 if(IOCTLWrite(handle,"\x04\x00",2) == -1)
520 FatalC(handle,IOCTL_BAD_MESS);
523 if(switches.SWITCH_WCON) {
524 if(IOCTLWrite(handle,"\x04\x03",2) == -1)
525 FatalC(handle,IOCTL_BAD_MESS);
527 else if(switches.SWITCH_WCOFF) {
528 if(IOCTLWrite(handle,"\x04\x02",2) == -1)
529 FatalC(handle,IOCTL_BAD_MESS);
532 if(switches.SWITCH_L) {
533 if(IOCTLWrite(handle,"\x06",1) == -1)
534 FatalC(handle,IOCTL_BAD_MESS);
536 else if(switches.SWITCH_U) {
537 if(IOCTLWrite(handle,"\x07",1) == -1)
538 FatalC(handle,IOCTL_BAD_MESS);
541 if(switches.SWITCH_T) {
542 if(IOCTLWrite(handle,&tickpacket.Tchar,3) == -1)
543 FatalC(handle,IOCTL_BAD_MESS);
546 if(switches.SWITCH_RON) {
547 if(IOCTLWrite(handle,"\x08\x01",2) == -1)
548 FatalC(handle,IOCTL_BAD_MESS);
550 else if(switches.SWITCH_ROFF) {
551 if(IOCTLWrite(handle,"\x08\x00",2) == -1)
552 FatalC(handle,IOCTL_BAD_MESS);
555 if(switches.SWITCH_CON) {
556 if(IOCTLWrite(handle,"\x0A\x01",2) == -1)
557 FatalC(handle,IOCTL_BAD_MESS);
559 else if(switches.SWITCH_COFF) {
560 if(IOCTLWrite(handle,"\x0A\x00",2) == -1)
561 FatalC(handle,IOCTL_BAD_MESS);
564 if(switches.SWITCH_E) {
565 if(IOCTLWrite(handle,"\x03",1) == -1)
566 FatalC(handle,IOCTL_BAD_MESS);
568 else if(switches.SWITCH_D) {
569 if(IOCTLWrite(handle,"\x02",1) == -1)
570 FatalC(handle,IOCTL_BAD_MESS);
573 if(switches.SWITCH_S || switches.SWITCH_SR || switches.SWITCH_SX) {
574 if(IOCTLRead(handle,&config,sizeof(config)) == -1)
575 FatalC(handle,IOCTL_BAD_MESS);
576 if(config.nuldev != 0)
580 if(config.enable_13 != 0)
581 printf(CACHE_MES,ENSTRING);
583 printf(CACHE_MES,DISSTRING);
584 if(config.lock_cache != 0)
585 printf(L_MES,LOCKSTRING);
587 printf(L_MES,UNLSTRING);
589 if(config.write_buff != 0)
590 printf(WB_MES,ONSTRING);
592 printf(WB_MES,OFFSTRING);
593 if(config.reboot_flush != 0)
594 printf(REBOOT_MES,ONSTRING);
596 printf(REBOOT_MES,OFFSTRING);
598 if(config.all_cache != 0)
599 printf(C_MES,ONSTRING);
601 printf(C_MES,OFFSTRING);
602 if(config.write_through != 0)
603 printf(WT_MES,ONSTRING);
605 printf(WT_MES,OFFSTRING);
607 if(config.ticksetting == 0) {
612 seconds = ((unsigned long)config.ticksetting * 10) / 182;
613 minutes = seconds / 60;
614 seconds = seconds % 60;
616 printf(T_MES,minutes,seconds,config.ticksetting);
618 if(switches.SWITCH_SR) {
619 if(IOCTLWrite(handle,"\x09",1) == -1)
620 FatalC(handle,IOCTL_BAD_MESS);
621 /* get the status again so that the extended status has the reset */
622 if(IOCTLRead(handle,&config,sizeof(config)) == -1)
623 FatalC(handle,IOCTL_BAD_MESS);
625 if(switches.SWITCH_SX || switches.SWITCH_SR) {
626 if(config.total_writes == 0)
627 printf(STATUS_3W,config.write_hits,config.total_writes,(unsigned int) 0);
629 printf(STATUS_3W,config.write_hits,config.total_writes,(unsigned int)(config.write_hits*100/config.total_writes));
630 if(config.total_reads == 0)
631 printf(STATUS_3R,config.read_hits,config.total_reads,(unsigned int) 0);
633 printf(STATUS_3R,config.read_hits,config.total_reads,(unsigned int)(config.read_hits*100/config.total_reads));
634 total_ops = config.total_reads + config.total_writes;
635 total_hits = config.read_hits + config.write_hits;
637 printf(STATUS_3T,total_hits,total_ops,(unsigned int) 0);
639 printf(STATUS_3T,total_hits,total_ops,(unsigned int)(total_hits*100/total_ops));
640 printf(STATUS_4,config.ttracks,config.total_used,config.total_locked,config.total_dirty);
641 printf(STATUS_5,config.current_size,config.initial_size,config.minimum_size);
646 /* Close the device, and done */
653 * Fatal -- Fatal (to flush13) error
655 * ENTRY: p is pointer to error message to print
665 fprintf(stderr,"\n%s\n",p);
670 * FatalC -- Fatal (to flush13) error, and close open handle
672 * ENTRY: p is pointer to error message to print
673 * hand is handle number of open device channel to close