]> git.baikalelectronics.ru Git - kernel.git/commitdiff
netfs: do not unlock and put the folio twice
authorXiubo Li <xiubli@redhat.com>
Mon, 11 Jul 2022 04:11:21 +0000 (12:11 +0800)
committerIlya Dryomov <idryomov@gmail.com>
Thu, 14 Jul 2022 08:10:12 +0000 (10:10 +0200)
check_write_begin() will unlock and put the folio when return
non-zero.  So we should avoid unlocking and putting it twice in
netfs layer.

Change the way ->check_write_begin() works in the following two ways:

 (1) Pass it a pointer to the folio pointer, allowing it to unlock and put
     the folio prior to doing the stuff it wants to do, provided it clears
     the folio pointer.

 (2) Change the return values such that 0 with folio pointer set means
     continue, 0 with folio pointer cleared means re-get and all error
     codes indicating an error (no special treatment for -EAGAIN).

[ bagasdotme: use Sphinx code text syntax for *foliop pointer ]

Cc: stable@vger.kernel.org
Link: https://tracker.ceph.com/issues/56423
Link: https://lore.kernel.org/r/cf169f43-8ee7-8697-25da-0204d1b4343e@redhat.com
Co-developed-by: David Howells <dhowells@redhat.com>
Signed-off-by: Xiubo Li <xiubli@redhat.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Documentation/filesystems/netfs_library.rst
fs/afs/file.c
fs/ceph/addr.c
fs/netfs/buffered_read.c
include/linux/netfs.h

index 4d19b19bcc0867a834f81498f73727eac2030508..73a4176144b3b80aff4db87bd4ab3bd8e96c35c4 100644 (file)
@@ -301,7 +301,7 @@ through which it can issue requests and negotiate::
                void (*issue_read)(struct netfs_io_subrequest *subreq);
                bool (*is_still_valid)(struct netfs_io_request *rreq);
                int (*check_write_begin)(struct file *file, loff_t pos, unsigned len,
-                                        struct folio *folio, void **_fsdata);
+                                        struct folio **foliop, void **_fsdata);
                void (*done)(struct netfs_io_request *rreq);
        };
 
@@ -381,8 +381,10 @@ The operations are as follows:
    allocated/grabbed the folio to be modified to allow the filesystem to flush
    conflicting state before allowing it to be modified.
 
-   It should return 0 if everything is now fine, -EAGAIN if the folio should be
-   regrabbed and any other error code to abort the operation.
+   It may unlock and discard the folio it was given and set the caller's folio
+   pointer to NULL.  It should return 0 if everything is now fine (``*foliop``
+   left set) or the op should be retried (``*foliop`` cleared) and any other
+   error code to abort the operation.
 
  * ``done``
 
index 42118a4f338331433af660cd9464c43e1cc8b03b..d1cfb235c4b9b7be2226fd8129ea79326c933ef9 100644 (file)
@@ -375,7 +375,7 @@ static int afs_begin_cache_operation(struct netfs_io_request *rreq)
 }
 
 static int afs_check_write_begin(struct file *file, loff_t pos, unsigned len,
-                                struct folio *folio, void **_fsdata)
+                                struct folio **foliop, void **_fsdata)
 {
        struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
 
index 6dee88815491c869364ee223dcaed0a0f1ff7492..d6e5916138e4f3c3f89fe529933537a924fb6091 100644 (file)
@@ -63,7 +63,7 @@
         (CONGESTION_ON_THRESH(congestion_kb) >> 2))
 
 static int ceph_netfs_check_write_begin(struct file *file, loff_t pos, unsigned int len,
-                                       struct folio *folio, void **_fsdata);
+                                       struct folio **foliop, void **_fsdata);
 
 static inline struct ceph_snap_context *page_snap_context(struct page *page)
 {
@@ -1288,18 +1288,19 @@ ceph_find_incompatible(struct page *page)
 }
 
 static int ceph_netfs_check_write_begin(struct file *file, loff_t pos, unsigned int len,
-                                       struct folio *folio, void **_fsdata)
+                                       struct folio **foliop, void **_fsdata)
 {
        struct inode *inode = file_inode(file);
        struct ceph_inode_info *ci = ceph_inode(inode);
        struct ceph_snap_context *snapc;
 
-       snapc = ceph_find_incompatible(folio_page(folio, 0));
+       snapc = ceph_find_incompatible(folio_page(*foliop, 0));
        if (snapc) {
                int r;
 
-               folio_unlock(folio);
-               folio_put(folio);
+               folio_unlock(*foliop);
+               folio_put(*foliop);
+               *foliop = NULL;
                if (IS_ERR(snapc))
                        return PTR_ERR(snapc);
 
index 42f892c5712eae9daead74227a202ab8e5b4aa0e..0ce5358521510694f21b9dd1133081e182593be7 100644 (file)
@@ -319,8 +319,9 @@ zero_out:
  * conflicting writes once the folio is grabbed and locked.  It is passed a
  * pointer to the fsdata cookie that gets returned to the VM to be passed to
  * write_end.  It is permitted to sleep.  It should return 0 if the request
- * should go ahead; unlock the folio and return -EAGAIN to cause the folio to
- * be regot; or return an error.
+ * should go ahead or it may return an error.  It may also unlock and put the
+ * folio, provided it sets ``*foliop`` to NULL, in which case a return of 0
+ * will cause the folio to be re-got and the process to be retried.
  *
  * The calling netfs must initialise a netfs context contiguous to the vfs
  * inode before calling this.
@@ -348,13 +349,13 @@ retry:
 
        if (ctx->ops->check_write_begin) {
                /* Allow the netfs (eg. ceph) to flush conflicts. */
-               ret = ctx->ops->check_write_begin(file, pos, len, folio, _fsdata);
+               ret = ctx->ops->check_write_begin(file, pos, len, &folio, _fsdata);
                if (ret < 0) {
                        trace_netfs_failure(NULL, NULL, ret, netfs_fail_check_write_begin);
-                       if (ret == -EAGAIN)
-                               goto retry;
                        goto error;
                }
+               if (!folio)
+                       goto retry;
        }
 
        if (folio_test_uptodate(folio))
@@ -416,8 +417,10 @@ have_folio_no_wait:
 error_put:
        netfs_put_request(rreq, false, netfs_rreq_trace_put_failed);
 error:
-       folio_unlock(folio);
-       folio_put(folio);
+       if (folio) {
+               folio_unlock(folio);
+               folio_put(folio);
+       }
        _leave(" = %d", ret);
        return ret;
 }
index 1773e5df8e65ba3c3e1cd563fc4f3e311ecacaf6..1b18dfa52e48e2c0d733bccc89cd91a13c01c43e 100644 (file)
@@ -214,7 +214,7 @@ struct netfs_request_ops {
        void (*issue_read)(struct netfs_io_subrequest *subreq);
        bool (*is_still_valid)(struct netfs_io_request *rreq);
        int (*check_write_begin)(struct file *file, loff_t pos, unsigned len,
-                                struct folio *folio, void **_fsdata);
+                                struct folio **foliop, void **_fsdata);
        void (*done)(struct netfs_io_request *rreq);
 };