vfs: add vn_getsize
getattr is very expensive and in important cases only gets called to get the size. This can be optimized with a dedicated routine which obtains that statistic. As a step towards that goal make size-only consumers use a dedicated routine. Reviewed by: kib Differential Revision: https://reviews.freebsd.org/D37885
This commit is contained in:
parent
e300f328fe
commit
f45feecfb2
@ -563,7 +563,6 @@ sendfile_getobj(struct thread *td, struct file *fp, vm_object_t *obj_res,
|
||||
struct vnode **vp_res, struct shmfd **shmfd_res, off_t *obj_size,
|
||||
int *bsize)
|
||||
{
|
||||
struct vattr va;
|
||||
vm_object_t obj;
|
||||
struct vnode *vp;
|
||||
struct shmfd *shmfd;
|
||||
@ -602,10 +601,9 @@ sendfile_getobj(struct thread *td, struct file *fp, vm_object_t *obj_res,
|
||||
VM_OBJECT_RLOCK(obj);
|
||||
*obj_size = obj->un_pager.vnp.vnp_size;
|
||||
} else {
|
||||
error = VOP_GETATTR(vp, &va, td->td_ucred);
|
||||
error = vn_getsize_locked(vp, obj_size, td->td_ucred);
|
||||
if (error != 0)
|
||||
goto out;
|
||||
*obj_size = va.va_size;
|
||||
VM_OBJECT_RLOCK(obj);
|
||||
}
|
||||
} else if (fp->f_type == DTYPE_SHM) {
|
||||
|
@ -6377,7 +6377,7 @@ static int
|
||||
filt_vfsread(struct knote *kn, long hint)
|
||||
{
|
||||
struct vnode *vp = (struct vnode *)kn->kn_hook;
|
||||
struct vattr va;
|
||||
off_t size;
|
||||
int res;
|
||||
|
||||
/*
|
||||
@ -6391,11 +6391,11 @@ filt_vfsread(struct knote *kn, long hint)
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (VOP_GETATTR(vp, &va, curthread->td_ucred))
|
||||
if (vn_getsize_locked(vp, &size, curthread->td_ucred) != 0)
|
||||
return (0);
|
||||
|
||||
VI_LOCK(vp);
|
||||
kn->kn_data = va.va_size - kn->kn_fp->f_offset;
|
||||
kn->kn_data = size - kn->kn_fp->f_offset;
|
||||
res = (kn->kn_sfflags & NOTE_FILE_POLL) != 0 || kn->kn_data != 0;
|
||||
VI_UNLOCK(vp);
|
||||
return (res);
|
||||
@ -7116,6 +7116,30 @@ vn_irflag_unset(struct vnode *vp, short tounset)
|
||||
VI_UNLOCK(vp);
|
||||
}
|
||||
|
||||
int
|
||||
vn_getsize_locked(struct vnode *vp, off_t *size, struct ucred *cred)
|
||||
{
|
||||
struct vattr vattr;
|
||||
int error;
|
||||
|
||||
ASSERT_VOP_LOCKED(vp, __func__);
|
||||
error = VOP_GETATTR(vp, &vattr, cred);
|
||||
if (__predict_true(error == 0))
|
||||
*size = vattr.va_size;
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
vn_getsize(struct vnode *vp, off_t *size, struct ucred *cred)
|
||||
{
|
||||
int error;
|
||||
|
||||
VOP_LOCK(vp, LK_SHARED);
|
||||
error = vn_getsize_locked(vp, size, cred);
|
||||
VOP_UNLOCK(vp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
#ifdef INVARIANTS
|
||||
void
|
||||
vn_set_state_validate(struct vnode *vp, enum vstate state)
|
||||
|
@ -1690,9 +1690,9 @@ static int
|
||||
vn_ioctl(struct file *fp, u_long com, void *data, struct ucred *active_cred,
|
||||
struct thread *td)
|
||||
{
|
||||
struct vattr vattr;
|
||||
struct vnode *vp;
|
||||
struct fiobmap2_arg *bmarg;
|
||||
off_t size;
|
||||
int error;
|
||||
|
||||
vp = fp->f_vnode;
|
||||
@ -1701,11 +1701,9 @@ vn_ioctl(struct file *fp, u_long com, void *data, struct ucred *active_cred,
|
||||
case VREG:
|
||||
switch (com) {
|
||||
case FIONREAD:
|
||||
vn_lock(vp, LK_SHARED | LK_RETRY);
|
||||
error = VOP_GETATTR(vp, &vattr, active_cred);
|
||||
VOP_UNLOCK(vp);
|
||||
error = vn_getsize(vp, &size, active_cred);
|
||||
if (error == 0)
|
||||
*(int *)data = vattr.va_size - fp->f_offset;
|
||||
*(int *)data = size - fp->f_offset;
|
||||
return (error);
|
||||
case FIOBMAP2:
|
||||
bmarg = (struct fiobmap2_arg *)data;
|
||||
@ -2558,7 +2556,7 @@ int
|
||||
vn_bmap_seekhole_locked(struct vnode *vp, u_long cmd, off_t *off,
|
||||
struct ucred *cred)
|
||||
{
|
||||
struct vattr va;
|
||||
off_t size;
|
||||
daddr_t bn, bnp;
|
||||
uint64_t bsize;
|
||||
off_t noff;
|
||||
@ -2572,16 +2570,16 @@ vn_bmap_seekhole_locked(struct vnode *vp, u_long cmd, off_t *off,
|
||||
error = ENOTTY;
|
||||
goto out;
|
||||
}
|
||||
error = VOP_GETATTR(vp, &va, cred);
|
||||
error = vn_getsize_locked(vp, &size, cred);
|
||||
if (error != 0)
|
||||
goto out;
|
||||
noff = *off;
|
||||
if (noff < 0 || noff >= va.va_size) {
|
||||
if (noff < 0 || noff >= size) {
|
||||
error = ENXIO;
|
||||
goto out;
|
||||
}
|
||||
bsize = vp->v_mount->mnt_stat.f_iosize;
|
||||
for (bn = noff / bsize; noff < va.va_size; bn++, noff += bsize -
|
||||
for (bn = noff / bsize; noff < size; bn++, noff += bsize -
|
||||
noff % bsize) {
|
||||
error = VOP_BMAP(vp, bn, NULL, &bnp, NULL, NULL);
|
||||
if (error == EOPNOTSUPP) {
|
||||
@ -2596,9 +2594,9 @@ vn_bmap_seekhole_locked(struct vnode *vp, u_long cmd, off_t *off,
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (noff > va.va_size)
|
||||
noff = va.va_size;
|
||||
/* noff == va.va_size. There is an implicit hole at the end of file. */
|
||||
if (noff > size)
|
||||
noff = size;
|
||||
/* noff == size. There is an implicit hole at the end of file. */
|
||||
if (cmd == FIOSEEKDATA)
|
||||
error = ENXIO;
|
||||
out:
|
||||
@ -2627,8 +2625,7 @@ vn_seek(struct file *fp, off_t offset, int whence, struct thread *td)
|
||||
{
|
||||
struct ucred *cred;
|
||||
struct vnode *vp;
|
||||
struct vattr vattr;
|
||||
off_t foffset, size;
|
||||
off_t foffset, fsize, size;
|
||||
int error, noneg;
|
||||
|
||||
cred = td->td_ucred;
|
||||
@ -2647,10 +2644,8 @@ vn_seek(struct file *fp, off_t offset, int whence, struct thread *td)
|
||||
offset += foffset;
|
||||
break;
|
||||
case L_XTND:
|
||||
vn_lock(vp, LK_SHARED | LK_RETRY);
|
||||
error = VOP_GETATTR(vp, &vattr, cred);
|
||||
VOP_UNLOCK(vp);
|
||||
if (error)
|
||||
error = vn_getsize(vp, &fsize, cred);
|
||||
if (error != 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -2658,16 +2653,16 @@ vn_seek(struct file *fp, off_t offset, int whence, struct thread *td)
|
||||
* the media size and use that to determine the ending
|
||||
* offset.
|
||||
*/
|
||||
if (vattr.va_size == 0 && vp->v_type == VCHR &&
|
||||
if (fsize == 0 && vp->v_type == VCHR &&
|
||||
fo_ioctl(fp, DIOCGMEDIASIZE, &size, cred, td) == 0)
|
||||
vattr.va_size = size;
|
||||
fsize = size;
|
||||
if (noneg &&
|
||||
(vattr.va_size > OFF_MAX ||
|
||||
(offset > 0 && vattr.va_size > OFF_MAX - offset))) {
|
||||
(fsize > OFF_MAX ||
|
||||
(offset > 0 && fsize > OFF_MAX - offset))) {
|
||||
error = EOVERFLOW;
|
||||
break;
|
||||
}
|
||||
offset += vattr.va_size;
|
||||
offset += fsize;
|
||||
break;
|
||||
case L_SET:
|
||||
break;
|
||||
@ -3259,7 +3254,6 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||
struct vnode *outvp, off_t *outoffp, size_t *lenp, unsigned int flags,
|
||||
struct ucred *incred, struct ucred *outcred, struct thread *fsize_td)
|
||||
{
|
||||
struct vattr va, inva;
|
||||
struct mount *mp;
|
||||
off_t startoff, endoff, xfer, xfer2;
|
||||
u_long blksize;
|
||||
@ -3267,6 +3261,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||
bool cantseek, readzeros, eof, lastblock, holetoeof;
|
||||
ssize_t aresid, r = 0;
|
||||
size_t copylen, len, savlen;
|
||||
off_t insize, outsize;
|
||||
char *dat;
|
||||
long holein, holeout;
|
||||
struct timespec curts, endts;
|
||||
@ -3283,7 +3278,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||
if (VOP_PATHCONF(invp, _PC_MIN_HOLE_SIZE, &holein) != 0)
|
||||
holein = 0;
|
||||
if (holein > 0)
|
||||
error = VOP_GETATTR(invp, &inva, incred);
|
||||
error = vn_getsize_locked(invp, &insize, incred);
|
||||
VOP_UNLOCK(invp);
|
||||
if (error != 0)
|
||||
goto out;
|
||||
@ -3314,13 +3309,12 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||
/*
|
||||
* Holes that are past EOF do not need to be written as a block
|
||||
* of zero bytes. So, truncate the output file as far as
|
||||
* possible and then use va.va_size to decide if writing 0
|
||||
* possible and then use size to decide if writing 0
|
||||
* bytes is necessary in the loop below.
|
||||
*/
|
||||
if (error == 0)
|
||||
error = VOP_GETATTR(outvp, &va, outcred);
|
||||
if (error == 0 && va.va_size > *outoffp && va.va_size <=
|
||||
*outoffp + len) {
|
||||
error = vn_getsize_locked(outvp, &outsize, outcred);
|
||||
if (error == 0 && outsize > *outoffp && outsize <= *outoffp + len) {
|
||||
#ifdef MAC
|
||||
error = mac_vnode_check_write(curthread->td_ucred,
|
||||
outcred, outvp);
|
||||
@ -3329,7 +3323,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||
error = vn_truncate_locked(outvp, *outoffp,
|
||||
false, outcred);
|
||||
if (error == 0)
|
||||
va.va_size = *outoffp;
|
||||
outsize = *outoffp;
|
||||
}
|
||||
VOP_UNLOCK(outvp);
|
||||
}
|
||||
@ -3409,7 +3403,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||
error = VOP_IOCTL(invp, FIOSEEKDATA, &startoff, 0,
|
||||
incred, curthread);
|
||||
if (error == ENXIO) {
|
||||
startoff = endoff = inva.va_size;
|
||||
startoff = endoff = insize;
|
||||
eof = holetoeof = true;
|
||||
error = 0;
|
||||
}
|
||||
@ -3434,9 +3428,9 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||
if (startoff > *inoffp) {
|
||||
/* Found hole before data block. */
|
||||
xfer = MIN(startoff - *inoffp, len);
|
||||
if (*outoffp < va.va_size) {
|
||||
if (*outoffp < outsize) {
|
||||
/* Must write 0s to punch hole. */
|
||||
xfer2 = MIN(va.va_size - *outoffp,
|
||||
xfer2 = MIN(outsize - *outoffp,
|
||||
xfer);
|
||||
memset(dat, 0, MIN(xfer2, blksize));
|
||||
error = vn_write_outvp(outvp, dat,
|
||||
@ -3445,7 +3439,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||
}
|
||||
|
||||
if (error == 0 && *outoffp + xfer >
|
||||
va.va_size && (xfer == len || holetoeof)) {
|
||||
outsize && (xfer == len || holetoeof)) {
|
||||
/* Grow output file (hole at end). */
|
||||
error = vn_write_outvp(outvp, dat,
|
||||
*outoffp, xfer, blksize, true,
|
||||
@ -3515,12 +3509,12 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||
false;
|
||||
if (xfer == len)
|
||||
lastblock = true;
|
||||
if (!cantseek || *outoffp < va.va_size ||
|
||||
if (!cantseek || *outoffp < outsize ||
|
||||
lastblock || !readzeros)
|
||||
error = vn_write_outvp(outvp, dat,
|
||||
*outoffp, xfer, blksize,
|
||||
readzeros && lastblock &&
|
||||
*outoffp >= va.va_size, false,
|
||||
*outoffp >= outsize, false,
|
||||
outcred);
|
||||
if (error == 0) {
|
||||
*inoffp += xfer;
|
||||
|
@ -1135,6 +1135,8 @@ int vn_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
|
||||
struct thread *td);
|
||||
int vn_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
|
||||
struct thread *td);
|
||||
int vn_getsize_locked(struct vnode *vp, off_t *size, struct ucred *active_cred);
|
||||
int vn_getsize(struct vnode *vp, off_t *size, struct ucred *active_cred);
|
||||
|
||||
void vn_fsid(struct vnode *vp, struct vattr *va);
|
||||
|
||||
|
@ -155,7 +155,6 @@ vnode_create_vobject(struct vnode *vp, off_t isize, struct thread *td)
|
||||
{
|
||||
vm_object_t object;
|
||||
vm_ooffset_t size = isize;
|
||||
struct vattr va;
|
||||
bool last;
|
||||
|
||||
if (!vn_isdisk(vp) && vn_canvmio(vp) == FALSE)
|
||||
@ -169,9 +168,8 @@ vnode_create_vobject(struct vnode *vp, off_t isize, struct thread *td)
|
||||
if (vn_isdisk(vp)) {
|
||||
size = IDX_TO_OFF(INT_MAX);
|
||||
} else {
|
||||
if (VOP_GETATTR(vp, &va, td->td_ucred))
|
||||
if (vn_getsize_locked(vp, &size, td->td_ucred) != 0)
|
||||
return (0);
|
||||
size = va.va_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user