Whamcloud - gitweb
LU-15340 llite: Delay dput in ll_dirty_page_discard_warn 84/45784/4
authorOleg Drokin <green@whamcloud.com>
Wed, 8 Dec 2021 04:30:06 +0000 (23:30 -0500)
committerOleg Drokin <green@whamcloud.com>
Mon, 31 Jan 2022 01:24:37 +0000 (01:24 +0000)
Otherwise we can be final dput and need to wait for pages
to clear which is bad because this is called from ptlrpcd
that is not supposed to block esp. for network traffic as
it can cause livelocks if it happens to be needed to kill
the very same RPC we are waiting on.

Additionally pass in the inode from IO since the page
we are using might come from directio and that is
probably not even a valid inode.

Fixes: 624a3ac23393 ("LU-921 llite: warning in case of discarding dirty pages")
Change-Id: Ie2f1a34047145202c11a4e1a0b18b2e01d9e4601
Signed-off-by: Oleg Drokin <green@whamcloud.com>
Reviewed-on: https://review.whamcloud.com/45784
Tested-by: jenkins <devops@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Patrick Farrell <pfarrell@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Shaun Tancheff <shaun.tancheff@hpe.com>
lustre/llite/llite_internal.h
lustre/llite/llite_lib.c
lustre/llite/vvp_page.c

index 9b7386c..e11927c 100644 (file)
@@ -1228,7 +1228,7 @@ int ll_flush_ctx(struct inode *inode);
 void ll_umount_begin(struct super_block *sb);
 int ll_remount_fs(struct super_block *sb, int *flags, char *data);
 int ll_show_options(struct seq_file *seq, struct dentry *dentry);
-void ll_dirty_page_discard_warn(struct page *page, int ioret);
+void ll_dirty_page_discard_warn(struct inode *inode, int ioret);
 int ll_prep_inode(struct inode **inode, struct req_capsule *pill,
                  struct super_block *sb, struct lookup_intent *it);
 int ll_obd_statfs(struct inode *inode, void __user *arg);
index 21bac82..88d193d 100644 (file)
@@ -3447,6 +3447,23 @@ int ll_get_obd_name(struct inode *inode, unsigned int cmd, unsigned long arg)
        RETURN(0);
 }
 
+struct dname_buf {
+       struct work_struct db_work;
+       struct dentry *db_dentry;
+       /* Let's hope the path is not too long, 32 bytes for the work struct
+        * on my kernel
+        */
+       char buf[PAGE_SIZE - sizeof(struct work_struct) - sizeof(void *)];
+};
+
+static void ll_dput_later(struct work_struct *work)
+{
+       struct dname_buf *db = container_of(work, struct dname_buf, db_work);
+
+       dput(db->db_dentry);
+       free_page((unsigned long)db);
+}
+
 static char* ll_d_path(struct dentry *dentry, char *buf, int bufsize)
 {
        char *path = NULL;
@@ -3461,33 +3478,43 @@ static char* ll_d_path(struct dentry *dentry, char *buf, int bufsize)
        return path;
 }
 
-void ll_dirty_page_discard_warn(struct page *page, int ioret)
+void ll_dirty_page_discard_warn(struct inode *inode, int ioret)
 {
-       char *buf, *path = NULL;
+       struct dname_buf *db;
+       char  *path = NULL;
        struct dentry *dentry = NULL;
-       struct inode *inode = page->mapping->host;
 
        /* this can be called inside spin lock so use GFP_ATOMIC. */
-       buf = (char *)__get_free_page(GFP_ATOMIC);
-       if (buf != NULL) {
-               dentry = d_find_alias(page->mapping->host);
+       db = (struct dname_buf *)__get_free_page(GFP_ATOMIC);
+       if (db != NULL) {
+
+               dentry = d_find_alias(inode);
                if (dentry != NULL)
-                       path = ll_d_path(dentry, buf, PAGE_SIZE);
+                       path = ll_d_path(dentry, db->buf, sizeof(db->buf));
        }
 
        /* The below message is checked in recovery-small.sh test_24b */
        CDEBUG(D_WARNING,
               "%s: dirty page discard: %s/fid: "DFID"/%s may get corrupted "
               "(rc %d)\n", ll_i2sbi(inode)->ll_fsname,
-              s2lsi(page->mapping->host->i_sb)->lsi_lmd->lmd_dev,
+              s2lsi(inode->i_sb)->lsi_lmd->lmd_dev,
               PFID(ll_inode2fid(inode)),
               (path && !IS_ERR(path)) ? path : "", ioret);
 
-       if (dentry != NULL)
-               dput(dentry);
-
-       if (buf != NULL)
-               free_page((unsigned long)buf);
+       if (dentry != NULL) {
+               /* We cannot dput here since if we happen to be the last holder
+                * then we can end up waiting for page evictions that
+                * in turn wait for RPCs that need this instance of ptlrpcd
+                * (callng brw_interpret->*page_completion*->vmpage_error->here)
+                * LU-15340
+                */
+               INIT_WORK(&db->db_work, ll_dput_later);
+               db->db_dentry = dentry;
+               schedule_work(&db->db_work);
+       } else {
+               if (db != NULL)
+                       free_page((unsigned long)db);
+       }
 }
 
 ssize_t ll_copy_user_md(const struct lov_user_md __user *md,
index c841fac..43cc618 100644 (file)
@@ -254,7 +254,7 @@ static void vvp_vmpage_error(struct inode *inode, struct page *vmpage,
                if ((ioret == -ESHUTDOWN || ioret == -EINTR ||
                     ioret == -EIO) && obj->vob_discard_page_warned == 0) {
                        obj->vob_discard_page_warned = 1;
-                       ll_dirty_page_discard_warn(vmpage, ioret);
+                       ll_dirty_page_discard_warn(inode, ioret);
                }
        }
 }