/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /*%% (c) Copyright 1993, 1994 Hewlett-Packard Company */ /*%% (c) Copyright 1993, 1994 International Business Machines Corp. */ /*%% (c) Copyright 1993, 1994 Sun Microsystems, Inc. */ /*%% (c) Copyright 1993, 1994 Novell, Inc. */ /*%% $XConsortium: isdiskbufs2.c /main/3 1995/10/23 11:37:47 rswiston $ */ /* * Copyright (c) 1988 by Sun Microsystems, Inc. */ /* * _isdiskbufs.c * * Description: * ISAM disk buffer managament * */ /************************ NON MAPPED I/O version ***************************/ #include "isam_impl.h" extern struct dlink *_isdln_next(), *_isdln_first(); #define ISMAXBUFFERS 200 /* Use 20 buffers */ #define ISHASHHDRS 256 /* Must be a power of two */ #define ISHASHMASK (ISHASHHDRS-1) #define __hashblkno(fcb,blkno) (((size_t)(fcb)+(blkno)) & ISHASHMASK) #define base ((char *)0) #define _isdln_insert(l,e) _isdln_base_insert(base,(l),(e)) #define _isdln_append(l,e) _isdln_base_append(base,(l),(e)) #define _isdln_remove(e) _isdln_base_remove(base,(e)) #define _isdln_first(l) _isdln_base_first(base,(l)) #define _isdln_next(l) _isdln_base_next(base,(l)) #define _isdln_prev(l) _isdln_base_prev(base,(l)) #define _isdln_makeempty(l) _isdln_base_makeempty(base,(l)) #define _isdln_isempty(l) _isdln_base_isempty(base,(l)) /*---------------------- Local data ---------------------------------------*/ static Bufhdr *_getavail(void); static Bufhdr *_findblock(Fcb *fcb, int unixfd, Blkno blkno); static void _disk_init(void); static void _commit1buffer(Bufhdr *p); static void _rollback1buffer(Bufhdr *p); static void _flush1buffer(Bufhdr *p); static void _makenodata(Bufhdr *p); Bufhdr bufhdrs [ISMAXBUFFERS]; struct dlink hashhdrs [ISHASHHDRS]; /* Heads of hashed lists */ struct dlink availlist; /* Available buffer list */ struct dlink *pavail = &availlist; struct dlink changelist; /* Change buffer list */ struct dlink *pchangl = &changelist; struct dlink fixlist; /* Fixed buffer list */ struct dlink *pfixl = &fixlist; static int availn; /* Number of available buffers */ static int minavailn; /* Minimum available buffers */ static int maxavailn; /* Stop flushing when * when maxavailn buffers * are available */ #define ISB_FIXED (ISB_RFIXED | ISB_WFIXED) #define MINAVAILN 40 /* in procent of total # buffers */ #define MAXAVAILN 60 /* in procent of total # buffers */ /* unixfd, .rec, .ind., .var */ Bufhdr * _isdisk_fix(Fcb *fcb, int unixfd, Blkno blkno, int mode) { Bufhdr *p, *p2; struct dlink *hashl; /* * Initialize some local data. */ _disk_init(); if (fcb->datfd == unixfd) assert(blkno != 0); /* Never access control page */ hashl = (hashhdrs +__hashblkno(fcb,blkno)); /* Try to find the page in buffer pool. */ if ((p = _findblock(fcb, unixfd, blkno)) == NULL) { /* Page is not in the pool - install it. */ if (mode != ISFIXNOREAD) { p = _getavail(); /* Get free page from pool */ _isdln_insert(hashl,&p->isb_hash); /* Insert into hash list */ _isseekpg(unixfd, blkno); _isreadpg(unixfd, p->isb_buffer); p->isb_flags = ISB_READ; p->isb_oldcopy = NULL; p->isb_fcb = fcb; p->isb_unixfd = unixfd; p->isb_blkno = blkno; } else p = NULL; } if (p && (p->isb_flags & ISB_FIXED)==0) { /* Remove buffer from pavail (or pchangl) list. */ _isdln_remove(&p->isb_aclist); if (!(p->isb_flags & ISB_CHANGE)) availn--; } if (mode == ISFIXREAD) { assert(p); if(!(p->isb_flags & ISB_FIXED)) { /* Add buffer to pfixl list. */ _isdln_append(pfixl,&p->isb_flist); p->isb_flags |= ISB_RFIXED; } return (p); } /* if (p) */ else { /* If buffer is already fixed for write, no other actions are necces. */ if (p && p->isb_flags & ISB_FIXED) { assert((p->isb_flags & ISB_RFIXED) == 0); /* Buffer cannot be */ /* fixed for read when is fixed */ /* being fixed for write */ return (p); } /* Create shadow page */ p2 = _getavail(); /* Get free page from pool */ availn--; _isdln_remove(&p2->isb_aclist); /* Remove from pavail */ _isdln_insert(hashl,&p2->isb_hash); /* Insert into hash list */ _isdln_insert(pfixl,&p2->isb_flist); /* Insert into pfixl list */ p2->isb_fcb = fcb; p2->isb_unixfd = unixfd; p2->isb_blkno = blkno; p2->isb_flags = ISB_READ|ISB_WFIXED; /* Mark buffer as dirty */ if (mode == ISFIXWRITE) /* Copy buffer content */ memcpy(p2->isb_buffer,p->isb_buffer,ISPAGESIZE); p2->isb_oldcopy = p; /* Make old copy */ if (p) { assert ((p->isb_flags & ISB_FIXED) == 0); assert((p->isb_flags & ISB_OLDCOPY) == 0); p->isb_flags |= ISB_OLDCOPY; _isdln_remove(&p->isb_hash); } return (p2); } } void _isdisk_unfix (Bufhdr *p) { if (!(p->isb_flags & ISB_FIXED)) assert(p->isb_flags & ISB_FIXED); if (p->isb_flags & ISB_WFIXED) /* Unfix at commit/abort time */ return; p->isb_flags &= ~ISB_FIXED; /* Clear bit */ _isdln_remove(&p->isb_flist); /* Remove from pfixl */ /* Append to pavail or pchangl list. */ if (p->isb_flags & ISB_CHANGE) _isdln_append(pchangl,&p->isb_aclist); /* Append to pchangl list */ else { _isdln_append(pavail,&p->isb_aclist); /* Append to pavail list */ availn++; } } void _isdisk_commit1 (Bufhdr *p) { _commit1buffer(p); } void _isdisk_commit(void) { Bufhdr *p; struct dlink *e; while ((e = _isdln_first(pfixl)) != pfixl) { p = GETBASE(e,bufhdr,isb_flist); /* Get pointer to bufhdr */ assert(p->isb_flags & ISB_WFIXED); _commit1buffer(p); } } void _isdisk_rollback(void) { Bufhdr *p; struct dlink *e; while ((e = _isdln_first(pfixl)) != pfixl) { p = GETBASE(e,bufhdr,isb_flist); /* Get pointer to bufhdr */ assert(p->isb_flags & ISB_FIXED); if (p->isb_flags & ISB_WFIXED) _rollback1buffer(p); else _isdisk_unfix(p); } } Bufhdr * _isdisk_refix(Bufhdr *p, int newmode) { Blkno blkno = p->isb_blkno; Fcb *fcb = p->isb_fcb; int unixfd = p->isb_unixfd; assert(newmode == ISFIXWRITE); if (p->isb_flags & ISB_RFIXED) { _isdisk_unfix(p); return (_isdisk_fix(fcb, unixfd, blkno, ISFIXWRITE)); } else return (p); } void _isdisk_sync(void) { extern time_t _istimeget(); Bufhdr *p; struct dlink *e; while ((e = _isdln_first(pchangl)) != pchangl) { p = GETBASE(e,bufhdr,isb_aclist); /* Get pointer to bufhdr */ assert(p->isb_flags & ISB_CHANGE); assert((p->isb_flags & ISB_FIXED)==0); _flush1buffer(p); } } void _isdisk_inval(void) { extern time_t _istimeget(); Bufhdr *p; struct dlink *e; /* ensure pavail is initialized before using it */ if (pavail->dln_forward == 0) { _isdln_makeempty(pavail); } e = pavail; while ((e = _isdln_prev(e)) != pavail) { p = GETBASE(e,bufhdr,isb_aclist); /* Get pointer to bufhdr */ if ((p->isb_flags & ISB_READ) == 0) break; _isdln_remove(&p->isb_hash); p->isb_flags = ISB_NODATA; /* Mark as no data in the buffer */ } } #if ISDEBUG _isdisk_dumphd(void) { Bufhdr *p; int i; (void)printf("\nInd isfd blkno mode temp oldcopy\n"); for (p = bufhdrs, i = 0; i < ISMAXBUFFERS; p++,i++) if (p->isb_flags != ISB_NODATA) (void) printf("%3d: %3d %6d %2x %3d\n",i, _isfd_getisfd(p->isb_pisfd), p->isb_blkno,p->isb_flags, p->isb_oldcopy?(p->isb_oldcopy - bufhdrs):-1); } aclistdump(struct dlink *lh) { Bufhdr *p; struct dlink *e; for (e = _isdln_first(lh); e != lh; e = _isdln_next(e)) { p = GETBASE(e,bufhdr,isb_aclist); /* Get pointer to bufhdr */ (void) printf("%3d: %3d %6d %2x %3d\n",p-bufhdrs, _isfd_getisfd(p->isb_pisfd), p->isb_blkno,p->isb_flags, p->isb_oldcopy?(p->isb_oldcopy - bufhdrs):-1); } } flistdump(struct dlink *lh) { Bufhdr *p; struct dlink *e; for (e = _isdln_first(lh); e != lh; e = _isdln_next(e)) { p = GETBASE(e,bufhdr,isb_flist); /* Get pointer to bufhdr */ (void) printf("%3d: %3d %6d %2x %3d\n",p-bufhdrs, _isfd_getisfd(p->isb_pisfd), p->isb_blkno,p->isb_flags, p->isb_oldcopy?(p->isb_oldcopy - bufhdrs):-1); } } #endif /*------------------------ Local functions ---------------------------------*/ Static void _disk_init(void) { static Bool initialized = FALSE; int i; if (initialized == TRUE) return; initialized = TRUE; /* Initialize hash queue list heads. */ for (i = 0; i < ISHASHHDRS; i++) { _isdln_makeempty(hashhdrs+i); } /* initialize pavail, pchangel, and pfixl lists to empty. */ _isdln_makeempty(pavail); _isdln_makeempty(pchangl); _isdln_makeempty(pfixl); /* Link all buffers into pavail list. */ for (i = 0; i < ISMAXBUFFERS; i++) { bufhdrs[i].isb_buffer = _ismalloc(ISPAGESIZE); _isdln_append(pavail,&bufhdrs[i].isb_aclist); availn++; } /* Set maxavailn and minavailn. */ minavailn = (ISMAXBUFFERS * MINAVAILN) / 100; maxavailn = (ISMAXBUFFERS * MAXAVAILN) / 100; } /* _getavail() - get available buffer in disk */ Static Bufhdr * _getavail(void) { Bufhdr *p; struct dlink *q; if ((q = _isdln_first(pavail)) == pavail) { _isfatal_error("No buffer in pool available"); } p = GETBASE(q,bufhdr,isb_aclist); if (p->isb_flags & ISB_READ) { /* Remove from hash queue */ _isdln_remove(&p->isb_hash); p->isb_flags = ISB_NODATA; /* Mark as no data in the buffer */ } return ((Bufhdr *) p); } /* _findblock() - Find block in buffer pool */ Static Bufhdr * _findblock(Fcb *fcb, int unixfd, Blkno blkno) { Bufhdr *p; struct dlink *lh, *e; int hashval; hashval = __hashblkno(fcb,blkno); lh = hashhdrs + hashval; /* lh is list head */ for (e = _isdln_first(lh); e != lh; e = _isdln_next(e)) { p = GETBASE(e,bufhdr,isb_hash); /* Get pointer to bufhdr */ if (p->isb_blkno == blkno && p->isb_fcb == fcb && p->isb_unixfd == unixfd) { assert(p->isb_flags != ISB_NODATA); return(p); } } return (NULL); } /* _commit1buffer() - Commit changes to buffer */ Static void _commit1buffer(Bufhdr *p) { assert(p->isb_flags & ISB_WFIXED); /* Fixed for read buffers should */ /* go through _isdisk_unfix() */ /* Free old permanent buffer if any exists. */ if (p->isb_oldcopy) { _makenodata(p->isb_oldcopy); /* Make this buffer available */ } /* Remove buffer from list of fixed buffers. */ /* Append buffer to list of changed buffers. */ _isdln_remove(&p->isb_flist); _isdln_append(pchangl,&p->isb_aclist); p->isb_flags &= ~ISB_FIXED; p->isb_flags |= ISB_CHANGE; } /* _rollback1buffer() - Rollback changes to buffer */ Static void _rollback1buffer(Bufhdr *p) { Bufhdr *p2; assert(p->isb_flags & ISB_WFIXED); /* Fixed for read buffers should */ /* go through _isdisk_unfix() */ /* Re-install old copy if that exists. */ if ((p2 = p->isb_oldcopy) != NULL) { if (p2->isb_flags & ISB_CHANGE) { _isdln_append(pchangl,&p2->isb_aclist); } else { _isdln_append(pavail,&p2->isb_aclist); availn++; } p2->isb_flags &= ~ISB_OLDCOPY; /* Clear bit */ /* See implementation of _isdln_append() that this will work. */ _isdln_append(&p->isb_hash,&p2->isb_hash); /* Insert into hash list */ } _isdln_remove(&p->isb_hash); /* Remove bufer from hash list */ _isdln_remove(&p->isb_flist); /* Remove bufer from pfixl */ _makenodata(p); /* Make this buffer available */ } /* _makenodata() - make buffer available with no data in it*/ Static void _makenodata(Bufhdr *p) { assert(p->isb_flags & ISB_READ); p->isb_flags = ISB_NODATA; _isdln_insert(pavail,&p->isb_aclist); availn++; } /* _flush1buffer() - flush buffer to disk */ Static void _flush1buffer(Bufhdr *p) { assert(p->isb_flags & ISB_CHANGE); _isseekpg(p->isb_unixfd, p->isb_blkno); _iswritepg(p->isb_unixfd, p->isb_buffer); p->isb_flags &= ~ISB_CHANGE; /* clear change flag */ _isdln_remove(&p->isb_aclist); /* Remove from pchangl */ _isdln_append(pavail,&p->isb_aclist); /* Append to pavail */ availn++; }