[PATCH V12 08/12] famfs: iomap_begin and file-to-dax offset resolution
John Groves <[email protected]> Mon, 3 Aug 2026 02:29:37 +0000
| Newsgroups | dev.linux.lists.nvdimm,dev.linux.lists.fuse-devel,org.kernel.vger.linux-cxl,org.kernel.vger.linux-doc,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <0100019fc5748c1e-45cb66a3-f847-4c3e-8a89-a04c89d24b32-000000@email.amazonses.com> |
From: John Groves <[email protected]>=0D=0A=0D=0AAdd the iomap resolver tha= t maps a file offset to a (daxdev, offset) pair:=0D=0Afamfs_meta_to_dax_o= ffset() for simple extent lists and=0D=0Afamfs_meta_to_dax_offset_interle= aved() for striped files, backed by the=0D=0Aper-daxdev health check (fam= fs_dax_err) and table lookup=0D=0A(famfs_daxdev_for_index), plus famfs_io= map_begin() and famfs_iomap_ops.=0D=0A=0D=0AWire it into the read, write = and fault paths by replacing their=0D=0ANULL /*&famfs_iomap_ops*/ stub wi= th &famfs_iomap_ops, so dax_iomap_rw() and=0D=0Adax_iomap_fault() now res= olve through famfs.=0D=0A=0D=0ASigned-off-by: John Groves <[email protected]= t>=0D=0A---=0D=0A fs/famfs/famfs_file.c | 298 +++++++++++++++++++++++++++= ++++++++++++++-=0D=0A 1 file changed, 295 insertions(+), 3 deletions(-)=0D= =0A=0D=0Adiff --git a/fs/famfs/famfs_file.c b/fs/famfs/famfs_file.c=0D=0A= index d710c8a0c923..e7f271ce6d03 100644=0D=0A--- a/fs/famfs/famfs_file.c=0D= =0A+++ b/fs/famfs/famfs_file.c=0D=0A@@ -320,6 +320,298 @@ famfs_file_ioct= l(struct file *file, unsigned int cmd, unsigned long arg)=0D=0A =09return= rc;=0D=0A }=0D=0A=20=0D=0A+/********************************************= *************************=0D=0A+ * iomap_operations=0D=0A+ *=0D=0A+ * Thi= s stuff uses the iomap (dax-related) helpers to resolve file offsets to=0D= =0A+ * offsets within a dax device.=0D=0A+ */=0D=0A+=0D=0A+static ssize_t= famfs_file_invalid(struct inode *inode);=0D=0A+=0D=0A+/* Check the healt= h of a daxdev table slot */=0D=0A+static int famfs_dax_err(struct famfs_d= axdev *dd)=0D=0A+{=0D=0A+=09if (!dd->valid) {=0D=0A+=09=09pr_debug("%s: d= axdev=3D%s invalid\n", __func__, dd->name);=0D=0A+=09=09return -EIO;=0D=0A= +=09}=0D=0A+=09if (dd->dax_err) {=0D=0A+=09=09pr_debug("%s: daxdev=3D%s d= ax_err\n", __func__, dd->name);=0D=0A+=09=09return -EIO;=0D=0A+=09}=0D=0A= +=09if (dd->error) {=0D=0A+=09=09pr_debug("%s: daxdev=3D%s memory error\n= ", __func__, dd->name);=0D=0A+=09=09return -EHWPOISON;=0D=0A+=09}=0D=0A+=09= return 0;=0D=0A+}=0D=0A+=0D=0A+/*=0D=0A+ * famfs_daxdev_from_index() - re= solve an extent's dev_index to a health-checked=0D=0A+ * dax_device from = the table. On success returns the dax_device and sets=0D=0A+ * *errp =3D = 0; on failure returns NULL and sets *errp (< 0).=0D=0A+ */=0D=0A+static s= truct dax_device *=0D=0A+famfs_daxdev_from_index(struct famfs_fs_info *fs= i, u64 dev_index, int *errp)=0D=0A+{=0D=0A+=09struct famfs_dax_devlist *d= evlist =3D fsi->dax_devlist;=0D=0A+=09struct famfs_daxdev *dd;=0D=0A+=09i= nt rc;=0D=0A+=0D=0A+=09if (!devlist || dev_index >=3D devlist->nslots) {=0D= =0A+=09=09pr_debug("%s: dev_index %llu out of range\n",=0D=0A+=09=09=09__= func__, dev_index);=0D=0A+=09=09*errp =3D -EIO;=0D=0A+=09=09return NULL;=0D= =0A+=09}=0D=0A+=09dd =3D &devlist->devlist[dev_index];=0D=0A+=09rc =3D fa= mfs_dax_err(dd);=0D=0A+=09if (rc) {=0D=0A+=09=09*errp =3D rc;=0D=0A+=09=09= return NULL;=0D=0A+=09}=0D=0A+=09*errp =3D 0;=0D=0A+=09return dd->devp;=0D= =0A+}=0D=0A+=0D=0A+static int=0D=0A+famfs_meta_to_dax_offset_interleaved(= struct inode *inode, struct iomap *iomap,=0D=0A+=09=09=09 loff_t file_off= set, off_t len, unsigned int flags)=0D=0A+{=0D=0A+=09struct famfs_fs_info= *fsi =3D inode->i_sb->s_fs_info;=0D=0A+=09struct famfs_file_meta *meta = =3D inode->i_private;=0D=0A+=09loff_t local_offset =3D file_offset;=0D=0A= +=09int rc;=0D=0A+=09int i;=0D=0A+=0D=0A+=09/* This function is only for = extent_type FAMFS_IOC_EXT_INTERLEAVE */=0D=0A+=09if (meta->fm_extent_type= !=3D FAMFS_IOC_EXT_INTERLEAVE) {=0D=0A+=09=09pr_debug("%s: bad extent ty= pe\n", __func__);=0D=0A+=09=09goto err_out;=0D=0A+=09}=0D=0A+=0D=0A+=09if= (fsi->deverror || famfs_file_invalid(inode))=0D=0A+=09=09goto err_out;=0D= =0A+=0D=0A+=09iomap->offset =3D file_offset;=0D=0A+=0D=0A+=09for (i =3D 0= ; i < meta->fm_niext; i++) {=0D=0A+=09=09struct famfs_meta_interleaved_ex= t *fei =3D &meta->ie[i];=0D=0A+=09=09u64 chunk_size =3D fei->fie_chunk_si= ze;=0D=0A+=09=09u64 nstrips =3D fei->fie_nstrips;=0D=0A+=09=09u64 ext_siz= e =3D fei->fie_nbytes;=0D=0A+=0D=0A+=09=09ext_size =3D min_t(u64, ext_siz= e, meta->file_size);=0D=0A+=0D=0A+=09=09if (ext_size =3D=3D 0)=0D=0A+=09=09= =09goto err_out;=0D=0A+=0D=0A+=09=09/* Is the data is in this striped ext= ent=3F */=0D=0A+=09=09if (local_offset < ext_size) {=0D=0A+=09=09=09u64 c= hunk_num =3D local_offset / chunk_size;=0D=0A+=09=09=09u64 chunk_of= fset =3D local_offset % chunk_size;=0D=0A+=09=09=09u64 stripe_num = =3D chunk_num / nstrips;=0D=0A+=09=09=09u64 strip_num =3D chunk_nu= m % nstrips;=0D=0A+=09=09=09u64 chunk_remainder =3D chunk_size - chunk_of= fset;=0D=0A+=09=09=09u64 strip_offset =3D chunk_offset + (stripe_num *= chunk_size);=0D=0A+=09=09=09struct famfs_meta_simple_ext *strip =3D &fei= ->ie_strips[strip_num];=0D=0A+=09=09=09struct dax_device *daxdev;=0D=0A+=0D= =0A+=09=09=09/*=0D=0A+=09=09=09 * MAP_CREATE only checks that the strips'= combined=0D=0A+=09=09=09 * length covers the file, not that each strip i= s large=0D=0A+=09=09=09 * enough for the chunks striped onto it. Guard ag= ainst a=0D=0A+=09=09=09 * malformed fmap with an undersized strip so we n= ever=0D=0A+=09=09=09 * resolve to a dax offset past the strip's extent.=0D= =0A+=09=09=09 */=0D=0A+=09=09=09if (strip_offset >=3D strip->ext_len)=0D=0A= +=09=09=09=09goto err_out;=0D=0A+=0D=0A+=09=09=09daxdev =3D famfs_daxdev_= from_index(fsi, strip->dev_index, &rc);=0D=0A+=09=09=09if (!daxdev) {=0D=0A= +=09=09=09=09meta->error =3D true;=0D=0A+=09=09=09=09return rc;=0D=0A+=09= =09=09}=0D=0A+=0D=0A+=09=09=09iomap->addr =3D strip->ext_offset + stri= p_offset;=0D=0A+=09=09=09iomap->offset =3D file_offset;=0D=0A+=09=09=09i= omap->length =3D min_t(loff_t, len, chunk_remainder);=0D=0A+=09=09=09iom= ap->length =3D min_t(loff_t, iomap->length,=0D=0A+=09=09=09=09=09 = strip->ext_len - strip_offset);=0D=0A+=09=09=09iomap->dax_dev =3D daxdev;= =0D=0A+=09=09=09iomap->type =3D IOMAP_MAPPED;=0D=0A+=09=09=09iomap->fl= ags =3D flags;=0D=0A+=0D=0A+=09=09=09return 0;=0D=0A+=09=09}=0D=0A+=09=09= local_offset -=3D ext_size; /* offset is beyond this striped extent */=0D= =0A+=09}=0D=0A+=0D=0A+ err_out:=0D=0A+=09/*=0D=0A+=09 * We fell out the e= nd of the extent list (access past EOF) or the file=0D=0A+=09 * is invali= d. Return -EIO: iomap requires a non-zero-length mapping on=0D=0A+=09 * s= uccess (iomap_iter_done() warns on length =3D=3D 0), so signal the error=0D= =0A+=09 * rather than returning a zero-length IOMAP_MAPPED.=0D=0A+=09 */=0D= =0A+=09pr_debug("%s: could not resolve file_offset %lld (past EOF=3F)\n",= =0D=0A+=09=09 __func__, (long long)file_offset);=0D=0A+=0D=0A+=09iomap->a= ddr =3D 0; /* there is no valid dax device offset */=0D=0A+=09iomap->o= ffset =3D file_offset; /* file offset */=0D=0A+=09iomap->length =3D 0;=0D= =0A+=09iomap->dax_dev =3D famfs_daxdev_from_index(fsi, 0, &rc);=0D=0A+=09= iomap->type =3D IOMAP_MAPPED;=0D=0A+=09iomap->flags =3D flags;=0D=0A= +=0D=0A+=09return -EIO;=0D=0A+}=0D=0A+=0D=0A+/**=0D=0A+ * famfs_meta_to_d= ax_offset() - Resolve (file, offset, len) to (daxdev, offset, len)=0D=0A+= *=0D=0A+ * This function is called by famfs_iomap_begin() to resolve an = offset in a=0D=0A+ * file to an offset in a dax device. This is upcalled = from dax from calls to=0D=0A+ * both * dax_iomap_fault() and dax_iomap_r= w(). Dax finishes the job resolving=0D=0A+ * a fault to a specific physic= al page (the fault case) or doing a memcpy=0D=0A+ * variant (the rw case)= =0D=0A+ *=0D=0A+ * Pages can be PTE (4k), PMD (2MiB) or (theoretically) P= uD (1GiB)=0D=0A+ * (these sizes are for X86; may vary on other cpu archit= ectures=0D=0A+ *=0D=0A+ * @inode: The file where the fault occurred=0D=0A= + * @iomap: To be filled in to indicate where to find the right mem= ory,=0D=0A+ * relative to a dax device.=0D=0A+ * @file_off= set: Within the file where the fault occurred (will be page boundary)=0D=0A= + * @len: The length of the faulted mapping (will be a page multi= ple)=0D=0A+ * (will be trimmed in *iomap if it's disjoint i= n the extent list)=0D=0A+ * @flags:=0D=0A+ *=0D=0A+ * Return values: 0. (= info is returned in a modified @iomap struct)=0D=0A+ */=0D=0A+static int=0D= =0A+famfs_meta_to_dax_offset(struct inode *inode, struct iomap *iomap,=0D= =0A+=09=09=09 loff_t file_offset, off_t len, unsigned int flags)=0D=0A+{=0D= =0A+=09struct famfs_fs_info *fsi =3D inode->i_sb->s_fs_info;=0D=0A+=09st= ruct famfs_file_meta *meta =3D inode->i_private;=0D=0A+=09loff_t local_of= fset =3D file_offset;=0D=0A+=09int rc;=0D=0A+=09int i;=0D=0A+=0D=0A+=09if= (fsi->deverror || famfs_file_invalid(inode))=0D=0A+=09=09goto err_out;=0D= =0A+=0D=0A+=09if (meta->fm_extent_type =3D=3D FAMFS_IOC_EXT_INTERLEAVE)=0D= =0A+=09=09return famfs_meta_to_dax_offset_interleaved(inode,=0D=0A+=09=09= =09=09=09iomap, file_offset, len, flags);=0D=0A+=0D=0A+=09if (meta->fm_ex= tent_type !=3D FAMFS_IOC_EXT_SIMPLE)=0D=0A+=09=09goto err_out;=0D=0A+=0D=0A= +=09iomap->offset =3D file_offset;=0D=0A+=0D=0A+=09for (i =3D 0; i < meta= ->fm_nextents; i++) {=0D=0A+=09=09loff_t dax_ext_offset =3D meta->se[i].e= xt_offset;=0D=0A+=09=09loff_t dax_ext_len =3D meta->se[i].ext_len;=0D=0A= +=0D=0A+=09=09if ((dax_ext_offset =3D=3D 0) &&=0D=0A+=09=09 (meta->fil= e_type !=3D FAMFS_SUPERBLOCK))=0D=0A+=09=09=09pr_warn("%s: zero offset on= non-superblock file!!\n",=0D=0A+=09=09=09=09__func__);=0D=0A+=0D=0A+=09=09= /* local_offset is the offset minus the size of extents skipped=0D=0A+=09= =09 * so far; If local_offset < dax_ext_len, the data of interest=0D=0A+=09= =09 * starts in this extent=0D=0A+=09=09 */=0D=0A+=09=09if (local_offset = < dax_ext_len) {=0D=0A+=09=09=09loff_t ext_len_remainder =3D dax_ext_len = - local_offset;=0D=0A+=09=09=09struct dax_device *daxdev;=0D=0A+=0D=0A+=09= =09=09daxdev =3D famfs_daxdev_from_index(fsi,=0D=0A+=09=09=09=09=09=09met= a->se[i].dev_index, &rc);=0D=0A+=09=09=09if (!daxdev) {=0D=0A+=09=09=09=09= meta->error =3D true;=0D=0A+=09=09=09=09return rc;=0D=0A+=09=09=09}=0D=0A= +=0D=0A+=09=09=09/*=0D=0A+=09=09=09 * OK, we found the file metadata exte= nt where this=0D=0A+=09=09=09 * data begins=0D=0A+=09=09=09 * @local_offs= et - The offset within the current=0D=0A+=09=09=09 * = extent=0D=0A+=09=09=09 * @ext_len_remainder - Remaining length of e= xt after=0D=0A+=09=09=09 * skipping local_offset=0D=0A= +=09=09=09 * Outputs:=0D=0A+=09=09=09 * iomap->addr: the offset within = the dax device where=0D=0A+=09=09=09 * the data starts=0D= =0A+=09=09=09 * iomap->offset: the file offset=0D=0A+=09=09=09 * iomap->l= ength: the valid length resolved here=0D=0A+=09=09=09 */=0D=0A+=09=09=09i= omap->addr =3D dax_ext_offset + local_offset;=0D=0A+=09=09=09iomap->of= fset =3D file_offset;=0D=0A+=09=09=09iomap->length =3D min_t(loff_t, le= n, ext_len_remainder);=0D=0A+=09=09=09iomap->dax_dev =3D daxdev;=0D=0A+=09= =09=09iomap->type =3D IOMAP_MAPPED;=0D=0A+=09=09=09iomap->flags =3D = flags;=0D=0A+=0D=0A+=09=09=09return 0;=0D=0A+=09=09}=0D=0A+=09=09local_of= fset -=3D dax_ext_len; /* Get ready for the next extent */=0D=0A+=09}=0D=0A= +=0D=0A+ err_out:=0D=0A+=09/*=0D=0A+=09 * We fell out the end of the exte= nt list (access past EOF) or the file=0D=0A+=09 * is in an invalid state.= Return -EIO: iomap requires a non-zero-length=0D=0A+=09 * mapping on suc= cess (iomap_iter_done() warns on length =3D=3D 0), so signal=0D=0A+=09 * = the error rather than returning a zero-length IOMAP_MAPPED. dax turns=0D=0A= +=09 * this into a short read/write or a SIGBUS.=0D=0A+=09 */=0D=0A+=09pr= _debug("%s: could not resolve file_offset %lld (past EOF=3F)\n",=0D=0A+=09= =09 __func__, (long long)file_offset);=0D=0A+=0D=0A+=09iomap->addr =3D= 0; /* there is no valid dax device offset */=0D=0A+=09iomap->offset =3D= file_offset; /* file offset */=0D=0A+=09iomap->length =3D 0;=0D=0A+=09i= omap->dax_dev =3D famfs_daxdev_from_index(fsi, 0, &rc);=0D=0A+=09iomap->t= ype =3D IOMAP_MAPPED;=0D=0A+=09iomap->flags =3D flags;=0D=0A+=0D=0A+= =09return -EIO;=0D=0A+}=0D=0A+=0D=0A+/**=0D=0A+ * famfs_iomap_begin() - H= andler for iomap_begin upcall from dax=0D=0A+ *=0D=0A+ * This function is= pretty simple because files are=0D=0A+ * * never partially allocated=0D=0A= + * * never have holes (never sparse)=0D=0A+ * * never "allocate on write= "=0D=0A+ *=0D=0A+ * @inode: inode for the file being accessed=0D=0A+ * @= offset: offset within the file=0D=0A+ * @length: Length being accessed at= offset=0D=0A+ * @flags:=0D=0A+ * @iomap: iomap struct to be filled in, = resolving (offset, length) to=0D=0A+ * (daxdev, offset, len)=0D=0A= + * @srcmap:=0D=0A+ */=0D=0A+static int=0D=0A+famfs_iomap_begin(struct in= ode *inode, loff_t offset, loff_t length,=0D=0A+=09=09 unsigned int flag= s, struct iomap *iomap, struct iomap *srcmap)=0D=0A+{=0D=0A+=09return fam= fs_meta_to_dax_offset(inode, iomap, offset, length, flags);=0D=0A+}=0D=0A= +=0D=0A+/* Note: We never need a special set of write_iomap_ops because f= amfs never=0D=0A+ * performs allocation on write.=0D=0A+ */=0D=0A+const s= truct iomap_ops famfs_iomap_ops =3D {=0D=0A+=09.iomap_begin=09=09=3D famf= s_iomap_begin,=0D=0A+};=0D=0A+=0D=0A /***********************************= **********************************=0D=0A * vm_operations=0D=0A */=0D=0A= @@ -346,7 +638,7 @@ __famfs_filemap_fault(struct vm_fault *vmf, unsigned = int order,=0D=0A =09=09file_update_time(vmf->vma->vm_file);=0D=0A =09}=0D= =0A=20=0D=0A-=09ret =3D dax_iomap_fault(vmf, order, &pfn, NULL, NULL /*&f= amfs_iomap_ops */);=0D=0A+=09ret =3D dax_iomap_fault(vmf, order, &pfn, NU= LL, &famfs_iomap_ops);=0D=0A =09if (ret & VM_FAULT_NEEDDSYNC)=0D=0A =09=09= ret =3D dax_finish_sync_fault(vmf, order, pfn);=0D=0A=20=0D=0A@@ -468,7 += 760,7 @@ famfs_dax_read_iter(struct kiocb *iocb, struct iov_iter=09*to)=0D= =0A =09=09return rc;=0D=0A =09}=0D=0A=20=0D=0A-=09rc =3D dax_iomap_rw(ioc= b, to, NULL /*&famfs_iomap_ops */);=0D=0A+=09rc =3D dax_iomap_rw(iocb, to= , &famfs_iomap_ops);=0D=0A =09inode_unlock_shared(inode);=0D=0A=20=0D=0A = =09file_accessed(iocb->ki_filp);=0D=0A@@ -501,7 +793,7 @@ famfs_dax_write= _iter(struct kiocb *iocb, struct iov_iter *from)=0D=0A =09=09return rc;=0D= =0A =09}=0D=0A=20=0D=0A-=09rc =3D dax_iomap_rw(iocb, from, NULL /*&famfs_= iomap_ops*/);=0D=0A+=09rc =3D dax_iomap_rw(iocb, from, &famfs_iomap_ops);= =0D=0A =09inode_unlock(inode);=0D=0A =09return rc;=0D=0A }=0D=0A--=20=0D=0A= 2.53.0=0D=0A=0D=0A