xfs: run an eofblocks scan on ENOSPC/EDQUOT
[firefly-linux-kernel-4.4.55.git] / fs / xfs / xfs_icache.c
index 43452081b705286fd815e5b4300360c13a6a35d1..981b2cf519853f72c91dff609c90b78eb1626c4e 100644 (file)
@@ -33,6 +33,9 @@
 #include "xfs_trace.h"
 #include "xfs_icache.h"
 #include "xfs_bmap_util.h"
+#include "xfs_quota.h"
+#include "xfs_dquot_item.h"
+#include "xfs_dquot.h"
 
 #include <linux/kthread.h>
 #include <linux/freezer.h>
@@ -1300,6 +1303,55 @@ xfs_icache_free_eofblocks(
                                         eofb, XFS_ICI_EOFBLOCKS_TAG);
 }
 
+/*
+ * Run eofblocks scans on the quotas applicable to the inode. For inodes with
+ * multiple quotas, we don't know exactly which quota caused an allocation
+ * failure. We make a best effort by including each quota under low free space
+ * conditions (less than 1% free space) in the scan.
+ */
+int
+xfs_inode_free_quota_eofblocks(
+       struct xfs_inode *ip)
+{
+       int scan = 0;
+       struct xfs_eofblocks eofb = {0};
+       struct xfs_dquot *dq;
+
+       ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
+
+       /*
+        * Set the scan owner to avoid a potential livelock. Otherwise, the scan
+        * can repeatedly trylock on the inode we're currently processing. We
+        * run a sync scan to increase effectiveness and use the union filter to
+        * cover all applicable quotas in a single scan.
+        */
+       eofb.eof_scan_owner = ip->i_ino;
+       eofb.eof_flags = XFS_EOF_FLAGS_UNION|XFS_EOF_FLAGS_SYNC;
+
+       if (XFS_IS_UQUOTA_ENFORCED(ip->i_mount)) {
+               dq = xfs_inode_dquot(ip, XFS_DQ_USER);
+               if (dq && xfs_dquot_lowsp(dq)) {
+                       eofb.eof_uid = VFS_I(ip)->i_uid;
+                       eofb.eof_flags |= XFS_EOF_FLAGS_UID;
+                       scan = 1;
+               }
+       }
+
+       if (XFS_IS_GQUOTA_ENFORCED(ip->i_mount)) {
+               dq = xfs_inode_dquot(ip, XFS_DQ_GROUP);
+               if (dq && xfs_dquot_lowsp(dq)) {
+                       eofb.eof_gid = VFS_I(ip)->i_gid;
+                       eofb.eof_flags |= XFS_EOF_FLAGS_GID;
+                       scan = 1;
+               }
+       }
+
+       if (scan)
+               xfs_icache_free_eofblocks(ip->i_mount, &eofb);
+
+       return scan;
+}
+
 void
 xfs_inode_set_eofblocks_tag(
        xfs_inode_t     *ip)