struct cch_buf * _cch_getblk( struct cch_f *cch_info, /* cch_f structure for the file */ struct fdinfo *llfio, /* ffio file descriptor for underlying layer */ off_t fileaddr, /* bit offset within the file of the buffer. * This number must be a multiple of the buffer * size. */ int64 *nblkp, /* on input, the number of contiguous buffer * blocks sought. On output, assigned the * actual number of contiguous buffer blocks * assigned. */ int rd, /* 0 if all of the new blocks may be * assigned without reading the file page. * != 0 if the pages must be read. */ int valid, /* 0 if the CCH_VALIDBUFFER bit should */ /* not be set in the new blocks */ struct ffsw *stat /* pointer to status return word */ ) { int i, nbu, ret; int bs; int lru_id; /* buffer number of least recently */ /* used buffer. */ int limit; int64 nblk; off_t endaddr, firstpaddr, faddr; long *wptr; long lru_tm; struct cch_buf *cubuf; struct cch_buf *cbufs; struct cch_buf *fb; CCH_DEBUG(("_cch_getblk EN: to bit offset %d\n",fileaddr)); nbu = cch_info->nbufs; cbufs = cch_info->bufs; bs = cch_info->bsize; nblk = *nblkp; if (nblk > 1) { /* * Find the first page in the consecutive list of pages which * is buffer-resident. */ endaddr = fileaddr + nblk * bs; firstpaddr = endaddr; for (i=0; i<nbu; i++) { off_t x; cubuf = &cbufs[i]; x = cubuf->filead; if (fileaddr <= x && x < firstpaddr) firstpaddr = x; } if (firstpaddr < endaddr) /* a page is buffer resident */ nblk = *nblkp = (firstpaddr - fileaddr) / bs; if (nblk <= 0) return((struct cch_buf *)NULL); /* shouldn't happen ! */ } /* * Find the least-recently accessed sequence of *nblkp contiguous buffers. * Free buffers are counted as if their last access time was 0. * Search the buffers in groups of size nblk to speed this search and * reduce fragmentation of the cache. When nblk>1, this algorithm * approximates LRU and, most importantly, is deterministic. */ lru_tm = MAXLONG; /* min _rtc() value in upcoming loop */ lru_id = 0; for (i=0; i<(nbu-nblk+1); i+=nblk) { long last_access = 0; /* free pages have last_access == 0 */ if (cbufs[i].filead >= 0) last_access = cbufs[i].atime; if (last_access < lru_tm) { lru_tm = last_access; lru_id = i; } } /* * Use the least recently used page buffer or group of page buffers. * Flush any of these page buffers which have the dirty bit set. When * several adjacent buffers are dirty and correspond to adjacent pages * in the file, they can be flushed with one request. */ fb = &cbufs[lru_id]; for (i=0; i<nblk; i++) { int contig = 0; /* number of contiguous dirty buffers */ faddr = fb[i].filead; if (faddr == -1) continue; /* buffer is free */ while (i+contig < nblk && (fb[i+contig].flags & CCH_DIRTY) && fb[i+contig].filead == faddr) { if (fb[i+contig].lastdata || fb[i+contig].firstdata) { if (contig == 0) contig = 1; break; } contig++; faddr += bs; } if (contig > 0) { if (faddr > cch_info->fsize) { /* eof is in the last buffer */ /* clear it if necessary */ if ((fb[i+contig-1].flags & CCH_ZEROED) == 0){ bitptr toptr; off_t eofaddr; int pgoff; eofaddr = CCHFLOOR(cch_info->fsize, bs); pgoff = cch_info->fsize - eofaddr; SET_BPTR(toptr, INC_BPTR(fb[i+contig-1].buf, pgoff)); CCH_MEMCLEAR(toptr,(bs - pgoff)); fb[i+contig-1].flags |= CCH_ZEROED; } } ret = _cch_wrabuf(cch_info, llfio, &fb[i], BITS2BYTES(bs), BITS2BYTES(fb[i].filead), contig, &cch_info->feof, #if defined(__mips) || defined(_LITTLE_ENDIAN) 's', /* flush synchronously */ #else 'a', /* flush asynchronously */ #endif stat); if (ret == ERR) return((struct cch_buf *)NULL); i += contig - 1; } } /* * Wait for any active page buffer I/O, and then requisition the buffers * for the appropriate file pages. */ for (i=0; i<nblk; i++) { if (fb[i].flags & (CCH_WRITING | CCH_READING)) { CCHWAITIO(llfio,&fb[i],stat,ret); if (ret == ERR) return((struct cch_buf *)NULL); } fb[i].filead = fileaddr + i * bs; fb[i].flags = CCH_VALID; fb[i].firstdata = fb[i].lastdata = 0; if (valid) fb[i].flags |= CCH_VALIDBUFFER; } /* * Now start the synchronous reading of the file page into the buffer. If * all of the pages lie beyond the EOF, then suppress the read. */ if (rd) { if (fileaddr < cch_info->feof) { int by_tran; fb->sw.sw_flag = 0; /* indicate I/O in progress */ ret = _cch_rdabuf(cch_info, llfio, fb, BITS2BYTES(bs), BITS2BYTES(fb->filead), nblk, 's',stat); if (ret == ERR) return((struct cch_buf *)NULL); /* * Zero portions of the buffers past the end of file. */ by_tran = fb->sw.sw_count; #ifdef CCH_SDS_SUPPORTED if (cch_info->optflags & CCHOPT_SDS) { int ret; ret = _sdsset( (BPTR2CP(fb->buf) - (char*)NULL) + by_tran, 0, nblk * BITS2BYTES(bs) - by_tran); if (ret == ERR) { _SETERROR(stat, errno, 0); return((struct cch_buf *)NULL); } } else #endif { if ((nblk*BITS2BYTES(bs)-by_tran) != 0) (void)memset(BPTR2CP( fb->buf) + by_tran, 0, nblk * BITS2BYTES(bs) - by_tran); } for (i=0; i<nblk; i++) { fb[i].flags |= CCH_ZEROED; } } else { /* page lies beyond EOF */ /* * Zero the entire buffer. */ #ifdef CCH_SDS_SUPPORTED if (cch_info->optflags & CCHOPT_SDS) { int ret; ret = _sdsset( (BPTR2CP(fb->buf) - (char*)NULL), 0, nblk * BITS2BYTES(bs)); if (ret == ERR) { _SETERROR(stat, errno, 0); return((struct cch_buf *)NULL); } for (i=0; i<nblk; i++) { fb[i].flags |= CCH_ZEROED; } } else #endif if (fileaddr < cch_info->fsize){ /* this block is between cch_info->feof and */ /* cch_info->fsize, so we must zero it */ /* Logic in other parts of this layer will */ /* only zero what is beyond cch_info->fsize */ #ifdef _CRAY1 wptr = BPTR2WP(fb->buf); limit = (nblk * bs) >> 6; /* convert to words */ /* this loop vectorizes! */ for (i=0; i<limit; i++) wptr[i] = 0; #else memset(BPTR2CP(fb->buf), 0, (nblk * BITS2BYTES(bs))); #endif for (i=0; i<nblk; i++) { fb[i].flags |= CCH_ZEROED; } } } }
/* * _cch_write * * Process write requests for the cache layer. * * Return value: * * The number of bytes transferred is returned upon successful completion. * If an error occurs, -1 is returned. * * The stat->sw_stat field is set to FFCNT upon normal return. */ ssize_t _cch_write( struct fdinfo *fio, /* ffio file descriptor. */ bitptr datptr, /* bit pointer to the user's data. */ size_t nbytes, /* Nuber of bytes to be written. */ struct ffsw *stat, /* pointer to status return word */ int fulp, /* full or partial write mode flag */ int *ubcp /* pointer to unused bit count. On return, */ /* *ubcp is updated to contain the unused bit */ /* count in the data returned. */ ) { off_t cpos; /* bit position in file */ int64 moved; /* number of bits transfered */ int64 bytes_moved; /* number of bytes transfered */ int64 morebits; /* bits moved in current iteration */ int64 numblocks; /* num of pages to process this iter */ int pgoff; off_t fileaddr; off_t eofaddr; int gb_rd; /* nonzero if pages must be read */ int valid; /* nonzero if CCH_VALIDBUFFER should */ /* be set */ int64 nbits; int64 i; int bs, nbu; off_t olpos, endpos, endoff; bitptr toptr; struct ffsw locstat; struct fdinfo *llfio; struct cch_f *cch_info; struct cch_buf *cubuf; int err; short firsteof = 0; short setfirst; CCH_DEBUG(("_cch_write EN: nbytes=%d fulp=%d ubc=%d\n",nbytes,fulp, *ubcp)); CLRSTAT(locstat); cch_info = (struct cch_f *)fio->lyr_info; nbits = BYTES2BITS(nbytes) - *ubcp; fio->rwflag = WRITIN; #if defined(__mips) || defined(_LITTLE_ENDIAN) /* Although this layer is capable of handling non-zero ubc */ /* and bitptrs that aren't on a byte boundary, we are not */ /* supporting this right now on mips systems. */ if (*ubcp != 0) { err = FDC_ERR_UBC; goto err1_ret; } if ((BPBITOFF(datptr) & 07) != 0) { err = FDC_ERR_REQ; goto err1_ret; } #endif if (nbits == 0) { /* quick return for nbits == 0*/ SETSTAT(stat, FFCNT, 0); return(0); } /* * Move data from user to buffer */ llfio = fio->fioptr; bs = cch_info->bsize; /* bit size of each buffer */ cpos = cch_info->cpos; /* current file position */ olpos = cpos; /* save original position */ fileaddr = CCHFLOOR(cpos,bs); /* bit offset within the file of the * start of the current page */ if (cpos > cch_info->fsize) { firsteof = 1; /* Is the page with eof in memory? */ /* If so, zero out the portion beyond eof. */ eofaddr = CCHFLOOR(cch_info->fsize, bs); CCH_FINDBLK(cch_info, eofaddr, cubuf); if (cubuf != NULL && (cubuf->flags & CCH_ZEROED) == 0) { #ifdef CCH_SDS_SUPPORTED if (cch_info->optflags & CCHOPT_SDS) { /* should never happen */ ERETURN(stat, FDC_ERR_INTERR, 0); } #endif pgoff = cch_info->fsize - eofaddr; /* offset of eof */ /* within the page */ SET_BPTR(toptr, INC_BPTR(cubuf->buf, pgoff)); morebits = bs - pgoff; if (morebits != 0) { CCH_MEMCLEAR(toptr, morebits); } cubuf->flags |= CCH_ZEROED; } } while (nbits > 0) { /* * Find the cache buffer assigned to the current page. If * no buffer is currently assigned, then _cch_getblk assigns * one. */ pgoff = cpos - fileaddr; /* offset within the page */ numblocks = 1; /* number of of pages to prcess * in this iteration */ CCH_FINDBLK(cch_info, fileaddr, cubuf); if (cubuf == NULL) { /* if data not buffer-resident*/ if (nbits > cch_info->bypasssize #ifdef CCH_SDS_SUPPORTED && !(cch_info->optflags & CCHOPT_SDS) #endif ) { /* Maybe we can bypass buffering */ if ((morebits= _cch_bypass(cch_info, nbits, cpos, datptr, fileaddr, 'w', llfio, &locstat))>0) goto adjust; else if (morebits < 0) { /* Is it right to return the count */ /* in locstat? Because we might */ /* have read some data... */ goto er1; } /* we weren't able to bypass buffering */ } morebits = nbits; endpos = cpos + morebits; /*1 bit past the end*/ endoff = endpos - CCHFLOOR(endpos,bs); if (endpos > fileaddr + bs) { numblocks = (endpos-fileaddr-1)/bs + 1; nbu = cch_info->nbufs; /* * Handle at most a cache full at a time */ if (numblocks > nbu) { numblocks = nbu; endpos = fileaddr + nbu * bs; endoff = 0; morebits = endpos - cpos; } } /* * It is possible that the first or last * page must be read because the transfer * fills only part of these pages. In each * iteration, _cch_getblk requires that * consecutive buffer pages must all be read, * or else all be assigned without pre-reading. * The following code breaks off the current * portion of the transfer when necessary to * accomplish this. */ if (numblocks > 1) { if (numblocks == 2) { if ((pgoff == 0) != (endoff == 0)) { /* process only first page */ numblocks = 1; endoff = 0; morebits = bs - pgoff; } } else { if (pgoff) { /* process only first page */ numblocks = 1; endoff = 0; morebits = bs - pgoff; } else if (endoff) { /* process all but last page */ numblocks -= 1; endoff = 0; morebits -= endoff; } } } /* * Request that _cch_getblk read in the file * pages if partial pages of data will be * written. */ gb_rd = (pgoff || endoff); /* The pages will be valid if we do not */ /* have to read them. That's because */ /* we will be writing to the entire page */ /* The page will also be valid if we do read it */ valid = 1; setfirst = 0; if (gb_rd && #ifdef CCH_SDS_SUPPORTED !(cch_info->optflags & CCHOPT_SDS) && #endif (numblocks == 1) && ((fileaddr+bs) < cch_info->feof) && (_CCH_ALIGN(pgoff) && _CCH_ALIGN(endoff))) { /* do we really need to read the page in? */ /* if pgoff and endoff are properly aligned, */ /* we do not */ /* Note that if any part of the page is */ /* beyond feof, we want to read it in. */ /* That's because code in _cch_rdabuf */ /* that handles having a partially dirty */ /* page expects to be able to read the */ /* data preceding the dirty data */ gb_rd = 0; valid = 0; /* the page will not be valid */ setfirst = 1; } cubuf = _cch_getblk(cch_info, llfio, fileaddr, &numblocks, gb_rd, valid, &locstat); if (cubuf == NULL) { goto er1; } if (setfirst) { cubuf->firstdata = pgoff; if (endoff == 0) cubuf->lastdata = bs; else cubuf->lastdata = endoff; } if (firsteof && pgoff != 0) { /* There is a gap between the eof and */ /* this data. Zero it if necessary. */ if ((cubuf->flags & CCH_ZEROED) == 0) { int zbits; #ifdef CCH_SDS_SUPPORTED if (cch_info->optflags & CCHOPT_SDS) { /* should never happen */ ERETURN(stat, FDC_ERR_INTERR, 0); } #endif if ((eofaddr == fileaddr)) { /* the eof is on this page */ zbits = bs - (cch_info->fsize - eofaddr); SET_BPTR(toptr, INC_BPTR(cubuf->buf, (cch_info->fsize - eofaddr))); } else { /* the eof is not on this page */ /* zero the entire page */ zbits = bs; toptr = cubuf->buf; } CCH_MEMCLEAR(toptr, zbits); cubuf->flags |= CCH_ZEROED; } } morebits = MIN(nbits, bs * numblocks - pgoff); /* remember the last buffer page for next time */ cch_info->cubuf = cubuf + numblocks - 1; } else { morebits = MIN(nbits, bs - pgoff); if (!(cubuf->flags & CCH_VALIDBUFFER)) { /* The buffer is there, but it */ /* is not entirely valid, because */ /* we never read into it. */ /* We can continue to just dirty it, */ /* provided that the dirty part is */ /* contiguous, and is properly aligned */ endoff = pgoff + morebits; if ((pgoff == cubuf->lastdata && _CCH_ALIGN(endoff))|| (endoff == cubuf->firstdata && _CCH_ALIGN(pgoff)) || (pgoff >= cubuf->firstdata && endoff <= cubuf->lastdata)) { cubuf->firstdata = MIN(pgoff, cubuf->firstdata); cubuf->lastdata = MAX(endoff, cubuf->lastdata); if (cubuf->firstdata == 0 && cubuf->lastdata == bs) { cubuf->lastdata = 0; cubuf->flags |=CCH_VALIDBUFFER; } } else { /* We can't just keep on putting */ /* stuff in the buffer without */ /* prereading it. So, we will call */ /* _cch_rdabuf, which has the */ /* smarts to read only the non-dirty */ /* parts */ if (_cch_rdabuf(cch_info, llfio, cubuf, BITS2BYTES(cch_info->bsize), BITS2BYTES(cubuf->filead), 1, 's', &locstat)) { goto er1; } } } } for (i=0; i<numblocks; i++) { /* adjust last access time */ CCH_CHRONOMETER(cubuf[i],cch_info); cubuf[i].flags |= CCH_DIRTY; } SET_BPTR(toptr, INC_BPTR(cubuf->buf, pgoff)); #ifdef CCH_SDS_SUPPORTED if (cch_info->optflags & CCHOPT_SDS) { if (_sds_fr_mem(toptr, datptr, morebits) == ERR) ERETURN(stat, errno, 0); } else _CCH_MOV_BITS(toptr, datptr, morebits); /* contiguous bufs */ #else _CCH_MOV_BITS(toptr, datptr, morebits); /* contiguous bufs */ #endif adjust: SET_BPTR(datptr, INC_BPTR(datptr, morebits)); cpos += morebits; nbits -= morebits; fileaddr = CCHFLOOR(cpos,bs); /* bit offset within the file of the page */ firsteof = 0; if (cpos > cch_info->fsize) { cch_info->fsize = cpos; } } cch_info->cpos = cpos; moved = cpos - olpos; fio->recbits += moved; bytes_moved = BITS2BYTES(moved); SETSTAT(stat, FFCNT, bytes_moved); return(bytes_moved); err1_ret: ERETURN(stat, err, 0); er1: *stat = locstat; return(ERR); }