f275c8eca772c4dfe6bcdda6b1253a65fefe3e83
[linux-2.6.git] / kernel / res_counter.c
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
18 void res_counter_init(struct res_counter *counter)
19 {
20         spin_lock_init(&counter->lock);
21         counter->limit = (unsigned long long)LLONG_MAX;
22 }
23
24 int res_counter_charge_locked(struct res_counter *counter, unsigned long val)
25 {
26         if (counter->usage + val > counter->limit) {
27                 counter->failcnt++;
28                 return -ENOMEM;
29         }
30
31         counter->usage += val;
32         if (counter->usage > counter->max_usage)
33                 counter->max_usage = counter->usage;
34         return 0;
35 }
36
37 int res_counter_charge(struct res_counter *counter, unsigned long val)
38 {
39         int ret;
40         unsigned long flags;
41
42         spin_lock_irqsave(&counter->lock, flags);
43         ret = res_counter_charge_locked(counter, val);
44         spin_unlock_irqrestore(&counter->lock, flags);
45         return ret;
46 }
47
48 void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val)
49 {
50         if (WARN_ON(counter->usage < val))
51                 val = counter->usage;
52
53         counter->usage -= val;
54 }
55
56 void res_counter_uncharge(struct res_counter *counter, unsigned long val)
57 {
58         unsigned long flags;
59
60         spin_lock_irqsave(&counter->lock, flags);
61         res_counter_uncharge_locked(counter, val);
62         spin_unlock_irqrestore(&counter->lock, flags);
63 }
64
65
66 static inline unsigned long long *
67 res_counter_member(struct res_counter *counter, int member)
68 {
69         switch (member) {
70         case RES_USAGE:
71                 return &counter->usage;
72         case RES_MAX_USAGE:
73                 return &counter->max_usage;
74         case RES_LIMIT:
75                 return &counter->limit;
76         case RES_FAILCNT:
77                 return &counter->failcnt;
78         };
79
80         BUG();
81         return NULL;
82 }
83
84 ssize_t res_counter_read(struct res_counter *counter, int member,
85                 const char __user *userbuf, size_t nbytes, loff_t *pos,
86                 int (*read_strategy)(unsigned long long val, char *st_buf))
87 {
88         unsigned long long *val;
89         char buf[64], *s;
90
91         s = buf;
92         val = res_counter_member(counter, member);
93         if (read_strategy)
94                 s += read_strategy(*val, s);
95         else
96                 s += sprintf(s, "%llu\n", *val);
97         return simple_read_from_buffer((void __user *)userbuf, nbytes,
98                         pos, buf, s - buf);
99 }
100
101 u64 res_counter_read_u64(struct res_counter *counter, int member)
102 {
103         return *res_counter_member(counter, member);
104 }
105
106 int res_counter_memparse_write_strategy(const char *buf,
107                                         unsigned long long *res)
108 {
109         char *end;
110         /* FIXME - make memparse() take const char* args */
111         *res = memparse((char *)buf, &end);
112         if (*end != '\0')
113                 return -EINVAL;
114
115         *res = PAGE_ALIGN(*res);
116         return 0;
117 }
118
119 int res_counter_write(struct res_counter *counter, int member,
120                       const char *buf, write_strategy_fn write_strategy)
121 {
122         char *end;
123         unsigned long flags;
124         unsigned long long tmp, *val;
125
126         if (write_strategy) {
127                 if (write_strategy(buf, &tmp))
128                         return -EINVAL;
129         } else {
130                 tmp = simple_strtoull(buf, &end, 10);
131                 if (*end != '\0')
132                         return -EINVAL;
133         }
134         spin_lock_irqsave(&counter->lock, flags);
135         val = res_counter_member(counter, member);
136         *val = tmp;
137         spin_unlock_irqrestore(&counter->lock, flags);
138         return 0;
139 }