The branch main has been updated by kib:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=85cff1455a8c3422c6ba8a9f895b5d65118f3499

commit 85cff1455a8c3422c6ba8a9f895b5d65118f3499
Author:     Konstantin Belousov <[email protected]>
AuthorDate: 2022-10-17 15:26:43 +0000
Commit:     Konstantin Belousov <[email protected]>
CommitDate: 2022-10-19 17:24:07 +0000

    tmpfs: implement FIOSEEKDATA and FIOSEEKHOLE
    
    providing the support for lseek(2) SEEK_DATA and SEEK_HOLE.
    
    Reviewed by:    markj
    Tested by:      pho
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D37024
---
 sys/fs/tmpfs/tmpfs_vnops.c | 130 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 130 insertions(+)

diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c
index 9bcf835aa627..aaeaa843eb43 100644
--- a/sys/fs/tmpfs/tmpfs_vnops.c
+++ b/sys/fs/tmpfs/tmpfs_vnops.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/dirent.h>
 #include <sys/fcntl.h>
 #include <sys/file.h>
+#include <sys/filio.h>
 #include <sys/limits.h>
 #include <sys/lockf.h>
 #include <sys/lock.h>
@@ -63,6 +64,9 @@ __FBSDID("$FreeBSD$");
 #include <vm/vm.h>
 #include <vm/vm_param.h>
 #include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+#include <vm/swap_pager.h>
 
 #include <fs/tmpfs/tmpfs_vnops.h>
 #include <fs/tmpfs/tmpfs.h>
@@ -1820,6 +1824,131 @@ restart_locked:
        return (ENOENT);
 }
 
+static off_t
+tmpfs_seek_data_locked(vm_object_t obj, off_t noff)
+{
+       vm_page_t m;
+       vm_pindex_t p, p_m, p_swp;
+
+       p = OFF_TO_IDX(noff);
+       m = vm_page_find_least(obj, p);
+
+       /*
+        * Microoptimize the most common case for SEEK_DATA, where
+        * there is no hole and the page is resident.
+        */
+       if (m != NULL && vm_page_any_valid(m) && m->pindex == p)
+               return (noff);
+
+       p_swp = swap_pager_find_least(obj, p);
+       if (p_swp == p)
+               return (noff);
+
+       p_m = m == NULL ? obj->size : m->pindex;
+       return (IDX_TO_OFF(MIN(p_m, p_swp)));
+}
+
+static off_t
+tmpfs_seek_next(off_t noff)
+{
+       return (noff + PAGE_SIZE - (noff & PAGE_MASK));
+}
+
+static int
+tmpfs_seek_clamp(struct tmpfs_node *tn, off_t *noff, bool seekdata)
+{
+       if (*noff < tn->tn_size)
+               return (0);
+       if (seekdata)
+               return (ENXIO);
+       *noff = tn->tn_size;
+       return (0);
+}
+
+static off_t
+tmpfs_seek_hole_locked(vm_object_t obj, off_t noff)
+{
+       vm_page_t m;
+       vm_pindex_t p, p_swp;
+
+       for (;; noff = tmpfs_seek_next(noff)) {
+               /*
+                * Walk over the largest sequential run of the valid pages.
+                */
+               for (m = vm_page_lookup(obj, OFF_TO_IDX(noff));
+                   m != NULL && vm_page_any_valid(m);
+                   m = vm_page_next(m), noff = tmpfs_seek_next(noff))
+                       ;
+
+               /*
+                * Found a hole in the object's page queue.  Check if
+                * there is a hole in the swap at the same place.
+                */
+               p = OFF_TO_IDX(noff);
+               p_swp = swap_pager_find_least(obj, p);
+               if (p_swp != p) {
+                       noff = IDX_TO_OFF(p);
+                       break;
+               }
+       }
+       return (noff);
+}
+
+static int
+tmpfs_seek_datahole(struct vnode *vp, off_t *off, bool seekdata)
+{
+       struct tmpfs_node *tn;
+       vm_object_t obj;
+       off_t noff;
+       int error;
+
+       if (vp->v_type != VREG)
+               return (ENOTTY);
+       tn = VP_TO_TMPFS_NODE(vp);
+       noff = *off;
+       if (noff < 0)
+               return (ENXIO);
+       error = tmpfs_seek_clamp(tn, &noff, seekdata);
+       if (error != 0)
+               return (error);
+       obj = tn->tn_reg.tn_aobj;
+
+       VM_OBJECT_RLOCK(obj);
+       noff = seekdata ? tmpfs_seek_data_locked(obj, noff) :
+           tmpfs_seek_hole_locked(obj, noff);
+       VM_OBJECT_RUNLOCK(obj);
+
+       error = tmpfs_seek_clamp(tn, &noff, seekdata);
+       if (error == 0)
+               *off = noff;
+       return (error);
+}
+
+static int
+tmpfs_ioctl(struct vop_ioctl_args *ap)
+{
+       struct vnode *vp = ap->a_vp;
+       int error = 0;
+
+       switch (ap->a_command) {
+       case FIOSEEKDATA:
+       case FIOSEEKHOLE:
+               error = vn_lock(vp, LK_SHARED);
+               if (error != 0) {
+                       error = EBADF;
+                       break;
+               }
+               error = tmpfs_seek_datahole(vp, (off_t *)ap->a_data,
+                   ap->a_command == FIOSEEKDATA);
+               VOP_UNLOCK(vp);
+               break;
+       default:
+               error = ENOTTY;
+               break;
+       }
+       return (error);
+}
+
 /*
  * Vnode operations vector used for files stored in a tmpfs file system.
  */
@@ -1863,6 +1992,7 @@ struct vop_vector tmpfs_vnodeop_entries = {
        .vop_unlock =                   vop_unlock,
        .vop_islocked =                 vop_islocked,
        .vop_add_writecount =           vop_stdadd_writecount_nomsync,
+       .vop_ioctl =                    tmpfs_ioctl,
 };
 VFS_VOP_VECTOR_REGISTER(tmpfs_vnodeop_entries);
 

Reply via email to