]> exis.tech > repos - linux.git/blobdiff - mm/damon/core.c
Merge tag 'hwmon-for-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/groec...
[linux.git] / mm / damon / core.c
index 265d51ade25bf4090d7cd9faf6f3c5ac0f8f2a11..cff932b3317d0897b1db5e3d3b2aa644bd033cc6 100644 (file)
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Data Access Monitor
- *
- * Author: SeongJae Park <sj@kernel.org>
  */
 
 #define pr_fmt(fmt) "damon: " fmt
@@ -356,8 +354,21 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges,
 {
        struct damon_region *r, *next;
        unsigned int i;
+       unsigned long last_end;
        int err;
 
+       for (i = 0; i < nr_ranges; i++) {
+               unsigned long start, end;
+
+               start = ALIGN_DOWN(ranges[i].start, min_region_sz);
+               end = ALIGN(ranges[i].end, min_region_sz);
+               if (start >= end)
+                       return -EINVAL;
+               if (i > 0 && last_end > start)
+                       return -EINVAL;
+               last_end = end;
+       }
+
        /* Remove regions which are not in the new ranges */
        damon_for_each_region_safe(r, next, t) {
                for (i = 0; i < nr_ranges; i++) {
@@ -1387,10 +1398,36 @@ static int damon_commit_target(
        return 0;
 }
 
+/*
+ * damon_revert_target_commits() - revert unsuccessful target commits.
+ * @dst:       Commit destination context
+ * @failed:    Commit failed destination target
+ * @src:       Commit source context
+ *
+ * Revert target states that changed by damon_commit_target(), and cannot be
+ * cleaned up by the destination context's ops.cleanup_target().
+ */
+static void damon_revert_target_commits(struct damon_ctx *dst,
+               struct damon_target *failed, struct damon_ctx *src)
+{
+       struct damon_target *target;
+
+       if (!damon_target_has_pid(src))
+               return;
+       if (dst->ops.cleanup_target)
+               return;
+       damon_for_each_target(target, dst) {
+               if (target == failed)
+                       return;
+               put_pid(target->pid);
+       }
+}
+
 static int damon_commit_targets(
                struct damon_ctx *dst, struct damon_ctx *src)
 {
        struct damon_target *dst_target, *next, *src_target, *new_target;
+       struct damon_target *failed;
        int i = 0, j = 0, err;
 
        damon_for_each_target_safe(dst_target, next, dst) {
@@ -1404,8 +1441,10 @@ static int damon_commit_targets(
                                        dst_target, damon_target_has_pid(dst),
                                        src_target, damon_target_has_pid(src),
                                        src->min_region_sz);
-                       if (err)
-                               return err;
+                       if (err) {
+                               failed = dst_target;
+                               goto out;
+                       }
                } else {
                        struct damos *s;
 
@@ -1419,25 +1458,34 @@ static int damon_commit_targets(
                }
        }
 
+       failed = NULL;
        damon_for_each_target_safe(src_target, next, src) {
                if (j++ < i)
                        continue;
                /* target to remove has no matching dst */
-               if (src_target->obsolete)
-                       return -EINVAL;
+               if (src_target->obsolete) {
+                       err = -EINVAL;
+                       goto out;
+               }
                new_target = damon_new_target();
-               if (!new_target)
-                       return -ENOMEM;
+               if (!new_target) {
+                       err = -ENOMEM;
+                       goto out;
+               }
                err = damon_commit_target(new_target, false,
                                src_target, damon_target_has_pid(src),
                                src->min_region_sz);
                if (err) {
                        damon_destroy_target(new_target, NULL);
-                       return err;
+                       goto out;
                }
                damon_add_target(dst, new_target);
        }
        return 0;
+
+out:
+       damon_revert_target_commits(dst, failed, src);
+       return err;
 }
 
 static void damon_commit_filter(struct damon_filter *dst,
@@ -1571,8 +1619,10 @@ int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src)
         */
        if (!damon_attrs_equals(&dst->attrs, &src->attrs)) {
                err = damon_set_attrs(dst, &src->attrs);
-               if (err)
+               if (err) {
+                       damon_revert_target_commits(dst, NULL, src);
                        return err;
+               }
        }
        dst->pause = src->pause;
        dst->ops = src->ops;