138882Smacklem /* 238882Smacklem * Copyright (c) 1989 The Regents of the University of California. 338882Smacklem * All rights reserved. 438882Smacklem * 538882Smacklem * This code is derived from software contributed to Berkeley by 638882Smacklem * Rick Macklem at The University of Guelph. 738882Smacklem * 838882Smacklem * Redistribution and use in source and binary forms are permitted 938882Smacklem * provided that the above copyright notice and this paragraph are 1038882Smacklem * duplicated in all such forms and that any documentation, 1138882Smacklem * advertising materials, and other materials related to such 1238882Smacklem * distribution and use acknowledge that the software was developed 1338882Smacklem * by the University of California, Berkeley. The name of the 1438882Smacklem * University may not be used to endorse or promote products derived 1538882Smacklem * from this software without specific prior written permission. 1638882Smacklem * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1738882Smacklem * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1838882Smacklem * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1938882Smacklem * 20*39487Smckusick * @(#)nfs_bio.c 7.4 (Berkeley) 11/03/89 2138882Smacklem */ 2238882Smacklem 2338882Smacklem #include "param.h" 2438882Smacklem #include "user.h" 2538882Smacklem #include "buf.h" 2638882Smacklem #include "vnode.h" 2738882Smacklem #include "trace.h" 2838882Smacklem #include "mount.h" 2938882Smacklem #include "nfsnode.h" 3038882Smacklem #include "nfsiom.h" 3138882Smacklem 3238882Smacklem /* True and false, how exciting */ 3338882Smacklem #define TRUE 1 3438882Smacklem #define FALSE 0 3538882Smacklem 3638882Smacklem /* 3738882Smacklem * Vnode op for read using bio 3838882Smacklem * Any similarity to readip() is purely coincidental 3938882Smacklem */ 4038882Smacklem nfs_read(vp, uio, offp, ioflag, cred) 4138882Smacklem register struct vnode *vp; 4238882Smacklem struct uio *uio; 4338882Smacklem off_t *offp; 4438882Smacklem int ioflag; 4538882Smacklem struct ucred *cred; 4638882Smacklem { 4738882Smacklem register struct nfsnode *np = VTONFS(vp); 4838882Smacklem struct buf *bp; 4938882Smacklem struct vattr vattr; 5038882Smacklem daddr_t lbn, bn, rablock; 51*39487Smckusick int diff, error = 0; 5238882Smacklem long n, on; 5338882Smacklem 5438882Smacklem if (!(ioflag & IO_NODELOCKED)) 5538882Smacklem nfs_lock(vp); 5638882Smacklem /* 5738882Smacklem * Avoid caching directories. Once everything is using getdirentries() 5838882Smacklem * this will never happen anyhow. 5938882Smacklem */ 6038882Smacklem if (vp->v_type == VDIR) { 6138882Smacklem error = nfs_readrpc(vp, uio, offp, cred); 6239340Smckusick if (!(ioflag & IO_NODELOCKED)) 6339340Smckusick nfs_unlock(vp); 6439340Smckusick return (error); 6538882Smacklem } 6639340Smckusick uio->uio_offset = *offp; 6738882Smacklem if (uio->uio_rw != UIO_READ) 6838882Smacklem panic("nfs_read mode"); 6938882Smacklem if (vp->v_type != VREG) 7038882Smacklem panic("nfs_read type"); 7138882Smacklem if (uio->uio_resid == 0) 7238882Smacklem goto out; 7338882Smacklem if (uio->uio_offset < 0) { 7438882Smacklem error = EINVAL; 7538882Smacklem goto out; 7638882Smacklem } 7738882Smacklem /* 7838882Smacklem * If the file's modify time on the server has changed since the 7938882Smacklem * last read rpc or you have written to the file, 8038882Smacklem * you may have lost data cache consistency with the 8138882Smacklem * server, so flush all of the file's data out of the cache. 8238882Smacklem * This will implicitly bring the modify time up to date, since 8338882Smacklem * up to date attributes are returned in the reply to any write rpc's 8438882Smacklem * NB: This implies that cache data can be read when up to 8538882Smacklem * NFS_ATTRTIMEO seconds out of date. If you find that you need current 8638882Smacklem * attributes this could be forced by setting n_attrstamp to 0 before 8738882Smacklem * the nfs_getattr() call. 8838882Smacklem */ 8938882Smacklem if (np->n_flag & NMODIFIED) { 9038882Smacklem np->n_flag &= ~NMODIFIED; 9139340Smckusick if (error = nfs_blkflush(vp, (daddr_t)0, np->n_size, TRUE)) 9238882Smacklem goto out; 9338882Smacklem if (error = nfs_getattr(vp, &vattr, cred)) 9438882Smacklem goto out; 9538882Smacklem np->n_mtime = vattr.va_mtime.tv_sec; 9638882Smacklem } else { 9738882Smacklem if (error = nfs_getattr(vp, &vattr, cred)) 9838882Smacklem goto out; 9938882Smacklem if (np->n_mtime != vattr.va_mtime.tv_sec) { 10039358Smckusick if (error = nfs_blkflush(vp, (daddr_t)0, 10139358Smckusick np->n_size, TRUE)) 10238882Smacklem goto out; 10338882Smacklem np->n_mtime = vattr.va_mtime.tv_sec; 10438882Smacklem } 10538882Smacklem } 10639340Smckusick np->n_flag |= NBUFFERED; 10738882Smacklem do { 10838882Smacklem lbn = uio->uio_offset >> NFS_BIOSHIFT; 10938882Smacklem on = uio->uio_offset & (NFS_BIOSIZE-1); 11038882Smacklem n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid); 11138882Smacklem diff = np->n_size - uio->uio_offset; 11238882Smacklem if (diff <= 0) 11338882Smacklem goto out; 11438882Smacklem if (diff < n) 11538882Smacklem n = diff; 11638882Smacklem bn = lbn*(NFS_BIOSIZE/DEV_BSIZE); 11738882Smacklem rablock = (lbn+1)*(NFS_BIOSIZE/DEV_BSIZE); 11839358Smckusick if (np->n_lastr+1 == lbn && np->n_size > (rablock*DEV_BSIZE)) 11938882Smacklem error = breada(vp, bn, NFS_BIOSIZE, rablock, NFS_BIOSIZE, 12038882Smacklem cred, &bp); 12138882Smacklem else 12238882Smacklem error = bread(vp, bn, NFS_BIOSIZE, cred, &bp); 12338882Smacklem np->n_lastr = lbn; 12438882Smacklem if (bp->b_resid) { 12538882Smacklem diff = (on >= (NFS_BIOSIZE-bp->b_resid)) ? 0 : 12638882Smacklem (NFS_BIOSIZE-bp->b_resid-on); 12738882Smacklem n = MIN(n, diff); 12838882Smacklem } 12938882Smacklem if (error) { 13038882Smacklem brelse(bp); 13138882Smacklem goto out; 13238882Smacklem } 13338882Smacklem if (n > 0) 13438882Smacklem error = uiomove(bp->b_un.b_addr + on, (int)n, uio); 13538882Smacklem if (n+on == NFS_BIOSIZE || uio->uio_offset == np->n_size) 13638882Smacklem bp->b_flags |= B_AGE; 13738882Smacklem brelse(bp); 13838882Smacklem } while (error == 0 && uio->uio_resid > 0 && n != 0); 13938882Smacklem out: 14038882Smacklem *offp = uio->uio_offset; 14139340Smckusick if (!(ioflag & IO_NODELOCKED)) 14238882Smacklem nfs_unlock(vp); 14338882Smacklem return (error); 14438882Smacklem } 14538882Smacklem 14638882Smacklem /* 14738882Smacklem * Vnode op for write using bio 14838882Smacklem */ 14938882Smacklem nfs_write(vp, uio, offp, ioflag, cred) 15038882Smacklem register struct vnode *vp; 15138882Smacklem register struct uio *uio; 15238882Smacklem off_t *offp; 15338882Smacklem int ioflag; 15438882Smacklem struct ucred *cred; 15538882Smacklem { 15638882Smacklem struct buf *bp; 15738882Smacklem struct nfsnode *np = VTONFS(vp); 15838882Smacklem daddr_t lbn, bn; 159*39487Smckusick int i, n, on, cnt, count, error = 0; 16038882Smacklem 16138882Smacklem if ((ioflag & IO_NODELOCKED) == 0) 16238882Smacklem nfs_lock(vp); 16338882Smacklem /* Should we try and do this ?? */ 16438882Smacklem if (vp->v_type == VREG && (ioflag & IO_APPEND)) 16538882Smacklem *offp = np->n_size; 16638882Smacklem uio->uio_offset = *offp; 16738882Smacklem cnt = uio->uio_resid; 16838882Smacklem #ifdef notdef 16938882Smacklem osize = np->n_size; 17038882Smacklem #endif 17138882Smacklem if (uio->uio_rw != UIO_WRITE) 17238882Smacklem panic("nfs_write mode"); 17338882Smacklem if (vp->v_type != VREG) 17438882Smacklem panic("nfs_write type"); 17538882Smacklem if (uio->uio_offset < 0) { 17638882Smacklem error = EINVAL; 17738882Smacklem goto out; 17838882Smacklem } 17938882Smacklem if (uio->uio_resid == 0) 18038882Smacklem goto out; 18138882Smacklem /* 18238882Smacklem * Maybe this should be above the vnode op call, but so long as 18338882Smacklem * file servers have no limits, i don't think it matters 18438882Smacklem */ 18538882Smacklem if (vp->v_type == VREG && 18638882Smacklem uio->uio_offset + uio->uio_resid > 18738882Smacklem u.u_rlimit[RLIMIT_FSIZE].rlim_cur) { 18838882Smacklem psignal(u.u_procp, SIGXFSZ); 18938882Smacklem error = EFBIG; 19038882Smacklem goto out; 19138882Smacklem } 19239340Smckusick np->n_flag |= (NMODIFIED|NBUFFERED); 19338882Smacklem do { 19438882Smacklem lbn = uio->uio_offset >> NFS_BIOSHIFT; 19538882Smacklem on = uio->uio_offset & (NFS_BIOSIZE-1); 19638882Smacklem n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid); 19738882Smacklem if (uio->uio_offset+n > np->n_size) 19838882Smacklem np->n_size = uio->uio_offset+n; 19938882Smacklem bn = lbn*(NFS_BIOSIZE/DEV_BSIZE); 20038882Smacklem count = howmany(NFS_BIOSIZE, CLBYTES); 20138882Smacklem for (i = 0; i < count; i++) 20238882Smacklem munhash(vp, bn + i * CLBYTES / DEV_BSIZE); 20338882Smacklem bp = getblk(vp, bn, NFS_BIOSIZE); 20438882Smacklem if (bp->b_wcred == NOCRED) { 20538882Smacklem crhold(cred); 20638882Smacklem bp->b_wcred = cred; 20738882Smacklem } 20838882Smacklem if (bp->b_dirtyend > 0) { 20938882Smacklem /* 21038882Smacklem * Iff the new write will leave a contiguous 21138882Smacklem * dirty area, just update the b_dirtyoff and 21238882Smacklem * b_dirtyend 21338882Smacklem * otherwise force a write rpc of the old dirty 21438882Smacklem * area 21538882Smacklem */ 21638882Smacklem if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) { 21738882Smacklem bp->b_dirtyoff = MIN(on, bp->b_dirtyoff); 21838882Smacklem bp->b_dirtyend = MAX((on+n), bp->b_dirtyend); 21938882Smacklem } else { 22038882Smacklem /* 22138882Smacklem * Like bwrite() but without the brelse 22238882Smacklem */ 22338882Smacklem bp->b_flags &= ~(B_READ | B_DONE | 22438882Smacklem B_ERROR | B_DELWRI | B_ASYNC); 22538882Smacklem u.u_ru.ru_oublock++; 22638882Smacklem VOP_STRATEGY(bp); 22738882Smacklem error = biowait(bp); 22838882Smacklem if (bp->b_flags & B_ERROR) { 22938882Smacklem brelse(bp); 23038882Smacklem if (bp->b_error) 23138882Smacklem error = bp->b_error; 23238882Smacklem else 23338882Smacklem error = EIO; 23438882Smacklem goto out; 23538882Smacklem } 23638882Smacklem bp->b_dirtyoff = on; 23738882Smacklem bp->b_dirtyend = on+n; 23838882Smacklem } 23938882Smacklem } else { 24038882Smacklem bp->b_dirtyoff = on; 24138882Smacklem bp->b_dirtyend = on+n; 24238882Smacklem } 24338882Smacklem if (error = uiomove(bp->b_un.b_addr + on, n, uio)) 24438882Smacklem goto out; 24538882Smacklem if ((n+on) == NFS_BIOSIZE) { 24638882Smacklem bp->b_flags |= B_AGE; 24738882Smacklem bawrite(bp); 24838882Smacklem } else { 24938882Smacklem bdwrite(bp); 25038882Smacklem } 25138882Smacklem } while (error == 0 && uio->uio_resid > 0 && n != 0); 25238882Smacklem #ifdef notdef 25338882Smacklem /* Should we try and do this for nfs ?? */ 25438882Smacklem if (error && (ioflag & IO_UNIT)) 25538882Smacklem np->n_size = osize; 25638882Smacklem else 25738882Smacklem #endif 25838882Smacklem *offp += cnt - uio->uio_resid; 25938882Smacklem out: 26038882Smacklem if ((ioflag & IO_NODELOCKED) == 0) 26138882Smacklem nfs_unlock(vp); 26238882Smacklem return (error); 26338882Smacklem } 26438882Smacklem 26538882Smacklem /* 26638882Smacklem * Flush and invalidate all of the buffers associated with the blocks of vp 26738882Smacklem */ 26838882Smacklem nfs_blkflush(vp, blkno, size, invalidate) 26938882Smacklem struct vnode *vp; 27038882Smacklem daddr_t blkno; 27138882Smacklem long size; 27238882Smacklem int invalidate; 27338882Smacklem { 27438882Smacklem register struct buf *ep; 27538882Smacklem struct buf *dp; 27639358Smckusick daddr_t curblk, nextblk, ecurblk, lastblk; 27738882Smacklem int s, error, allerrors = 0; 27839358Smckusick 27939358Smckusick /* 28039358Smckusick * Iterate through each possible hash chain. 28139358Smckusick */ 28239358Smckusick lastblk = blkno + btodb(size+DEV_BSIZE-1) - 1; 28339358Smckusick for (curblk = blkno; curblk <= lastblk; curblk = nextblk) { 28439358Smckusick #if RND & (RND-1) 28539358Smckusick nextblk = ((curblk / RND) + 1) * RND; 28639358Smckusick #else 28739358Smckusick nextblk = ((curblk & ~(RND-1)) + RND); 28839358Smckusick #endif 28939358Smckusick ecurblk = nextblk > lastblk ? lastblk : nextblk - 1; 29039358Smckusick dp = BUFHASH(vp, curblk); 29138882Smacklem loop: 29239358Smckusick for (ep = dp->b_forw; ep != dp; ep = ep->b_forw) { 29339358Smckusick if (ep->b_vp != vp || (ep->b_flags & B_INVAL)) 29439358Smckusick continue; 29539358Smckusick /* look for overlap */ 29639358Smckusick if (ep->b_bcount == 0 || ep->b_blkno > ecurblk || 29739358Smckusick ep->b_blkno + btodb(ep->b_bcount) <= curblk) 29839358Smckusick continue; 29939358Smckusick s = splbio(); 30039358Smckusick if (ep->b_flags&B_BUSY) { 30139358Smckusick ep->b_flags |= B_WANTED; 30239358Smckusick sleep((caddr_t)ep, PRIBIO+1); 30339358Smckusick splx(s); 30439358Smckusick goto loop; 30539358Smckusick } 30639358Smckusick if (ep->b_flags & B_DELWRI) { 30739358Smckusick splx(s); 30839358Smckusick notavail(ep); 30939358Smckusick if (error = bwrite(ep)) 31039358Smckusick allerrors = error; 31139358Smckusick goto loop; 31239358Smckusick } 31339358Smckusick splx(s); 31438882Smacklem if (invalidate) { 31539358Smckusick notavail(ep); 31638882Smacklem ep->b_flags |= B_INVAL; 31738882Smacklem brelvp(ep); 31839358Smckusick brelse(ep); 31938882Smacklem } 32039358Smckusick } 32138882Smacklem } 32238882Smacklem return (allerrors); 32338882Smacklem } 324