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