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