Merge branch 'master' into cde-next
[oweals/cde.git] / cde / lib / tt / mini_isam / isdiskbufs2.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*%%  (c) Copyright 1993, 1994 Hewlett-Packard Company                   */
24 /*%%  (c) Copyright 1993, 1994 International Business Machines Corp.     */
25 /*%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.                    */
26 /*%%  (c) Copyright 1993, 1994 Novell, Inc.                              */
27 /*%%  $XConsortium: isdiskbufs2.c /main/3 1995/10/23 11:37:47 rswiston $                                                         */
28
29 /*
30  * Copyright (c) 1988 by Sun Microsystems, Inc.
31  */
32
33 /*
34  * _isdiskbufs.c
35  *
36  * Description:
37  *      ISAM disk buffer managament
38  *
39  */
40
41 /************************ NON MAPPED I/O version ***************************/
42
43 #include "isam_impl.h"
44
45 extern struct dlink *_isdln_next(), *_isdln_first();
46
47 #define ISMAXBUFFERS    200                  /* Use 20 buffers */
48 #define ISHASHHDRS      256                  /* Must be a power of two */
49 #define ISHASHMASK      (ISHASHHDRS-1)
50
51 #define __hashblkno(fcb,blkno) (((size_t)(fcb)+(blkno)) & ISHASHMASK)
52
53
54 #define base ((char *)0)
55 #define _isdln_insert(l,e) _isdln_base_insert(base,(l),(e))
56 #define _isdln_append(l,e)  _isdln_base_append(base,(l),(e))
57 #define _isdln_remove(e)  _isdln_base_remove(base,(e))
58 #define _isdln_first(l)  _isdln_base_first(base,(l))
59 #define _isdln_next(l)  _isdln_base_next(base,(l))
60 #define _isdln_prev(l)  _isdln_base_prev(base,(l))
61 #define _isdln_makeempty(l)  _isdln_base_makeempty(base,(l))
62 #define _isdln_isempty(l)  _isdln_base_isempty(base,(l))
63
64 /*---------------------- Local data ---------------------------------------*/
65 static Bufhdr *_getavail(), *_findblock();
66 static void _disk_init(), _commit1buffer(), _rollback1buffer(), _flush1buffer();
67 static void _makenodata();
68
69 Bufhdr bufhdrs [ISMAXBUFFERS];
70 struct dlink  hashhdrs [ISHASHHDRS];         /* Heads of hashed lists */
71
72 struct dlink  availlist;                     /* Available buffer list */
73 struct dlink  *pavail = &availlist;
74
75 struct dlink  changelist;                    /* Change buffer list */
76 struct dlink  *pchangl = &changelist;
77
78 struct dlink  fixlist;                       /* Fixed buffer list */
79 struct dlink  *pfixl = &fixlist;
80
81 static int    availn;                        /* Number of available buffers */
82 static int    minavailn;                     /* Minimum available buffers */
83 static int    maxavailn;                     /* Stop flushing when 
84                                               * when maxavailn buffers
85                                               * are available */
86
87
88 #define ISB_FIXED (ISB_RFIXED | ISB_WFIXED)
89 #define MINAVAILN       40                   /* in procent of total # buffers */
90 #define MAXAVAILN       60                   /* in procent of total # buffers */
91
92
93
94 /* unixfd, .rec, .ind., .var */
95 Bufhdr *
96 _isdisk_fix(Fcb *fcb, int unixfd, Blkno blkno, int mode)
97 {
98     Bufhdr *p, *p2;
99     struct dlink           *hashl;
100
101     /*
102      * Initialize some local data.
103      */
104     _disk_init();       
105
106     if (fcb->datfd == unixfd)
107         assert(blkno != 0);                  /* Never access control page */
108
109     hashl = (hashhdrs +__hashblkno(fcb,blkno));
110     
111     /* Try to find the page in buffer pool. */
112     if ((p = _findblock(fcb, unixfd, blkno)) == NULL) {
113         
114         /* Page is not in the pool - install it. */
115         if (mode != ISFIXNOREAD) {
116             p = _getavail();                 /* Get free page from pool */
117             _isdln_insert(hashl,&p->isb_hash); /* Insert into hash list */
118             
119             _isseekpg(unixfd, blkno);
120             _isreadpg(unixfd, p->isb_buffer);
121
122             p->isb_flags = ISB_READ;
123             p->isb_oldcopy = NULL;
124             p->isb_fcb = fcb;
125             p->isb_unixfd  = unixfd;
126             p->isb_blkno = blkno;
127         }
128         else
129             p = NULL;
130     }   
131     
132     if (p && (p->isb_flags & ISB_FIXED)==0) {
133         
134         /* Remove buffer from pavail (or pchangl) list. */
135         _isdln_remove(&p->isb_aclist);
136
137
138         if (!(p->isb_flags & ISB_CHANGE))
139             availn--;
140
141     }
142     
143     if (mode == ISFIXREAD) {
144         assert(p);
145         
146         if(!(p->isb_flags & ISB_FIXED)) {  
147             
148             /* Add buffer to pfixl list. */
149             _isdln_append(pfixl,&p->isb_flist);
150             p->isb_flags |= ISB_RFIXED;
151             
152         }
153         return (p);
154     }   /* if (p) */
155     else {
156         
157         /* If buffer is already fixed for write, no other actions are necces. */
158         if (p && p->isb_flags & ISB_FIXED) {
159             assert((p->isb_flags & ISB_RFIXED) == 0); /* Buffer cannot be */
160             /* fixed for read when is fixed */
161             /* being fixed for write */
162             return (p);
163         }
164         
165         /* Create shadow page */
166         p2 = _getavail();                    /* Get free page from pool */
167         availn--;
168         _isdln_remove(&p2->isb_aclist);      /* Remove from pavail */
169         _isdln_insert(hashl,&p2->isb_hash);  /* Insert into hash list */
170         _isdln_insert(pfixl,&p2->isb_flist); /* Insert into pfixl list */
171         
172         p2->isb_fcb = fcb;
173         p2->isb_unixfd = unixfd;
174         p2->isb_blkno = blkno;
175         p2->isb_flags = ISB_READ|ISB_WFIXED; /* Mark buffer as dirty */
176         
177         if (mode == ISFIXWRITE)              /* Copy buffer content */
178             memcpy(p2->isb_buffer,p->isb_buffer,ISPAGESIZE);
179         
180         p2->isb_oldcopy = p;
181         
182         /* Make old copy */
183         if (p) {
184             assert ((p->isb_flags & ISB_FIXED) == 0);
185             assert((p->isb_flags & ISB_OLDCOPY) == 0);
186             
187             p->isb_flags |= ISB_OLDCOPY;
188             _isdln_remove(&p->isb_hash);
189         }
190         
191         return (p2);
192     }
193 }
194
195 void
196 _isdisk_unfix (Bufhdr *p)
197 {
198     if (!(p->isb_flags & ISB_FIXED))
199     assert(p->isb_flags & ISB_FIXED);
200     
201     if (p->isb_flags & ISB_WFIXED)           /* Unfix at commit/abort time */
202         return;
203     
204     p->isb_flags &= ~ISB_FIXED;              /* Clear bit */
205     _isdln_remove(&p->isb_flist);            /* Remove from pfixl */
206     
207     /* Append to pavail or pchangl list. */
208     if (p->isb_flags & ISB_CHANGE)
209         _isdln_append(pchangl,&p->isb_aclist); /* Append to pchangl list */
210     else {
211         _isdln_append(pavail,&p->isb_aclist); /* Append to pavail list */
212         availn++;
213     }
214 }
215
216 void
217 _isdisk_commit1 (Bufhdr *p)
218 {
219     _commit1buffer(p);
220 }
221
222 void
223 _isdisk_commit(void)
224 {
225     Bufhdr *p;
226     struct dlink           *e;
227     
228     while ((e = _isdln_first(pfixl)) != pfixl) {
229         p = GETBASE(e,bufhdr,isb_flist);  /* Get pointer to bufhdr */
230         assert(p->isb_flags & ISB_WFIXED);
231         _commit1buffer(p);
232     }
233 }
234
235 void
236 _isdisk_rollback(void)
237 {
238     Bufhdr      *p;
239     struct dlink           *e;
240     
241     while ((e = _isdln_first(pfixl)) != pfixl) {
242         p = GETBASE(e,bufhdr,isb_flist);  /* Get pointer to bufhdr */
243         assert(p->isb_flags & ISB_FIXED);
244         if (p->isb_flags & ISB_WFIXED)
245             _rollback1buffer(p);
246         else 
247             _isdisk_unfix(p);
248     }
249 }
250
251 Bufhdr *
252 _isdisk_refix(Bufhdr *p, int newmode)
253 {
254     Blkno       blkno = p->isb_blkno;
255     Fcb         *fcb = p->isb_fcb;
256     int         unixfd = p->isb_unixfd;
257     
258     assert(newmode == ISFIXWRITE);
259     
260     if (p->isb_flags & ISB_RFIXED) {    
261         _isdisk_unfix(p);
262         return (_isdisk_fix(fcb, unixfd, blkno, ISFIXWRITE));
263     }
264     else
265         return (p);
266 }
267
268 void
269 _isdisk_sync(void)
270 {
271     extern time_t _istimeget();
272     Bufhdr *p;
273     struct dlink           *e;
274     
275     while ((e = _isdln_first(pchangl)) != pchangl) {
276         p = GETBASE(e,bufhdr,isb_aclist);  /* Get pointer to bufhdr */
277         assert(p->isb_flags & ISB_CHANGE);
278         assert((p->isb_flags & ISB_FIXED)==0);
279         _flush1buffer(p);
280     }
281 }
282
283 void
284 _isdisk_inval(void)
285 {
286     extern time_t _istimeget();
287     Bufhdr *p;
288     struct dlink           *e;
289     
290     /* ensure pavail is initialized before using it */
291
292     if (pavail->dln_forward == 0) {
293             _isdln_makeempty(pavail);
294     }
295
296     e = pavail;
297
298     while ((e = _isdln_prev(e)) != pavail) {
299         p = GETBASE(e,bufhdr,isb_aclist);  /* Get pointer to bufhdr */
300     
301         if ((p->isb_flags & ISB_READ) == 0)  
302             break;
303
304         _isdln_remove(&p->isb_hash);
305         p->isb_flags = ISB_NODATA;           /* Mark as no data in the buffer */
306     }
307 }
308
309
310 #if ISDEBUG
311 _isdisk_dumphd(void)
312 {
313     Bufhdr *p;
314     int                     i;
315     
316     (void)printf("\nInd isfd   blkno mode temp oldcopy\n");
317     for (p = bufhdrs, i = 0; i < ISMAXBUFFERS; p++,i++)
318         if (p->isb_flags != ISB_NODATA)
319             (void) printf("%3d: %3d  %6d   %2x     %3d\n",i,
320                           _isfd_getisfd(p->isb_pisfd),
321                           p->isb_blkno,p->isb_flags,
322                           p->isb_oldcopy?(p->isb_oldcopy - bufhdrs):-1);
323 }
324
325 aclistdump(struct dlink *lh)
326 {
327     Bufhdr *p;
328     struct dlink           *e;
329     
330     for (e = _isdln_first(lh); e != lh; e = _isdln_next(e)) {
331         p = GETBASE(e,bufhdr,isb_aclist);  /* Get pointer to bufhdr */
332         (void) printf("%3d: %3d  %6d   %2x     %3d\n",p-bufhdrs,
333                       _isfd_getisfd(p->isb_pisfd),
334                       p->isb_blkno,p->isb_flags,
335                       p->isb_oldcopy?(p->isb_oldcopy - bufhdrs):-1);
336     }
337 }
338
339 flistdump(struct dlink *lh)
340 {
341     Bufhdr *p;
342     struct dlink           *e;
343     
344     for (e = _isdln_first(lh); e != lh; e = _isdln_next(e)) {
345         p = GETBASE(e,bufhdr,isb_flist);  /* Get pointer to bufhdr */
346         (void) printf("%3d: %3d  %6d   %2x     %3d\n",p-bufhdrs,
347                       _isfd_getisfd(p->isb_pisfd),
348                       p->isb_blkno,p->isb_flags,
349                       p->isb_oldcopy?(p->isb_oldcopy - bufhdrs):-1);
350     }
351 }
352
353 #endif
354
355
356 /*------------------------ Local functions ---------------------------------*/
357
358 Static void
359 _disk_init(void)
360 {
361     static Bool  initialized = FALSE;
362     int i;
363     
364     if (initialized == TRUE)
365         return;
366
367     initialized = TRUE;
368     
369     /* Initialize hash queue list heads. */
370     for (i = 0; i < ISHASHHDRS; i++) {
371         _isdln_makeempty(hashhdrs+i);
372     }
373
374     /* initialize pavail, pchangel, and pfixl lists to empty. */
375
376     _isdln_makeempty(pavail);
377     _isdln_makeempty(pchangl);
378     _isdln_makeempty(pfixl);
379     
380     /* Link all buffers into pavail list. */
381     for (i = 0; i < ISMAXBUFFERS; i++) {
382         bufhdrs[i].isb_buffer = _ismalloc(ISPAGESIZE);
383         _isdln_append(pavail,&bufhdrs[i].isb_aclist);
384         availn++;
385     }
386     
387     /* Set maxavailn and minavailn. */
388     minavailn = (ISMAXBUFFERS * MINAVAILN) / 100;
389     maxavailn = (ISMAXBUFFERS * MAXAVAILN) / 100;
390 }
391
392 /* _getavail() - get available buffer in disk */
393 Static Bufhdr *
394 _getavail(void)
395 {
396     Bufhdr *p;
397     struct dlink  *q;
398     
399     if ((q = _isdln_first(pavail)) == pavail) {
400         _isfatal_error("No buffer in pool available");
401     }
402     
403     p = GETBASE(q,bufhdr,isb_aclist);
404     
405     if (p->isb_flags & ISB_READ) {           /* Remove from hash queue */
406         _isdln_remove(&p->isb_hash);
407         p->isb_flags = ISB_NODATA;           /* Mark as no data in the buffer */
408     }
409     
410     return ((Bufhdr *) p);
411 }
412
413 /* _findblock() - Find block in buffer pool */
414 Static Bufhdr *
415 _findblock(Fcb *fcb, int unixfd, Blkno blkno)
416 {
417     Bufhdr *p;
418     struct dlink           *lh, *e;
419     int                    hashval;
420     
421     hashval = __hashblkno(fcb,blkno);
422     
423     lh = hashhdrs + hashval;                 /* lh is list head */
424     for (e = _isdln_first(lh); e != lh; e = _isdln_next(e)) {
425         p = GETBASE(e,bufhdr,isb_hash);  /* Get pointer to bufhdr */
426         if (p->isb_blkno == blkno && p->isb_fcb == fcb && p->isb_unixfd == unixfd) {
427             assert(p->isb_flags != ISB_NODATA); 
428             return(p);
429         }
430     }
431     
432     return (NULL);
433 }
434
435 /* _commit1buffer() - Commit changes to buffer */
436 Static void
437 _commit1buffer(Bufhdr *p)
438 {
439     assert(p->isb_flags & ISB_WFIXED);       /* Fixed for read buffers should */
440     /* go through _isdisk_unfix() */
441     
442     /* Free old permanent buffer if any exists. */
443     if (p->isb_oldcopy) {
444         _makenodata(p->isb_oldcopy);         /* Make this buffer available */
445     }
446     
447     /* Remove buffer from list of fixed buffers. */
448     /* Append buffer to list of changed buffers. */
449     _isdln_remove(&p->isb_flist);
450     _isdln_append(pchangl,&p->isb_aclist);
451     p->isb_flags &= ~ISB_FIXED;
452     p->isb_flags |= ISB_CHANGE;
453 }
454
455 /* _rollback1buffer() - Rollback changes to buffer */
456 Static void
457 _rollback1buffer(Bufhdr *p)
458 {
459     Bufhdr      *p2;
460     
461     assert(p->isb_flags & ISB_WFIXED);       /* Fixed for read buffers should */
462     /* go through _isdisk_unfix() */
463     
464     /* Re-install old copy if that exists. */
465     if ((p2 = p->isb_oldcopy) != NULL) {
466         if (p2->isb_flags & ISB_CHANGE) {
467             _isdln_append(pchangl,&p2->isb_aclist);
468         }
469         else {
470             _isdln_append(pavail,&p2->isb_aclist);
471             availn++;
472         }
473         p2->isb_flags &= ~ISB_OLDCOPY;       /* Clear bit */
474         
475         /* See implementation of _isdln_append() that this will work. */
476         _isdln_append(&p->isb_hash,&p2->isb_hash); /* Insert into hash list */
477     }   
478     
479     
480     _isdln_remove(&p->isb_hash);             /* Remove bufer from hash list */
481     _isdln_remove(&p->isb_flist);            /* Remove bufer from pfixl */
482     _makenodata(p);                          /* Make this buffer available */
483 }
484
485 /* _makenodata() - make buffer available with no data in it*/
486 Static void
487 _makenodata(Bufhdr *p)
488 {
489     assert(p->isb_flags & ISB_READ);
490     
491     p->isb_flags = ISB_NODATA;
492     _isdln_insert(pavail,&p->isb_aclist);
493     availn++;
494 }
495
496 /* _flush1buffer() - flush buffer to disk */
497 Static void
498 _flush1buffer(Bufhdr *p)
499 {
500     assert(p->isb_flags & ISB_CHANGE);
501     
502     _isseekpg(p->isb_unixfd, p->isb_blkno);
503     _iswritepg(p->isb_unixfd, p->isb_buffer);
504     
505     p->isb_flags &= ~ISB_CHANGE;             /* clear change flag */
506     
507     _isdln_remove(&p->isb_aclist);           /* Remove from pchangl */
508     _isdln_append(pavail,&p->isb_aclist);    /* Append to pavail */
509     availn++;
510 }