|
@@ -10,6 +10,7 @@
|
|
|
#include <linux/jiffies.h>
|
|
|
#include <linux/writeback.h>
|
|
|
#include <linux/workqueue.h>
|
|
|
+#include <linux/llist.h>
|
|
|
|
|
|
#include <cluster/masklog.h>
|
|
|
|
|
@@ -679,6 +680,27 @@ static int ocfs2_calc_qdel_credits(struct super_block *sb, int type)
|
|
|
OCFS2_INODE_UPDATE_CREDITS;
|
|
|
}
|
|
|
|
|
|
+void ocfs2_drop_dquot_refs(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct ocfs2_super *osb = container_of(work, struct ocfs2_super,
|
|
|
+ dquot_drop_work);
|
|
|
+ struct llist_node *list;
|
|
|
+ struct ocfs2_dquot *odquot, *next_odquot;
|
|
|
+
|
|
|
+ list = llist_del_all(&osb->dquot_drop_list);
|
|
|
+ llist_for_each_entry_safe(odquot, next_odquot, list, list) {
|
|
|
+ /* Drop the reference we acquired in ocfs2_dquot_release() */
|
|
|
+ dqput(&odquot->dq_dquot);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Called when the last reference to dquot is dropped. If we are called from
|
|
|
+ * downconvert thread, we cannot do all the handling here because grabbing
|
|
|
+ * quota lock could deadlock (the node holding the quota lock could need some
|
|
|
+ * other cluster lock to proceed but with blocked downconvert thread we cannot
|
|
|
+ * release any lock).
|
|
|
+ */
|
|
|
static int ocfs2_release_dquot(struct dquot *dquot)
|
|
|
{
|
|
|
handle_t *handle;
|
|
@@ -694,6 +716,19 @@ static int ocfs2_release_dquot(struct dquot *dquot)
|
|
|
/* Check whether we are not racing with some other dqget() */
|
|
|
if (atomic_read(&dquot->dq_count) > 1)
|
|
|
goto out;
|
|
|
+ /* Running from downconvert thread? Postpone quota processing to wq */
|
|
|
+ if (current == osb->dc_task) {
|
|
|
+ /*
|
|
|
+ * Grab our own reference to dquot and queue it for delayed
|
|
|
+ * dropping. Quota code rechecks after calling
|
|
|
+ * ->release_dquot() and won't free dquot structure.
|
|
|
+ */
|
|
|
+ dqgrab(dquot);
|
|
|
+ /* First entry on list -> queue work */
|
|
|
+ if (llist_add(&OCFS2_DQUOT(dquot)->list, &osb->dquot_drop_list))
|
|
|
+ queue_work(ocfs2_wq, &osb->dquot_drop_work);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
status = ocfs2_lock_global_qf(oinfo, 1);
|
|
|
if (status < 0)
|
|
|
goto out;
|