Index: share/man/man9/Makefile =================================================================== --- share/man/man9/Makefile +++ share/man/man9/Makefile @@ -2308,6 +2308,8 @@ MLINKS+=vm_page_wire.9 vm_page_unwire.9 MLINKS+=VOP_ACCESS.9 VOP_ACCESSX.9 MLINKS+=VOP_ATTRIB.9 VOP_GETATTR.9 \ + VOP_ATTRIB.9 VOP_UGETATTR_LITE.9 \ + VOP_ATTRIB.9 VOP_GETATTR_LITE.9 \ VOP_ATTRIB.9 VOP_SETATTR.9 \ VOP_ATTRIB.9 VOP_STAT.9 MLINKS+=VOP_CREATE.9 VOP_MKDIR.9 \ Index: share/man/man9/VOP_ATTRIB.9 =================================================================== --- share/man/man9/VOP_ATTRIB.9 +++ share/man/man9/VOP_ATTRIB.9 @@ -41,6 +41,10 @@ .Ft int .Fn VOP_GETATTR "struct vnode *vp" "struct vattr *vap" "struct ucred *cred" .Ft int +.Fn VOP_UGETATTR_LITE "struct vnode *vp" "struct vattr *vap" "struct ucred *cred" +.Ft int +.Fn VOP_GETATTR_LITE "struct vnode *vp" "struct vattr *vap" "struct ucred *cred" +.Ft int .Fn VOP_SETATTR "struct vnode *vp" "struct vattr *vap" "struct ucred *cred" .Ft int .Fn VOP_STAT "struct vnode *vp" "struct stat *sb" "struct ucred *active_cred" \ @@ -57,8 +61,17 @@ .Fn VOP_GETATTR . Filesystems may want to implement their own variant for performance reasons. .Pp +.Fn VOP_UGETATTR_LITE +can be used for lockless operation. It guarantees to not block if requested, +but may fail to provide requested data as a result. Callers may want to fallback +to +.Fn VOP_GETATTR_LITE +from a blocking context. +.Pp For -.Fn VOP_GETATTR +.Fn VOP_GETATTR , +.Fn VOP_UGETATTR_LITE , +.Fn VOP_GETATTR_LITE and .Fn VOP_SETATTR the arguments are: @@ -71,6 +84,14 @@ The user credentials of the calling thread. .El .Pp +Additionally +.Fn VOP_UGETATTR_LITE +accepts: +.Bl -tag -width cred +.It Fa flags +LK_NOWAIT to demand non-blocking operation or 0 to allow it. +.El +.Pp For .Fn VOP_STAT the arguments are: @@ -97,21 +118,27 @@ .Fa *vap prior to setting specific values. .Sh LOCKS -Both -.Fn VOP_GETATTR +.Fn VOP_GETATTR , +.Fn VOP_GETATTR_LITE and .Fn VOP_STAT expect the vnode to be locked on entry and will leave the vnode locked on return. The lock type can be either shared or exclusive. .Pp +.Fn VOP_UGETATTR_LITE +expects the vnode to be unlocked. +.Pp .Fn VOP_SETATTR expects the vnode to be locked on entry and will leave the vnode locked on return. The lock type must be exclusive. .Sh RETURN VALUES -.Fn VOP_GETATTR -returns 0 if it was able to retrieve the attribute data via +.Fn VOP_GETATTR , +.Fn VOP_UGETATTR_LITE +and +.Fn VOP_GETATTR_LITE +return 0 if it was able to retrieve the attribute data via .Fa *vap , otherwise an appropriate error is returned. .Fn VOP_SETATTR @@ -130,6 +157,16 @@ .It Bq Er EROFS The file system is read-only. .El +.Pp +Additionally +.Fn VOP_GETATTR_LITE +may return the following: +.Bl -tag -width Er +.It Bq Er ENOENT +The vnode is doomed. +.It Bq Er EAGAIN +The requested operation could not be completed without blocking. +.El .Sh SEE ALSO .Xr VFS 9 , .Xr vnode 9 , Index: sys/fs/tmpfs/tmpfs_vnops.h =================================================================== --- sys/fs/tmpfs/tmpfs_vnops.h +++ sys/fs/tmpfs/tmpfs_vnops.h @@ -51,6 +51,8 @@ vop_access_t tmpfs_access; vop_fplookup_vexec_t tmpfs_fplookup_vexec; vop_stat_t tmpfs_stat; +vop_ugetattr_lite_t tmpfs_ugetattr_lite; +vop_getattr_lite_t tmpfs_getattr_lite; vop_getattr_t tmpfs_getattr; vop_setattr_t tmpfs_setattr; vop_pathconf_t tmpfs_pathconf; Index: sys/fs/tmpfs/tmpfs_vnops.c =================================================================== --- sys/fs/tmpfs/tmpfs_vnops.c +++ sys/fs/tmpfs/tmpfs_vnops.c @@ -453,6 +453,49 @@ return (vop_stat_helper_post(v, error)); } +static void __inline +tmpfs_getattr_lite_fill(struct vnode *vp, struct tmpfs_node *node, + struct vattr_lite *lvap) +{ + + lvap->va_size = node->tn_size; + lvap->va_nlink = node->tn_links; + lvap->va_type = vp->v_type; + lvap->va_mode = node->tn_mode; + lvap->va_uid = node->tn_uid; + lvap->va_gid = node->tn_gid; +} + +int +tmpfs_ugetattr_lite(struct vop_ugetattr_lite_args *v) +{ + struct vnode *vp = v->a_vp; + struct vattr_lite *lvap = v->a_lvap; + struct tmpfs_node *node; + + vfs_smr_enter(); + node = VP_TO_TMPFS_NODE_SMR(vp); + if (__predict_false(node == NULL)) { + vfs_smr_exit(); + return (ENOENT); + } + tmpfs_getattr_lite_fill(vp, node, lvap); + vfs_smr_exit(); + return (0); +} + +int +tmpfs_getattr_lite(struct vop_getattr_lite_args *v) +{ + struct vnode *vp = v->a_vp; + struct vattr_lite *lvap = v->a_lvap; + struct tmpfs_node *node; + + node = VP_TO_TMPFS_NODE(vp); + tmpfs_getattr_lite_fill(vp, node, lvap); + return (0); +} + int tmpfs_getattr(struct vop_getattr_args *v) { @@ -1724,6 +1767,8 @@ .vop_fplookup_vexec = tmpfs_fplookup_vexec, .vop_access = tmpfs_access, .vop_stat = tmpfs_stat, + .vop_ugetattr_lite = tmpfs_ugetattr_lite, + .vop_getattr_lite = tmpfs_getattr_lite, .vop_getattr = tmpfs_getattr, .vop_setattr = tmpfs_setattr, .vop_read = tmpfs_read, Index: sys/kern/imgact_elf.c =================================================================== --- sys/kern/imgact_elf.c +++ sys/kern/imgact_elf.c @@ -738,13 +738,13 @@ { struct { struct nameidata nd; - struct vattr attr; + struct vattr_lite attr; struct image_params image_params; } *tempdata; const Elf_Ehdr *hdr = NULL; const Elf_Phdr *phdr = NULL; struct nameidata *nd; - struct vattr *attr; + struct vattr_lite *attr; struct image_params *imgp; u_long rbase; u_long base_addr = 0; Index: sys/kern/kern_exec.c =================================================================== --- sys/kern/kern_exec.c +++ sys/kern/kern_exec.c @@ -365,7 +365,7 @@ struct uidinfo *euip = NULL; uintptr_t stack_base; struct image_params image_params, *imgp; - struct vattr attr; + struct vattr_lite attr; int (*img_first)(struct image_params *); struct pargs *oldargs = NULL, *newargs = NULL; struct sigacts *oldsigacts = NULL, *newsigacts = NULL; @@ -1706,14 +1706,14 @@ exec_check_permissions(struct image_params *imgp) { struct vnode *vp = imgp->vp; - struct vattr *attr = imgp->attr; + struct vattr_lite *attr = imgp->attr; struct thread *td; int error; td = curthread; /* Get file attributes */ - error = VOP_GETATTR(vp, attr, td->td_ucred); + error = VOP_GETATTR_LITE(vp, attr, td->td_ucred); if (error) return (error); Index: sys/kern/vfs_default.c =================================================================== --- sys/kern/vfs_default.c +++ sys/kern/vfs_default.c @@ -89,6 +89,8 @@ static int vop_stdadd_writecount(struct vop_add_writecount_args *ap); static int vop_stdcopy_file_range(struct vop_copy_file_range_args *ap); static int vop_stdfdatasync(struct vop_fdatasync_args *ap); +static int vop_stdugetattr_lite(struct vop_ugetattr_lite_args *ap); +static int vop_stdgetattr_lite(struct vop_getattr_lite_args *ap); static int vop_stdgetpages_async(struct vop_getpages_async_args *ap); static int vop_stdstat(struct vop_stat_args *ap); @@ -120,6 +122,8 @@ .vop_fsync = VOP_NULL, .vop_stat = vop_stdstat, .vop_fdatasync = vop_stdfdatasync, + .vop_ugetattr_lite = vop_stdugetattr_lite, + .vop_getattr_lite = vop_stdgetattr_lite, .vop_getpages = vop_stdgetpages, .vop_getpages_async = vop_stdgetpages_async, .vop_getwritemount = vop_stdgetwritemount, @@ -749,6 +753,51 @@ return (vn_fsync_buf(ap->a_vp, MNT_WAIT)); } +int +vop_stdugetattr_lite(struct vop_ugetattr_lite_args *ap) +{ + struct vnode *vp; + struct ucred *cred; + struct vattr_lite *lvap; + int error, flags; + + vp = ap->a_vp; + cred = ap->a_cred; + lvap = ap->a_lvap; + flags = ap->a_flags; + error = VOP_LOCK(vp, LK_SHARED | flags); + if (error != 0) + return (EAGAIN); + error = VOP_GETATTR_LITE(vp, lvap, cred); + VOP_UNLOCK(vp); + return (error); +} + +int +vop_stdgetattr_lite(struct vop_getattr_lite_args *ap) +{ + struct vnode *vp; + struct ucred *cred; + struct vattr vattr; + struct vattr_lite *lvap; + int error; + + vp = ap->a_vp; + cred = ap->a_cred; + error = VOP_GETATTR(vp, &vattr, cred); + if (error != 0) + return (error); + + lvap = ap->a_lvap; + lvap->va_size = vattr.va_size; + lvap->va_nlink = vattr.va_nlink; + lvap->va_type = vattr.va_type; + lvap->va_mode = vattr.va_mode; + lvap->va_uid = vattr.va_uid; + lvap->va_gid = vattr.va_gid; + return (0); +} + /* XXX Needs good comment and more info in the manpage (VOP_GETPAGES(9)). */ int vop_stdgetpages(ap) Index: sys/kern/vfs_subr.c =================================================================== --- sys/kern/vfs_subr.c +++ sys/kern/vfs_subr.c @@ -5655,6 +5655,85 @@ ASSERT_VI_LOCKED(a->a_vp, "VOP_NEED_INACTIVE"); } + +static void +vop_getattr_lite_debug_prefill(struct vattr_lite *lvap) +{ + + lvap->va_size = VNOVAL; + lvap->va_nlink = VNOVAL; + lvap->va_type = VNON; + lvap->va_mode = 0xffff; + lvap->va_uid = VNOVAL; + lvap->va_gid = VNOVAL; +} + +static void +vop_getattr_lite_debug_validate(struct vnode *vp, struct vattr_lite *lvap) +{ + + if (lvap->va_size == VNOVAL || + lvap->va_nlink == VNOVAL || + lvap->va_type == VNON || + lvap->va_mode == 0xffff || + lvap->va_uid == VNOVAL || + lvap->va_gid == VNOVAL) { + VNASSERT(0, vp, + ("some fields got left uninitialized in vattr_lite")); + } +} + +void +vop_ugetattr_lite_debugpre(void *ap) +{ + struct vop_ugetattr_lite_args *a; + struct vattr_lite *lvap; + int flags; + + a = ap; + flags = a->a_flags; + lvap = a->a_lvap; + KASSERT((flags & ~(LK_NOWAIT)) == 0, + ("%s: unsupported flags %d passed\n", __func__, flags)); + vop_getattr_lite_debug_prefill(lvap); +} + +void +vop_ugetattr_lite_debugpost(void *ap, int rc) +{ + struct vop_ugetattr_lite_args *a; + struct vnode *vp; + struct vattr_lite *lvap; + + a = ap; + vp = a->a_vp; + lvap = a->a_lvap; + vop_getattr_lite_debug_validate(vp, lvap); +} + +void +vop_getattr_lite_debugpre(void *ap) +{ + struct vop_getattr_lite_args *a; + struct vattr_lite *lvap; + + a = ap; + lvap = a->a_lvap; + vop_getattr_lite_debug_prefill(lvap); +} + +void +vop_getattr_lite_debugpost(void *ap, int rc) +{ + struct vop_getattr_lite_args *a; + struct vnode *vp; + struct vattr_lite *lvap; + + a = ap; + vp = a->a_vp; + lvap = a->a_lvap; + vop_getattr_lite_debug_validate(vp, lvap); +} #endif void Index: sys/kern/vfs_vnops.c =================================================================== --- sys/kern/vfs_vnops.c +++ sys/kern/vfs_vnops.c @@ -2250,7 +2250,7 @@ { struct ucred *cred; struct vnode *vp; - struct vattr vattr; + struct vattr_lite vattr; off_t foffset, size; int error, noneg; @@ -2270,9 +2270,7 @@ offset += foffset; break; case L_XTND: - vn_lock(vp, LK_SHARED | LK_RETRY); - error = VOP_GETATTR(vp, &vattr, cred); - VOP_UNLOCK(vp); + error = VOP_UGETATTR_LITE(vp, &vattr, cred, 0); if (error) break; Index: sys/kern/vnode_if.src =================================================================== --- sys/kern/vnode_if.src +++ sys/kern/vnode_if.src @@ -188,6 +188,29 @@ }; +%% ugetattr_lite vp - - - +%! ugetattr_lite debugpre vop_ugetattr_lite_debugpre +%! ugetattr_lite debugpost vop_ugetattr_lite_debugpost + +vop_ugetattr_lite { + IN struct vnode *vp; + OUT struct vattr_lite *lvap; + IN struct ucred *cred; + IN int flags; +}; + + +%% getattr_lite vp L L L +%! getattr_lite debugpre vop_getattr_lite_debugpre +%! getattr_lite debugpost vop_getattr_lite_debugpost + +vop_getattr_lite { + IN struct vnode *vp; + OUT struct vattr_lite *lvap; + IN struct ucred *cred; +}; + + %% getattr vp L L L vop_getattr { Index: sys/sys/imgact.h =================================================================== --- sys/sys/imgact.h +++ sys/sys/imgact.h @@ -63,7 +63,7 @@ struct label *execlabel; /* optional exec label */ struct vnode *vp; /* pointer to vnode of file to exec */ struct vm_object *object; /* The vm object for this vp */ - struct vattr *attr; /* attributes of file */ + struct vattr_lite *attr; /* attributes of file */ const char *image_header; /* head of file to exec */ unsigned long entry_addr; /* entry address of target executable */ unsigned long reloc_base; /* load address of image */ Index: sys/sys/vnode.h =================================================================== --- sys/sys/vnode.h +++ sys/sys/vnode.h @@ -296,6 +296,19 @@ long va_spare; /* remain quad aligned */ }; +/* + * If you add fields to this structure make sure to update sanity checks + * in vop_getattr_lite_debug* + */ +struct vattr_lite { + u_quad_t va_size; /* file size in bytes */ + nlink_t va_nlink; /* number of references to file */ + enum vtype va_type; /* vnode type (for create) */ + u_short va_mode; /* files access mode and type */ + uid_t va_uid; /* owner user id */ + gid_t va_gid; /* owner group id */ +}; + /* * Flags for va_vaflags. */ @@ -878,6 +891,10 @@ void vop_unlock_debugpre(void *a); void vop_need_inactive_debugpre(void *a); void vop_need_inactive_debugpost(void *a, int rc); +void vop_ugetattr_lite_debugpre(void *a); +void vop_ugetattr_lite_debugpost(void *a, int rc); +void vop_getattr_lite_debugpre(void *a); +void vop_getattr_lite_debugpost(void *a, int rc); #else #define vop_fplookup_vexec_debugpre(x) do { } while (0) #define vop_fplookup_vexec_debugpost(x, y) do { } while (0) @@ -887,6 +904,10 @@ #define vop_unlock_debugpre(x) do { } while (0) #define vop_need_inactive_debugpre(x) do { } while (0) #define vop_need_inactive_debugpost(x, y) do { } while (0) +#define vop_ugetattr_lite_debugpre(x) do { } while (0) +#define vop_ugetattr_lite_debugpost(x, y) do { } while (0) +#define vop_getattr_lite_debugpre(x) do { } while (0) +#define vop_getattr_lite_debugpost(x, y) do { } while (0) #endif void vop_rename_fail(struct vop_rename_args *ap); Index: sys/ufs/ufs/ufs_vnops.c =================================================================== --- sys/ufs/ufs/ufs_vnops.c +++ sys/ufs/ufs/ufs_vnops.c @@ -109,6 +109,8 @@ static vop_close_t ufs_close; static vop_create_t ufs_create; static vop_stat_t ufs_stat; +static vop_ugetattr_lite_t ufs_ugetattr_lite; +static vop_getattr_lite_t ufs_getattr_lite; static vop_getattr_t ufs_getattr; static vop_ioctl_t ufs_ioctl; static vop_link_t ufs_link; @@ -526,6 +528,56 @@ return (vop_stat_helper_post(ap, error)); } +static void __inline +ufs_getattr_lite_fill(struct inode *ip, struct vattr_lite *lvap) +{ + + if (I_IS_UFS1(ip)) { + lvap->va_size = ip->i_din1->di_size; + } else { + lvap->va_size = ip->i_din2->di_size; + } + lvap->va_nlink = ip->i_effnlink; + lvap->va_type = IFTOVT(ip->i_mode); + lvap->va_mode = ip->i_mode & ~IFMT; + lvap->va_uid = ip->i_uid; + lvap->va_gid = ip->i_gid; +} + +static int +ufs_ugetattr_lite(struct vop_ugetattr_lite_args *ap) +{ + struct vnode *vp; + struct inode *ip; + struct vattr_lite *lvap; + + vfs_smr_enter(); + vp = ap->a_vp; + ip = VTOI_SMR(vp); + if (__predict_false(ip == NULL)) { + vfs_smr_exit(); + return (ENOENT); + } + lvap = ap->a_lvap; + ufs_getattr_lite_fill(ip, lvap); + vfs_smr_exit(); + return (0); +} + +static int +ufs_getattr_lite(struct vop_getattr_lite_args *ap) +{ + struct vnode *vp; + struct inode *ip; + struct vattr_lite *lvap; + + vp = ap->a_vp; + ip = VTOI(vp); + lvap = ap->a_lvap; + ufs_getattr_lite_fill(ip, lvap); + return (0); +} + /* ARGSUSED */ static int ufs_getattr(ap) @@ -2884,6 +2936,8 @@ .vop_close = ufs_close, .vop_create = ufs_create, .vop_stat = ufs_stat, + .vop_ugetattr_lite = ufs_ugetattr_lite, + .vop_getattr_lite = ufs_getattr_lite, .vop_getattr = ufs_getattr, .vop_inactive = ufs_inactive, .vop_ioctl = ufs_ioctl,