linux/kernel/res_counter.c
<<
>>
Prefs
   1/*
   2 * resource cgroups
   3 *
   4 * Copyright 2007 OpenVZ SWsoft Inc
   5 *
   6 * Author: Pavel Emelianov <xemul@openvz.org>
   7 *
   8 */
   9
  10#include <linux/types.h>
  11#include <linux/parser.h>
  12#include <linux/fs.h>
  13#include <linux/slab.h>
  14#include <linux/res_counter.h>
  15#include <linux/uaccess.h>
  16#include <linux/mm.h>
  17
  18void res_counter_init(struct res_counter *counter, struct res_counter *parent)
  19{
  20        spin_lock_init(&counter->lock);
  21        counter->limit = RESOURCE_MAX;
  22        counter->soft_limit = RESOURCE_MAX;
  23        counter->parent = parent;
  24}
  25
  26int res_counter_charge_locked(struct res_counter *counter, unsigned long val)
  27{
  28        if (counter->usage + val > counter->limit) {
  29                counter->failcnt++;
  30                return -ENOMEM;
  31        }
  32
  33        counter->usage += val;
  34        if (counter->usage > counter->max_usage)
  35                counter->max_usage = counter->usage;
  36        return 0;
  37}
  38
  39int res_counter_charge(struct res_counter *counter, unsigned long val,
  40                        struct res_counter **limit_fail_at)
  41{
  42        int ret;
  43        unsigned long flags;
  44        struct res_counter *c, *u;
  45
  46        *limit_fail_at = NULL;
  47        local_irq_save(flags);
  48        for (c = counter; c != NULL; c = c->parent) {
  49                spin_lock(&c->lock);
  50                ret = res_counter_charge_locked(c, val);
  51                spin_unlock(&c->lock);
  52                if (ret < 0) {
  53                        *limit_fail_at = c;
  54                        goto undo;
  55                }
  56        }
  57        ret = 0;
  58        goto done;
  59undo:
  60        for (u = counter; u != c; u = u->parent) {
  61                spin_lock(&u->lock);
  62                res_counter_uncharge_locked(u, val);
  63                spin_unlock(&u->lock);
  64        }
  65done:
  66        local_irq_restore(flags);
  67        return ret;
  68}
  69
  70void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val)
  71{
  72        if (WARN_ON(counter->usage < val))
  73                val = counter->usage;
  74
  75        counter->usage -= val;
  76}
  77
  78void res_counter_uncharge(struct res_counter *counter, unsigned long val)
  79{
  80        unsigned long flags;
  81        struct res_counter *c;
  82
  83        local_irq_save(flags);
  84        for (c = counter; c != NULL; c = c->parent) {
  85                spin_lock(&c->lock);
  86                res_counter_uncharge_locked(c, val);
  87                spin_unlock(&c->lock);
  88        }
  89        local_irq_restore(flags);
  90}
  91
  92
  93static inline unsigned long long *
  94res_counter_member(struct res_counter *counter, int member)
  95{
  96        switch (member) {
  97        case RES_USAGE:
  98                return &counter->usage;
  99        case RES_MAX_USAGE:
 100                return &counter->max_usage;
 101        case RES_LIMIT:
 102                return &counter->limit;
 103        case RES_FAILCNT:
 104                return &counter->failcnt;
 105        case RES_SOFT_LIMIT:
 106                return &counter->soft_limit;
 107        };
 108
 109        BUG();
 110        return NULL;
 111}
 112
 113ssize_t res_counter_read(struct res_counter *counter, int member,
 114                const char __user *userbuf, size_t nbytes, loff_t *pos,
 115                int (*read_strategy)(unsigned long long val, char *st_buf))
 116{
 117        unsigned long long *val;
 118        char buf[64], *s;
 119
 120        s = buf;
 121        val = res_counter_member(counter, member);
 122        if (read_strategy)
 123                s += read_strategy(*val, s);
 124        else
 125                s += sprintf(s, "%llu\n", *val);
 126        return simple_read_from_buffer((void __user *)userbuf, nbytes,
 127                        pos, buf, s - buf);
 128}
 129
 130u64 res_counter_read_u64(struct res_counter *counter, int member)
 131{
 132        return *res_counter_member(counter, member);
 133}
 134
 135int res_counter_memparse_write_strategy(const char *buf,
 136                                        unsigned long long *res)
 137{
 138        char *end;
 139
 140        /* return RESOURCE_MAX(unlimited) if "-1" is specified */
 141        if (*buf == '-') {
 142                *res = simple_strtoull(buf + 1, &end, 10);
 143                if (*res != 1 || *end != '\0')
 144                        return -EINVAL;
 145                *res = RESOURCE_MAX;
 146                return 0;
 147        }
 148
 149        /* FIXME - make memparse() take const char* args */
 150        *res = memparse((char *)buf, &end);
 151        if (*end != '\0')
 152                return -EINVAL;
 153
 154        *res = PAGE_ALIGN(*res);
 155        return 0;
 156}
 157
 158int res_counter_write(struct res_counter *counter, int member,
 159                      const char *buf, write_strategy_fn write_strategy)
 160{
 161        char *end;
 162        unsigned long flags;
 163        unsigned long long tmp, *val;
 164
 165        if (write_strategy) {
 166                if (write_strategy(buf, &tmp))
 167                        return -EINVAL;
 168        } else {
 169                tmp = simple_strtoull(buf, &end, 10);
 170                if (*end != '\0')
 171                        return -EINVAL;
 172        }
 173        spin_lock_irqsave(&counter->lock, flags);
 174        val = res_counter_member(counter, member);
 175        *val = tmp;
 176        spin_unlock_irqrestore(&counter->lock, flags);
 177        return 0;
 178}
 179