]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - kernel/res_counter.c
[media] wm8775: Revert changeset fcb9757333 to avoid a regression
[linux-2.6.git] / kernel / res_counter.c
index f275c8eca772c4dfe6bcdda6b1253a65fefe3e83..c7eaa37a768b785cf69cd2eb64ab3fb75c052124 100644 (file)
 #include <linux/types.h>
 #include <linux/parser.h>
 #include <linux/fs.h>
-#include <linux/slab.h>
 #include <linux/res_counter.h>
 #include <linux/uaccess.h>
 #include <linux/mm.h>
 
-void res_counter_init(struct res_counter *counter)
+void res_counter_init(struct res_counter *counter, struct res_counter *parent)
 {
        spin_lock_init(&counter->lock);
-       counter->limit = (unsigned long long)LLONG_MAX;
+       counter->limit = RESOURCE_MAX;
+       counter->soft_limit = RESOURCE_MAX;
+       counter->parent = parent;
 }
 
 int res_counter_charge_locked(struct res_counter *counter, unsigned long val)
@@ -34,14 +35,34 @@ int res_counter_charge_locked(struct res_counter *counter, unsigned long val)
        return 0;
 }
 
-int res_counter_charge(struct res_counter *counter, unsigned long val)
+int res_counter_charge(struct res_counter *counter, unsigned long val,
+                       struct res_counter **limit_fail_at)
 {
        int ret;
        unsigned long flags;
-
-       spin_lock_irqsave(&counter->lock, flags);
-       ret = res_counter_charge_locked(counter, val);
-       spin_unlock_irqrestore(&counter->lock, flags);
+       struct res_counter *c, *u;
+
+       *limit_fail_at = NULL;
+       local_irq_save(flags);
+       for (c = counter; c != NULL; c = c->parent) {
+               spin_lock(&c->lock);
+               ret = res_counter_charge_locked(c, val);
+               spin_unlock(&c->lock);
+               if (ret < 0) {
+                       *limit_fail_at = c;
+                       goto undo;
+               }
+       }
+       ret = 0;
+       goto done;
+undo:
+       for (u = counter; u != c; u = u->parent) {
+               spin_lock(&u->lock);
+               res_counter_uncharge_locked(u, val);
+               spin_unlock(&u->lock);
+       }
+done:
+       local_irq_restore(flags);
        return ret;
 }
 
@@ -56,10 +77,15 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val)
 void res_counter_uncharge(struct res_counter *counter, unsigned long val)
 {
        unsigned long flags;
+       struct res_counter *c;
 
-       spin_lock_irqsave(&counter->lock, flags);
-       res_counter_uncharge_locked(counter, val);
-       spin_unlock_irqrestore(&counter->lock, flags);
+       local_irq_save(flags);
+       for (c = counter; c != NULL; c = c->parent) {
+               spin_lock(&c->lock);
+               res_counter_uncharge_locked(c, val);
+               spin_unlock(&c->lock);
+       }
+       local_irq_restore(flags);
 }
 
 
@@ -75,6 +101,8 @@ res_counter_member(struct res_counter *counter, int member)
                return &counter->limit;
        case RES_FAILCNT:
                return &counter->failcnt;
+       case RES_SOFT_LIMIT:
+               return &counter->soft_limit;
        };
 
        BUG();
@@ -107,6 +135,16 @@ int res_counter_memparse_write_strategy(const char *buf,
                                        unsigned long long *res)
 {
        char *end;
+
+       /* return RESOURCE_MAX(unlimited) if "-1" is specified */
+       if (*buf == '-') {
+               *res = simple_strtoull(buf + 1, &end, 10);
+               if (*res != 1 || *end != '\0')
+                       return -EINVAL;
+               *res = RESOURCE_MAX;
+               return 0;
+       }
+
        /* FIXME - make memparse() take const char* args */
        *res = memparse((char *)buf, &end);
        if (*end != '\0')