Пример #1
0
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;
				}
			}
		}
	}
Пример #2
0
/*
 * _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);
}