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