diff options
Diffstat (limited to 'fs/gfs2')
-rw-r--r-- | fs/gfs2/quota.c | 82 |
1 files changed, 53 insertions, 29 deletions
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index ae02403407aa..283c6ff21911 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -316,11 +316,11 @@ static int qd_get(struct gfs2_sbd *sdp, struct kqid qid, } -static void qd_hold(struct gfs2_quota_data *qd) +static void __qd_hold(struct gfs2_quota_data *qd) { struct gfs2_sbd *sdp = qd->qd_sbd; - gfs2_assert(sdp, !__lockref_is_dead(&qd->qd_lockref)); - lockref_get(&qd->qd_lockref); + gfs2_assert(sdp, qd->qd_lockref.count > 0); + qd->qd_lockref.count++; } static void qd_put(struct gfs2_quota_data *qd) @@ -462,19 +462,27 @@ static void bh_put(struct gfs2_quota_data *qd) static bool qd_grab_sync(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd, u64 sync_gen) { + bool ret = false; + + spin_lock(&qd->qd_lockref.lock); if (test_bit(QDF_LOCKED, &qd->qd_flags) || !test_bit(QDF_CHANGE, &qd->qd_flags) || qd->qd_sync_gen >= sync_gen) - return false; + goto out; - if (!lockref_get_not_dead(&qd->qd_lockref)) - return false; + if (__lockref_is_dead(&qd->qd_lockref)) + goto out; + qd->qd_lockref.count++; list_move_tail(&qd->qd_list, &sdp->sd_quota_list); set_bit(QDF_LOCKED, &qd->qd_flags); qd->qd_change_sync = qd->qd_change; slot_hold(qd); - return true; + ret = true; + +out: + spin_unlock(&qd->qd_lockref.lock); + return ret; } static void qd_ungrab_sync(struct gfs2_quota_data *qd) @@ -493,8 +501,10 @@ static void qdsb_put(struct gfs2_quota_data *qd) static void qd_unlock(struct gfs2_quota_data *qd) { + spin_lock(&qd->qd_lockref.lock); gfs2_assert_warn(qd->qd_sbd, test_bit(QDF_LOCKED, &qd->qd_flags)); clear_bit(QDF_LOCKED, &qd->qd_flags); + spin_unlock(&qd->qd_lockref.lock); qdsb_put(qd); } @@ -663,6 +673,7 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change) struct gfs2_sbd *sdp = qd->qd_sbd; struct gfs2_inode *ip = GFS2_I(sdp->sd_qc_inode); struct gfs2_quota_change *qc = qd->qd_bh_qc; + bool needs_put = false; s64 x; mutex_lock(&sdp->sd_quota_mutex); @@ -674,26 +685,24 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change) * used, and we assume a value of 0 otherwise. */ + spin_lock(&qd->qd_lockref.lock); + x = 0; if (test_bit(QDF_CHANGE, &qd->qd_flags)) x = be64_to_cpu(qc->qc_change); x += change; - - spin_lock(&qd_lock); qd->qd_change += change; - spin_unlock(&qd_lock); if (!x && test_bit(QDF_CHANGE, &qd->qd_flags)) { /* The slot in the quota change file becomes unused. */ clear_bit(QDF_CHANGE, &qd->qd_flags); qc->qc_flags = 0; qc->qc_id = 0; - slot_put(qd); - qd_put(qd); + needs_put = true; } else if (x && !test_bit(QDF_CHANGE, &qd->qd_flags)) { /* The slot in the quota change file becomes used. */ set_bit(QDF_CHANGE, &qd->qd_flags); - qd_hold(qd); + __qd_hold(qd); slot_hold(qd); qc->qc_flags = 0; @@ -703,6 +712,12 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change) } qc->qc_change = cpu_to_be64(x); + spin_unlock(&qd->qd_lockref.lock); + + if (needs_put) { + slot_put(qd); + qd_put(qd); + } if (change < 0) /* Reset quiet flag if we freed some blocks */ clear_bit(QDF_QMSG_QUIET, &qd->qd_flags); mutex_unlock(&sdp->sd_quota_mutex); @@ -844,6 +859,7 @@ static int gfs2_adjust_quota(struct gfs2_sbd *sdp, loff_t loc, be64_add_cpu(&q.qu_value, change); if (((s64)be64_to_cpu(q.qu_value)) < 0) q.qu_value = 0; /* Never go negative on quota usage */ + spin_lock(&qd->qd_lockref.lock); qd->qd_qb.qb_value = q.qu_value; if (fdq) { if (fdq->d_fieldmask & QC_SPC_SOFT) { @@ -859,6 +875,7 @@ static int gfs2_adjust_quota(struct gfs2_sbd *sdp, loff_t loc, qd->qd_qb.qb_value = q.qu_value; } } + spin_unlock(&qd->qd_lockref.lock); err = gfs2_write_disk_quota(sdp, &q, loc); if (!err) { @@ -990,7 +1007,9 @@ static int update_qd(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd) qlvb->qb_limit = q.qu_limit; qlvb->qb_warn = q.qu_warn; qlvb->qb_value = q.qu_value; + spin_lock(&qd->qd_lockref.lock); qd->qd_qb = *qlvb; + spin_unlock(&qd->qd_lockref.lock); return 0; } @@ -1012,7 +1031,9 @@ restart: if (test_and_clear_bit(QDF_REFRESH, &qd->qd_flags)) force_refresh = FORCE; + spin_lock(&qd->qd_lockref.lock); qd->qd_qb = *(struct gfs2_quota_lvb *)qd->qd_gl->gl_lksb.sb_lvbptr; + spin_unlock(&qd->qd_lockref.lock); if (force_refresh || qd->qd_qb.qb_magic != cpu_to_be32(GFS2_MAGIC)) { gfs2_glock_dq_uninit(q_gh); @@ -1085,20 +1106,19 @@ static bool need_sync(struct gfs2_quota_data *qd) struct gfs2_tune *gt = &sdp->sd_tune; s64 value, change, limit; unsigned int num, den; + int ret = false; + spin_lock(&qd->qd_lockref.lock); if (!qd->qd_qb.qb_limit) - return false; + goto out; - spin_lock(&qd_lock); change = qd->qd_change; - spin_unlock(&qd_lock); - if (change <= 0) - return false; + goto out; value = (s64)be64_to_cpu(qd->qd_qb.qb_value); limit = (s64)be64_to_cpu(qd->qd_qb.qb_limit); if (value >= limit) - return false; + goto out; spin_lock(>->gt_spin); num = gt->gt_quota_scale_num; @@ -1108,8 +1128,12 @@ static bool need_sync(struct gfs2_quota_data *qd) change *= gfs2_jindex_size(sdp) * num; change = div_s64(change, den); if (value + change < limit) - return false; - return true; + goto out; + + ret = true; +out: + spin_unlock(&qd->qd_lockref.lock); + return ret; } void gfs2_quota_unlock(struct gfs2_inode *ip) @@ -1211,12 +1235,12 @@ int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid, qid_eq(qd->qd_id, make_kqid_gid(gid)))) continue; + spin_lock(&qd->qd_lockref.lock); warn = (s64)be64_to_cpu(qd->qd_qb.qb_warn); limit = (s64)be64_to_cpu(qd->qd_qb.qb_limit); value = (s64)be64_to_cpu(qd->qd_qb.qb_value); - spin_lock(&qd_lock); value += qd->qd_change; - spin_unlock(&qd_lock); + spin_unlock(&qd->qd_lockref.lock); if (limit > 0 && (limit - value) < ap->allowed) ap->allowed = limit - value; @@ -1282,12 +1306,12 @@ static bool qd_changed(struct gfs2_sbd *sdp) spin_lock(&qd_lock); list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { - if (test_bit(QDF_LOCKED, &qd->qd_flags) || - !test_bit(QDF_CHANGE, &qd->qd_flags)) - continue; - - changed = true; - break; + spin_lock(&qd->qd_lockref.lock); + changed = !test_bit(QDF_LOCKED, &qd->qd_flags) && + test_bit(QDF_CHANGE, &qd->qd_flags); + spin_unlock(&qd->qd_lockref.lock); + if (changed) + break; } spin_unlock(&qd_lock); return changed; |