Merge git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[firefly-linux-kernel-4.4.55.git] / fs / gfs2 / quota.c
index 3aa17d4d1cfcbf341e6115a8029c0e82f3e1a2cd..5c27e48aa76f57e1e070cfc89c0c99647fbea456 100644 (file)
@@ -923,6 +923,9 @@ restart:
        if (error)
                return error;
 
+       if (test_and_clear_bit(QDF_REFRESH, &qd->qd_flags))
+               force_refresh = FORCE;
+
        qd->qd_qb = *(struct gfs2_quota_lvb *)qd->qd_gl->gl_lksb.sb_lvbptr;
 
        if (force_refresh || qd->qd_qb.qb_magic != cpu_to_be32(GFS2_MAGIC)) {
@@ -974,11 +977,8 @@ int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid)
             sizeof(struct gfs2_quota_data *), sort_qd, NULL);
 
        for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) {
-               int force = NO_FORCE;
                qd = ip->i_res->rs_qa_qd[x];
-               if (test_and_clear_bit(QDF_REFRESH, &qd->qd_flags))
-                       force = FORCE;
-               error = do_glock(qd, force, &ip->i_res->rs_qa_qd_ghs[x]);
+               error = do_glock(qd, NO_FORCE, &ip->i_res->rs_qa_qd_ghs[x]);
                if (error)
                        break;
        }
@@ -1094,14 +1094,33 @@ static int print_message(struct gfs2_quota_data *qd, char *type)
        return 0;
 }
 
-int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid)
+/**
+ * gfs2_quota_check - check if allocating new blocks will exceed quota
+ * @ip:  The inode for which this check is being performed
+ * @uid: The uid to check against
+ * @gid: The gid to check against
+ * @ap:  The allocation parameters. ap->target contains the requested
+ *       blocks. ap->min_target, if set, contains the minimum blks
+ *       requested.
+ *
+ * Returns: 0 on success.
+ *                  min_req = ap->min_target ? ap->min_target : ap->target;
+ *                  quota must allow atleast min_req blks for success and
+ *                  ap->allowed is set to the number of blocks allowed
+ *
+ *          -EDQUOT otherwise, quota violation. ap->allowed is set to number
+ *                  of blocks available.
+ */
+int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid,
+                    struct gfs2_alloc_parms *ap)
 {
        struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
        struct gfs2_quota_data *qd;
-       s64 value;
+       s64 value, warn, limit;
        unsigned int x;
        int error = 0;
 
+       ap->allowed = UINT_MAX; /* Assume we are permitted a whole lot */
        if (!test_bit(GIF_QD_LOCKED, &ip->i_flags))
                return 0;
 
@@ -1115,30 +1134,37 @@ int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid)
                      qid_eq(qd->qd_id, make_kqid_gid(gid))))
                        continue;
 
+               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);
 
-               if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) {
-                       print_message(qd, "exceeded");
-                       quota_send_warning(qd->qd_id,
-                                          sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN);
-
-                       error = -EDQUOT;
-                       break;
-               } else if (be64_to_cpu(qd->qd_qb.qb_warn) &&
-                          (s64)be64_to_cpu(qd->qd_qb.qb_warn) < value &&
+               if (limit > 0 && (limit - value) < ap->allowed)
+                       ap->allowed = limit - value;
+               /* If we can't meet the target */
+               if (limit && limit < (value + (s64)ap->target)) {
+                       /* If no min_target specified or we don't meet
+                        * min_target, return -EDQUOT */
+                       if (!ap->min_target || ap->min_target > ap->allowed) {
+                               print_message(qd, "exceeded");
+                               quota_send_warning(qd->qd_id,
+                                                  sdp->sd_vfs->s_dev,
+                                                  QUOTA_NL_BHARDWARN);
+                               error = -EDQUOT;
+                               break;
+                       }
+               } else if (warn && warn < value &&
                           time_after_eq(jiffies, qd->qd_last_warn +
-                                        gfs2_tune_get(sdp,
-                                               gt_quota_warn_period) * HZ)) {
+                                        gfs2_tune_get(sdp, gt_quota_warn_period)
+                                        * HZ)) {
                        quota_send_warning(qd->qd_id,
                                           sdp->sd_vfs->s_dev, QUOTA_NL_BSOFTWARN);
                        error = print_message(qd, "warning");
                        qd->qd_last_warn = jiffies;
                }
        }
-
        return error;
 }