📄 nfs_vnodeops.c
字号:
} if (error == 0) /* XXX */ error = u.u_error; /* XXX */bad: return (error);}/* * Write to a remote file. * Writes to remote server in largest size chunks that the server can * handle. Write is synchronous from the client's point of view. */intnfswrite(bp, vp, base, offset, count, cred, blockio) struct buf *bp; struct vnode *vp; caddr_t base; int offset; int count; struct ucred *cred; int blockio; /* Block I/O?: * 1 (NFS_BLOCKIO) if called by do_bio(), * 0 (NFS_NOT_BLOCKIO) if called directly by rwvp(), * i.e. NFS locked files. */{ register struct rnode *rp = vtor(vp); int error; struct nfswriteargs wa; struct nfsattrstat *ns; int tsize; int async = 0; /* * Check for write-behind (asynchronous) errors, and * throw this request away if there has been one. */ error = rp->r_error; /* N.B.: gnode may not be locked here */ if (error) { bp->b_error = error; bp->b_flags |= B_ERROR; if (blockio) /* If doing block I/O, free the buffer */ iodone(bp); return(error); } kmem_alloc(ns, struct nfsattrstat *, (u_int)sizeof(*ns), KM_NFS); do { tsize = min(vtomi(vp)->mi_stsize, count); wa.wa_data = base; wa.wa_fhandle = *vtofh(vp); wa.wa_begoff = offset; wa.wa_totcount = tsize; wa.wa_count = tsize; wa.wa_offset = offset; error = rfscall(vtomi(vp), RFS_WRITE, xdr_writeargs, (caddr_t)&wa, xdr_attrstat, (caddr_t)ns, cred); if (!error) { error = geterrno(ns->ns_status); check_stale_fh(error, vp); } count -= tsize; base += tsize; offset += tsize; } while (!error && count); if (bp->b_flags & B_ASYNC) async = 1; if (error) { bp->b_error = error; bp->b_flags |= B_ERROR; } /* * N.B. It's easy to forget (or not understand in the first place) * that an async write is handled by a * biod only if there is one available; if there isn't, then * the calling process gets blocked. This can be the process * actually doing writes, the update daemon, or some completely * random victim that stumbled into a DELWRI buffer. * Synchronous writes can be the writing (or nfs_fsync()'ing) process * or a random buffer cache victim. * Thus, the gnode may or may not be held locked by the calling * process. This can cause deadlock problems between * buffers and gnodes. * * The general gnode/buffer rule to avoid deadlocks is: * `Don't hold a buffer busy while attempting to lock a gnode.' * * This is why we free the buffer before calling nfs_attrcache(). * If the write fails and it was asynchronous all future writes will * get an error. We must do the rp->r_error assignment before * releasing the buffer for proper nfs_fsync() synchronization, but * can't get the gnode lock since we could deadlock. * * Although it would be desirable to never throw attributes away, * there is a (hopefully rare) deadlock case where we are * writing for the random victim which may have anything locked. * In this case, we must throw attributes away. * * Since trying to account for all write buffers leads to deadlocks, * we rely on the RDIRTY flag used by nfs_fsync() when reconciling * file attributes in nfs_attrcache(). * When we release buffers here, that frees nfs_fsync() to complete * and reset RDIRTY before async write processes (which would * block on vp) have run through nfs_attrcache(). * So as to not create a race with nfs_attrcache() which could * result in confused file attributes, we never call * it on behalf of async writes. */ if (error && async) rp->r_error = error; /* * Free the buffer and rely on nfs_attrcache() to worry about * races with other processes that provide fresher attributes * than ours. */ if (blockio) /* If doing block I/O, free the buffer */ iodone(bp); /* Never cache async or error attributes */ if (!async && !error) nfs_attrcache(vp, &ns->ns_attr, NOFLUSH); kmem_free(ns, KM_NFS); switch (error) { case 0: case EDQUOT: break; case ENOSPC: printf("NFS write error, server %s, remote file system full\n", vtomi(vp)->mi_hostname); break; default: printf("NFS write error %d on host %s fh ", error, vtomi(vp)->mi_hostname); printfhandle((caddr_t)vtofh(vp)); printf("\n"); break; } return (error);}/* * Print a file handle */printfhandle(fh) caddr_t fh;{ int i; int fhint[NFS_FHSIZE / sizeof (int)]; bcopy(fh, (caddr_t)fhint, sizeof (fhint)); for (i = 0; i < (sizeof (fhint) / sizeof (int)); i++) { printf("%x ", fhint[i]); }}/* * Read from a remote file. * Reads data in largest chunks our interface can handle */intnfsread(bp, vp, base, offset, count, cred, blockio) struct buf *bp; struct vnode *vp; caddr_t base; int offset; int count; struct ucred *cred; int blockio; /* Block I/O?: * 1 (NFS_BLOCKIO) if called by do_bio(), * 0 (NFS_NOT_BLOCKIO) if called by rwvp(), * i.e. NFS locked files. */{ int error; struct nfsreadargs ra; struct nfsrdresult rr; register int tsize; do { tsize = min(vtomi(vp)->mi_tsize, count); rr.rr_data = base; ra.ra_fhandle = *vtofh(vp); ra.ra_offset = offset; ra.ra_totcount = tsize; ra.ra_count = tsize; ++nfs_rfsread_count; error = rfscall(vtomi(vp), RFS_READ, xdr_readargs, (caddr_t)&ra, xdr_rdresult, (caddr_t)&rr, cred); if (!error) { error = geterrno(rr.rr_status); check_stale_fh(error, vp); } if (!error) { count -= rr.rr_count; base += rr.rr_count; offset += rr.rr_count; } } while (!error && count && rr.rr_count == tsize); if (error) { bp->b_error = error; bp->b_flags |= B_ERROR; } else if (count) { bzero(bp->b_un.b_addr + bp->b_bcount - count, (u_int)count); } /* * The gnode may or may not be held locked by the calling * process. This can cause lock ordering problems * between buffers and gnodes. We free the buffer in all cases * and rely on nfs_attrcache() to worry about races with * other processes that provide fresher attributes than ours. */ if (blockio) /* If doing block I/O, free the buffer */ iodone(bp); if (!error) { nfs_attrcache(vp, &rr.rr_attr, SFLUSH); } return (error);}int binval_debug = 0;/* returns true if attributes are strictly newer, not equal */#define ATTR_NEW(vp, na) (((na)->na_mtime.tv_sec > (vp)->g_mtime.tv_sec) || \ ((na)->na_mtime.tv_sec == (vp)->g_mtime.tv_sec && \ (na)->na_mtime.tv_usec > (vp)->g_mtime.tv_usec) || \ ((na)->na_ctime.tv_sec > (vp)->g_ctime.tv_sec) || \ ((na)->na_ctime.tv_sec == (vp)->g_ctime.tv_sec && \ (na)->na_ctime.tv_usec > (vp)->g_ctime.tv_usec))/* returns true if modify time is strictly older and change time is equal */#define ATTR_OLD(vp, na) ((((na)->na_mtime.tv_sec < (vp)->g_mtime.tv_sec) || \ ((na)->na_mtime.tv_sec == (vp)->g_mtime.tv_sec && \ (na)->na_mtime.tv_usec < (vp)->g_mtime.tv_usec))&&\ ((na)->na_ctime.tv_sec == (vp)->g_ctime.tv_sec && \ (na)->na_ctime.tv_usec == (vp)->g_ctime.tv_usec))/* returns true if attributes are strictly equal */#define ATTR_SAME(vp, na) ((na)->na_mtime.tv_sec == (vp)->g_mtime.tv_sec && \ (na)->na_mtime.tv_usec == (vp)->g_mtime.tv_usec && \ (na)->na_ctime.tv_sec == (vp)->g_ctime.tv_sec && \ (na)->na_ctime.tv_usec == (vp)->g_ctime.tv_usec)intnfs_attrcache(vp, na, fflag) register struct vnode *vp; register struct nfsfattr *na; enum staleflush fflag;{ register struct rnode *rp = vtor(vp); int oldsize; int need_lock; char *s; if (binval_debug) { if (glocked((struct gnode *)vp) == LK_TRUE) s = "locked"; else s = "!locked"; mprintf("attr: 0x%x %s %s %s\n ntime %d %d nsize %d\n time %d %d size %d\n", vp, (vp->v_type == VREG)?"file":"!file", (fflag)?"flush":"!flush", s, na->na_mtime.tv_sec, na->na_mtime.tv_usec, na->na_size, vp->g_mtime.tv_sec, vp->g_mtime.tv_usec, vp->g_size); } /* * Lock the gnode if it's not already locked * (asynchronous I/O is one way for vp to be unlocked, * see nfswrite() comments for others). */ if (glocked(vp) != LK_TRUE) { need_lock = 1; gfs_lock((struct gnode *)vp); /* * If we lose a race to update attributes, * throw these away. */ if (ATTR_OLD(vp, na)) /* strictly old, not equal */ goto done; } else { /* we have the gnode locked already */ need_lock = 0; } if (binval_debug) { mprintf("cache: 0x%x %s %s %s\n ntime %d %d nsize %d\n time %d %d size %d\n", vp, (vp->v_type == VREG)?"file":"!file", (fflag)?"flush":"!flush", s, na->na_mtime.tv_sec, na->na_mtime.tv_usec, na->na_size, vp->g_mtime.tv_sec, vp->g_mtime.tv_usec, vp->g_size); } /* * Check the new modify time against the old modify time * to see if cached data is stale */ if (na->na_mtime.tv_sec != vp->g_mtime.tv_sec || na->na_mtime.tv_usec != vp->g_mtime.tv_usec) { /* * The file has changed on the server. * * If this was unexpected (SFLUSH), and we are not actively * modifying the file ourselves, * then flush delayed write blocks associated with this vnode * from the buffer cache and invalidate cached blocks * on the free list. * * If this is a text file, stop running copies. * * If this is a directory, purge the name lookup cache. */ if (fflag == SFLUSH && !(rp->r_flags & RDIRTY)) { if (binval_debug) { mprintf("do binvalfree\n"); } binvalfree((struct gnode *)vp); } /* if it is a text, clean out running procs and vm */ if (vp->v_flag & VTEXT) { xinval((struct gnode *)vp); cacheinval((struct gnode *)vp); /* binval((dev_t)NODEV, (struct gnode *)vp); */ } if (vp->v_type == VDIR) dnlc_purge_vp(vp); } /* * Copy the new attributes into the gnode and timestamp them. * * Any writes caused by binvalfree() call above will be async, * so those returned attributes will be discarded. * If this thread didn't have the gnode locked coming in, * when we got it above, we would have discarded old attributes. * The only way (famous last words) we can now have old attributes * is if we lost a race getting the gnode again after going to the * wire in nfs_getattr(). * * There is some weirdness with the gnode size here. We must * keep the old size if the file has unaccounted writes and * the old size is greater than the new size, since these writes * may make the file grow. * */ if (!(ATTR_OLD(vp, na))) { oldsize = vp->g_size; nattr_to_gattr(vp, na); if (oldsize > vp->g_size) { /* our size > server size */ /* Keep our size if there are writes outstanding. */ if (rp->r_flags & RDIRTY) { vp->g_size = oldsize; } } } /* * If no attributes caching, ignore time-to-life of attributes. */ if (!(vtomi(vp)->mi_noac)) { nfs_ttlattr(vp, na); } done: if (need_lock) gfs_unlock((struct gnode *)vp);} intnfs_ttlattr(vp, na) register struct vnode *vp; register struct nfsfattr *na;{ register int delta; register struct rnode *rp = vtor(vp); /* ***It is assumed that vp is locked by caller*** */ rp->r_nfsattrtime = *(timepick); /* * Delta is the number of seconds that we will cache * attributes of the file. It is based on the number of seconds * since the last change (i.e. files that changed recently * are likely to change soon), but there is a minimum and * a maximum for regular files and for directories. */ delta = (timepick->tv_sec - na->na_mtime.tv_sec) >> 4; if (vp->v_type == VDIR) { if (delta < vtomi(vp)->mi_acdirmin) { delta = vtomi(vp)->mi_acdirmin; } else if (delta > vtomi(vp)->mi_acdirmax) { delta = vtomi(vp)->mi_acdirmax; } } else { if (delta < vtomi(vp)->mi_acregmin) { delta = vtomi(vp)->mi_acregmin; } else if (delta > vtomi(vp)->mi_acregmax) { delta = vtomi(vp)->mi_acregmax; } } rp->r_nfsattrtime.tv_sec += delta;}intnfs_getattr(vp, cred) struct vnode *vp; struct ucred *cred;{ register struct rnode *rp = vtor(vp); int error; struct nfsattrstat *ns; int locked = 0; if (!vtomi(vp)->mi_noac) { /* i.e. maybe use cached attributes */ if ((timepick->tv_sec < rp->r_nfsattrtime.tv_sec) || ((timepick->tv_sec == rp->r_nfsattrtime.tv_sec) && (timepick->tv_usec < rp->r_nfsattrtime.tv_usec))) { /* * Use cached attributes. */ return (0); } } if (rp->r_flags & RDIRTY) { nfs_fsync(vp); /* nfs_fsync() goes directly to the wire */ } kmem_alloc(ns, struct nfsattrstat *, (u_int)sizeof(*ns), KM_NFS); /* don't hold gnode locked while going over the wire */ if (glocked((struct gnode *)vp) == LK_TRUE) { locked = 1; gfs_unlock((struct gnode *)vp); } error = rfscall(vtomi(vp), RFS_GETATTR, xdr_fhandle, (caddr_t)vtofh(vp), xdr_attrstat, (caddr_t)ns, cred); /* lock gnode again if it was locked coming in */ if (locked) gfs_lock((struct gnode *)vp); if (!error) { error = geterrno(ns->ns_status); if (!error) { nfs_attrcache(vp, &ns->ns_attr, SFLUSH); } else { check_stale_fh(error, vp); } } kmem_free(ns, KM_NFS); return (error);}intnfs_setattr(vp, vap, cred) register struct vnode *vp; register struct vattr *vap; struct ucred *cred;{ register struct rnode *rp = vtor(vp); int error; struct nfssaargs args; struct nfsattrstat *ns; kmem_alloc(ns, struct nfsattrstat *, (u_int)sizeof(*ns), KM_NFS); if ((vap->va_nlink != -1) || (vap->va_blocksize != -1) || (vap->va_rdev != -1) || (vap->va_blocks != -1) || (vap->va_ctime.tv_sec != -1) || (vap->va_ctime.tv_usec != -1)) { error = EINVAL; } else { if (rp->r_flags & RDIRTY) { nfs_fsync(vp); /* NB: nfs_getattr() will unlock vp */ } if (vap->va_size != (u_long) -1) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -