|
@@ -15,10 +15,11 @@
|
|
|
#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->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);
|
|
|
}
|
|
|
|
|
|
|