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/res_counter.h>
  14#include <linux/uaccess.h>
  15#include <linux/mm.h>
  16
  17void res_counter_init(struct res_counter *counter, struct res_counter *parent)
  18{
  19        spin_lock_init(&counter->lock);
  20        counter->limit = RESOURCE_MAX;
  21        counter->soft_limit = RESOURCE_MAX;
  22        counter->parent = parent;
  23}
  24
  25int res_counter_charge_locked(struct res_counter *counter, unsigned long val,
  26                              bool force)
  27{
  28        int ret = 0;
  29
  30        if (counter->usage + val > counter->limit) {
  31                counter->failcnt++;
  32                ret = -ENOMEM;
  33                if (!force)
  34                        return ret;
  35        }
  36
  37        counter->usage += val;
  38        if (counter->usage > counter->max_usage)
  39                counter->max_usage = counter->usage;
  40        return ret;
  41}
  42
  43static int __res_counter_charge(struct res_counter *counter, unsigned long val,
  44                                struct res_counter **limit_fail_at, bool force)
  45{
  46        int ret, r;
  47        unsigned long flags;
  48        struct res_counter *c, *u;
  49
  50        r = ret = 0;
  51        *limit_fail_at = NULL;
  52        local_irq_save(flags);
  53        for (c = counter; c != NULL; c = c->parent) {
  54                spin_lock(&c->lock);
  55                r = res_counter_charge_locked(c, val, force);
  56                spin_unlock(&c->lock);
  57                if (r < 0 && !ret) {
  58                        ret = r;
  59                        *limit_fail_at = c;
  60                        if (!force)
  61                                break;
  62                }
  63        }
  64
  65        if (ret < 0 && !force) {
  66                for (u = counter; u != c; u = u->parent) {
  67                        spin_lock(&u->lock);
  68                        res_counter_uncharge_locked(u, val);
  69                        spin_unlock(&u->lock);
  70                }
  71        }
  72        local_irq_restore(flags);
  73
  74        return ret;
  75}
  76
  77int res_counter_charge(struct res_counter *counter, unsigned long val,
  78                        struct res_counter **limit_fail_at)
  79{
  80        return __res_counter_charge(counter, val, limit_fail_at, false);
  81}
  82
  83int res_counter_charge_nofail(struct res_counter *counter, unsigned long val,
  84                              struct res_counter **limit_fail_at)
  85{
  86        return __res_counter_charge(counter, val, limit_fail_at, true);
  87}
  88
  89void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val)
  90{
  91        if (WARN_ON(counter->usage < val))
  92                val = counter->usage;
  93
  94        counter->usage -= val;
  95}
  96
  97void res_counter_uncharge_until(struct res_counter *counter,
  98                                struct res_counter *top,
  99                                unsigned long val)
 100{
 101        unsigned long flags;
 102        struct res_counter *c;
 103
 104        local_irq_save(flags);
 105        for (c = counter; c != top; c = c->parent) {
 106                spin_lock(&c->lock);
 107                res_counter_uncharge_locked(c, val);
 108                spin_unlock(&c->lock);
 109        }
 110        local_irq_restore(flags);
 111}
 112
 113void res_counter_uncharge(struct res_counter *counter, unsigned long val)
 114{
 115        res_counter_uncharge_until(counter, NULL, val);
 116}
 117
 118static inline unsigned long long *
 119res_counter_member(struct res_counter *counter, int member)
 120{
 121        switch (member) {
 122        case RES_USAGE:
 123                return &counter->usage;
 124        case RES_MAX_USAGE:
 125                return &counter->max_usage;
 126        case RES_LIMIT:
 127                return &counter->limit;
 128        case RES_FAILCNT:
 129                return &counter->failcnt;
 130        case RES_SOFT_LIMIT:
 131                return &counter->soft_limit;
 132        };
 133
 134        BUG();
 135        return NULL;
 136}
 137
 138ssize_t res_counter_read(struct res_counter *counter, int member,
 139                const char __user *userbuf, size_t nbytes, loff_t *pos,
 140                int (*read_strategy)(unsigned long long val, char *st_buf))
 141{
 142        unsigned long long *val;
 143        char buf[64], *s;
 144
 145        s = buf;
 146        val = res_counter_member(counter, member);
 147        if (read_strategy)
 148                s += read_strategy(*val, s);
 149        else
 150                s += sprintf(s, "%llu\n", *val);
 151        return simple_read_from_buffer((void __user *)userbuf, nbytes,
 152                        pos, buf, s - buf);
 153}
 154
 155#if BITS_PER_LONG == 32
 156u64 res_counter_read_u64(struct res_counter *counter, int member)
 157{
 158        unsigned long flags;
 159        u64 ret;
 160
 161        spin_lock_irqsave(&counter->lock, flags);
 162        ret = *res_counter_member(counter, member);
 163        spin_unlock_irqrestore(&counter->lock, flags);
 164
 165        return ret;
 166}
 167#else
 168u64 res_counter_read_u64(struct res_counter *counter, int member)
 169{
 170        return *res_counter_member(counter, member);
 171}
 172#endif
 173
 174int res_counter_memparse_write_strategy(const char *buf,
 175                                        unsigned long long *res)
 176{
 177        char *end;
 178
 179        /* return RESOURCE_MAX(unlimited) if "-1" is specified */
 180        if (*buf == '-') {
 181                *res = simple_strtoull(buf + 1, &end, 10);
 182                if (*res != 1 || *end != '\0')
 183                        return -EINVAL;
 184                *res = RESOURCE_MAX;
 185                return 0;
 186        }
 187
 188        *res = memparse(buf, &end);
 189        if (*end != '\0')
 190                return -EINVAL;
 191
 192        *res = PAGE_ALIGN(*res);
 193        return 0;
 194}
 195
 196int res_counter_write(struct res_counter *counter, int member,
 197                      const char *buf, write_strategy_fn write_strategy)
 198{
 199        char *end;
 200        unsigned long flags;
 201        unsigned long long tmp, *val;
 202
 203        if (write_strategy) {
 204                if (write_strategy(buf, &tmp))
 205                        return -EINVAL;
 206        } else {
 207                tmp = simple_strtoull(buf, &end, 10);
 208                if (*end != '\0')
 209                        return -EINVAL;
 210        }
 211        spin_lock_irqsave(&counter->lock, flags);
 212        val = res_counter_member(counter, member);
 213        *val = tmp;
 214        spin_unlock_irqrestore(&counter->lock, flags);
 215        return 0;
 216}
 217
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.