]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
afs: Use netfslib for symlinks, allowing them to be cached
authorDavid Howells <dhowells@redhat.com>
Mon, 16 Dec 2024 20:41:12 +0000 (20:41 +0000)
committerChristian Brauner <brauner@kernel.org>
Fri, 20 Dec 2024 21:34:07 +0000 (22:34 +0100)
Use netfslib to read symlinks, thereby allowing them to be cached by
fscache and cachefiles.

Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20241216204124.3752367-23-dhowells@redhat.com
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: linux-afs@lists.infradead.org
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/afs/file.c
fs/afs/inode.c
fs/afs/internal.h
fs/afs/mntpt.c
include/trace/events/afs.h

index 5bc36bfaa1732805f38755ac7d4996c8e443a441..48695a50d2f99d5b0c0ba8c8c7ea9c681e59c049 100644 (file)
@@ -20,7 +20,6 @@
 #include "internal.h"
 
 static int afs_file_mmap(struct file *file, struct vm_area_struct *vma);
-static int afs_symlink_read_folio(struct file *file, struct folio *folio);
 
 static ssize_t afs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter);
 static ssize_t afs_file_splice_read(struct file *in, loff_t *ppos,
@@ -61,13 +60,6 @@ const struct address_space_operations afs_file_aops = {
        .writepages     = afs_writepages,
 };
 
-const struct address_space_operations afs_symlink_aops = {
-       .read_folio     = afs_symlink_read_folio,
-       .release_folio  = netfs_release_folio,
-       .invalidate_folio = netfs_invalidate_folio,
-       .migrate_folio  = filemap_migrate_folio,
-};
-
 static const struct vm_operations_struct afs_vm_ops = {
        .open           = afs_vm_open,
        .close          = afs_vm_close,
@@ -346,30 +338,6 @@ static void afs_issue_read(struct netfs_io_subrequest *subreq)
        queue_work(system_long_wq, &subreq->work);
 }
 
-static int afs_symlink_read_folio(struct file *file, struct folio *folio)
-{
-       struct afs_vnode *vnode = AFS_FS_I(folio->mapping->host);
-       struct afs_read *fsreq;
-       int ret;
-
-       fsreq = afs_alloc_read(GFP_NOFS);
-       if (!fsreq)
-               return -ENOMEM;
-
-       fsreq->pos      = folio_pos(folio);
-       fsreq->len      = folio_size(folio);
-       fsreq->vnode    = vnode;
-       fsreq->iter     = &fsreq->def_iter;
-       iov_iter_xarray(&fsreq->def_iter, ITER_DEST, &folio->mapping->i_pages,
-                       fsreq->pos, fsreq->len);
-
-       ret = afs_fetch_data(fsreq->vnode, fsreq);
-       if (ret == 0)
-               folio_mark_uptodate(folio);
-       folio_unlock(folio);
-       return ret;
-}
-
 static int afs_init_request(struct netfs_io_request *rreq, struct file *file)
 {
        struct afs_vnode *vnode = AFS_FS_I(rreq->inode);
index 0ed1e5c35fef9fda2bd162545a373297e823c30f..6934cc30a4ca63fd44ebbb9bb73a25be6dc8fa9e 100644 (file)
 #include "internal.h"
 #include "afs_fs.h"
 
+static void afs_put_link(void *arg)
+{
+       struct folio *folio = virt_to_folio(arg);
+
+       kunmap_local(arg);
+       folio_put(folio);
+}
+
+const char *afs_get_link(struct dentry *dentry, struct inode *inode,
+                        struct delayed_call *callback)
+{
+       struct afs_vnode *vnode = AFS_FS_I(inode);
+       struct folio *folio;
+       char *content;
+       ssize_t ret;
+
+       if (atomic64_read(&vnode->cb_expires_at) == AFS_NO_CB_PROMISE ||
+           !test_bit(AFS_VNODE_DIR_READ, &vnode->flags)) {
+               if (!dentry)
+                       return ERR_PTR(-ECHILD);
+               ret = afs_read_single(vnode, NULL);
+               if (ret < 0)
+                       return ERR_PTR(ret);
+       }
+
+       folio = folioq_folio(vnode->directory, 0);
+       folio_get(folio);
+       content = kmap_local_folio(folio, 0);
+       set_delayed_call(callback, afs_put_link, content);
+       return content;
+}
+
+int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
+{
+       DEFINE_DELAYED_CALL(done);
+       const char *content;
+       int len;
+
+       content = afs_get_link(dentry, d_inode(dentry), &done);
+       if (IS_ERR(content)) {
+               do_delayed_call(&done);
+               return PTR_ERR(content);
+       }
+
+       len = umin(strlen(content), buflen);
+       if (copy_to_user(buffer, content, len))
+               len = -EFAULT;
+       do_delayed_call(&done);
+       return len;
+}
+
 static const struct inode_operations afs_symlink_inode_operations = {
-       .get_link       = page_get_link,
+       .get_link       = afs_get_link,
+       .readlink       = afs_readlink,
 };
 
 static noinline void dump_vnode(struct afs_vnode *vnode, struct afs_vnode *parent_vnode)
@@ -124,13 +176,13 @@ static int afs_inode_init_from_status(struct afs_operation *op,
                        inode->i_mode   = S_IFDIR | 0555;
                        inode->i_op     = &afs_mntpt_inode_operations;
                        inode->i_fop    = &afs_mntpt_file_operations;
-                       inode->i_mapping->a_ops = &afs_symlink_aops;
                } else {
                        inode->i_mode   = S_IFLNK | status->mode;
                        inode->i_op     = &afs_symlink_inode_operations;
-                       inode->i_mapping->a_ops = &afs_symlink_aops;
                }
+               inode->i_mapping->a_ops = &afs_dir_aops;
                inode_nohighmem(inode);
+               mapping_set_release_always(inode->i_mapping);
                break;
        default:
                dump_vnode(vnode, op->file[0].vnode != vnode ? op->file[0].vnode : NULL);
@@ -443,7 +495,8 @@ static void afs_get_inode_cache(struct afs_vnode *vnode)
        struct afs_vnode_cache_aux aux;
 
        if (vnode->status.type != AFS_FTYPE_FILE &&
-           vnode->status.type != AFS_FTYPE_DIR) {
+           vnode->status.type != AFS_FTYPE_DIR &&
+           vnode->status.type != AFS_FTYPE_SYMLINK) {
                vnode->netfs.cache = NULL;
                return;
        }
@@ -657,7 +710,8 @@ void afs_evict_inode(struct inode *inode)
 
        ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode);
 
-       if ((S_ISDIR(inode->i_mode)) &&
+       if ((S_ISDIR(inode->i_mode) ||
+            S_ISLNK(inode->i_mode)) &&
            (inode->i_state & I_DIRTY) &&
            !sbi->dyn_root) {
                struct writeback_control wbc = {
index 1744a93aae27296c9c9723e49b1a14a4a092a376..7f170455cf25149e57e918de28ee22972a4cf301 100644 (file)
@@ -1116,7 +1116,6 @@ extern void afs_dynroot_depopulate(struct super_block *);
  * file.c
  */
 extern const struct address_space_operations afs_file_aops;
-extern const struct address_space_operations afs_symlink_aops;
 extern const struct inode_operations afs_file_inode_operations;
 extern const struct file_operations afs_file_operations;
 extern const struct netfs_request_ops afs_req_ops;
@@ -1222,6 +1221,9 @@ extern void afs_fs_probe_cleanup(struct afs_net *);
  */
 extern const struct afs_operation_ops afs_fetch_status_operation;
 
+const char *afs_get_link(struct dentry *dentry, struct inode *inode,
+                        struct delayed_call *callback);
+int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen);
 extern void afs_vnode_commit_status(struct afs_operation *, struct afs_vnode_param *);
 extern int afs_fetch_status(struct afs_vnode *, struct key *, bool, afs_access_t *);
 extern int afs_ilookup5_test_by_fid(struct inode *, void *);
index 297487ee832317a43608b38db09186b47cb1d583..507c25a5b2cb10ccdba1b1e2cf4b23e99978d60d 100644 (file)
@@ -30,7 +30,7 @@ const struct file_operations afs_mntpt_file_operations = {
 
 const struct inode_operations afs_mntpt_inode_operations = {
        .lookup         = afs_mntpt_lookup,
-       .readlink       = page_readlink,
+       .readlink       = afs_readlink,
        .getattr        = afs_getattr,
 };
 
@@ -118,9 +118,9 @@ static int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt)
                ctx->volnamesz = sizeof(afs_root_volume) - 1;
        } else {
                /* read the contents of the AFS special symlink */
-               struct page *page;
+               DEFINE_DELAYED_CALL(cleanup);
+               const char *content;
                loff_t size = i_size_read(d_inode(mntpt));
-               char *buf;
 
                if (src_as->cell)
                        ctx->cell = afs_use_cell(src_as->cell, afs_cell_trace_use_mntpt);
@@ -128,16 +128,16 @@ static int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt)
                if (size < 2 || size > PAGE_SIZE - 1)
                        return -EINVAL;
 
-               page = read_mapping_page(d_inode(mntpt)->i_mapping, 0, NULL);
-               if (IS_ERR(page))
-                       return PTR_ERR(page);
+               content = afs_get_link(mntpt, d_inode(mntpt), &cleanup);
+               if (IS_ERR(content)) {
+                       do_delayed_call(&cleanup);
+                       return PTR_ERR(content);
+               }
 
-               buf = kmap(page);
                ret = -EINVAL;
-               if (buf[size - 1] == '.')
-                       ret = vfs_parse_fs_string(fc, "source", buf, size - 1);
-               kunmap(page);
-               put_page(page);
+               if (content[size - 1] == '.')
+                       ret = vfs_parse_fs_string(fc, "source", content, size - 1);
+               do_delayed_call(&cleanup);
                if (ret < 0)
                        return ret;
 
index d05f2c09efe37340d64131ac73cb5861e16d3a20..49a749672e38ee1aa463ddece5cc429cbb972fda 100644 (file)
@@ -422,6 +422,7 @@ enum yfs_cm_operation {
        EM(afs_file_error_dir_over_end,         "DIR_ENT_OVER_END")     \
        EM(afs_file_error_dir_small,            "DIR_SMALL")            \
        EM(afs_file_error_dir_unmarked_ext,     "DIR_UNMARKED_EXT")     \
+       EM(afs_file_error_symlink_big,          "SYM_BIG")              \
        EM(afs_file_error_mntpt,                "MNTPT_READ_FAILED")    \
        E_(afs_file_error_writeback_fail,       "WRITEBACK_FAILED")