vserver 1.9.5.x5
[linux-2.6.git] / kernel / vserver / dlimit.c
1 /*
2  *  linux/kernel/vserver/dlimit.c
3  *
4  *  Virtual Server: Context Disk Limits
5  *
6  *  Copyright (C) 2004-2005  Herbert Pƶtzl
7  *
8  *  V0.01  initial version
9  *
10  */
11
12 #include <linux/config.h>
13 #include <linux/fs.h>
14 #include <linux/namespace.h>
15 #include <linux/namei.h>
16 #include <linux/statfs.h>
17 #include <linux/vserver/switch.h>
18 #include <linux/vs_context.h>
19 #include <linux/vs_dlimit.h>
20
21 #include <asm/errno.h>
22 #include <asm/uaccess.h>
23
24 /*      __alloc_dl_info()
25
26         * allocate an initialized dl_info struct
27         * doesn't make it visible (hash)                        */
28
29 static struct dl_info *__alloc_dl_info(struct super_block *sb, xid_t xid)
30 {
31         struct dl_info *new = NULL;
32
33         vxdprintk(VXD_CBIT(dlim, 5),
34                 "alloc_dl_info(%p,%d)*", sb, xid);
35
36         /* would this benefit from a slab cache? */
37         new = kmalloc(sizeof(struct dl_info), GFP_KERNEL);
38         if (!new)
39                 return 0;
40
41         memset (new, 0, sizeof(struct dl_info));
42         new->dl_xid = xid;
43         new->dl_sb = sb;
44         INIT_RCU_HEAD(&new->dl_rcu);
45         INIT_HLIST_NODE(&new->dl_hlist);
46         spin_lock_init(&new->dl_lock);
47         atomic_set(&new->dl_refcnt, 0);
48         atomic_set(&new->dl_usecnt, 0);
49
50         /* rest of init goes here */
51
52         vxdprintk(VXD_CBIT(dlim, 4),
53                 "alloc_dl_info(%p,%d) = %p", sb, xid, new);
54         return new;
55 }
56
57 /*      __dealloc_dl_info()
58
59         * final disposal of dl_info                             */
60
61 static void __dealloc_dl_info(struct dl_info *dli)
62 {
63         vxdprintk(VXD_CBIT(dlim, 4),
64                 "dealloc_dl_info(%p)", dli);
65
66         dli->dl_hlist.next = LIST_POISON1;
67         dli->dl_xid = -1;
68         dli->dl_sb = 0;
69
70         BUG_ON(atomic_read(&dli->dl_usecnt));
71         BUG_ON(atomic_read(&dli->dl_refcnt));
72
73         kfree(dli);
74 }
75
76
77 /*      hash table for dl_info hash */
78
79 #define DL_HASH_SIZE    13
80
81 struct hlist_head dl_info_hash[DL_HASH_SIZE];
82
83 static spinlock_t dl_info_hash_lock = SPIN_LOCK_UNLOCKED;
84
85
86 static inline unsigned int __hashval(struct super_block *sb, xid_t xid)
87 {
88         return ((xid ^ (unsigned long)sb) % DL_HASH_SIZE);
89 }
90
91
92
93 /*      __hash_dl_info()
94
95         * add the dli to the global hash table
96         * requires the hash_lock to be held                     */
97
98 static inline void __hash_dl_info(struct dl_info *dli)
99 {
100         struct hlist_head *head;
101
102         vxdprintk(VXD_CBIT(dlim, 6),
103                 "__hash_dl_info: %p[#%d]", dli, dli->dl_xid);
104         get_dl_info(dli);
105         head = &dl_info_hash[__hashval(dli->dl_sb, dli->dl_xid)];
106         hlist_add_head_rcu(&dli->dl_hlist, head);
107 }
108
109 /*      __unhash_dl_info()
110
111         * remove the dli from the global hash table
112         * requires the hash_lock to be held                     */
113
114 static inline void __unhash_dl_info(struct dl_info *dli)
115 {
116         vxdprintk(VXD_CBIT(dlim, 6),
117                 "__unhash_dl_info: %p[#%d]", dli, dli->dl_xid);
118         hlist_del_rcu(&dli->dl_hlist);
119         put_dl_info(dli);
120 }
121
122
123 /*      __lookup_dl_info()
124
125         * requires the rcu_read_lock()
126         * doesn't increment the dl_refcnt                       */
127
128 static inline struct dl_info *__lookup_dl_info(struct super_block *sb, xid_t xid)
129 {
130         struct hlist_head *head = &dl_info_hash[__hashval(sb, xid)];
131         struct hlist_node *pos;
132
133         hlist_for_each_rcu(pos, head) {
134                 struct dl_info *dli =
135                         hlist_entry(pos, struct dl_info, dl_hlist);
136
137                 if (dli->dl_xid == xid && dli->dl_sb == sb) {
138                         return dli;
139                 }
140         }
141         return NULL;
142 }
143
144
145 struct dl_info *locate_dl_info(struct super_block *sb, xid_t xid)
146 {
147         struct dl_info *dli;
148
149         rcu_read_lock();
150         dli = get_dl_info(__lookup_dl_info(sb, xid));
151         vxdprintk(VXD_CBIT(dlim, 7),
152                 "locate_dl_info(%p,#%d) = %p", sb, xid, dli);
153         rcu_read_unlock();
154         return dli;
155 }
156
157 void rcu_free_dl_info(struct rcu_head *head)
158 {
159         struct dl_info *dli = container_of(head, struct dl_info, dl_rcu);
160         int usecnt, refcnt;
161
162         BUG_ON(!dli || !head);
163
164         usecnt = atomic_read(&dli->dl_usecnt);
165         BUG_ON(usecnt < 0);
166
167         refcnt = atomic_read(&dli->dl_refcnt);
168         BUG_ON(refcnt < 0);
169
170         vxdprintk(VXD_CBIT(dlim, 3),
171                 "rcu_free_dl_info(%p)", dli);
172         if (!usecnt)
173                 __dealloc_dl_info(dli);
174         else
175                 printk("!!! rcu didn't free\n");
176 }
177
178
179
180
181 int vc_add_dlimit(uint32_t id, void __user *data)
182 {
183         struct nameidata nd;
184         struct vcmd_ctx_dlimit_base_v0 vc_data;
185         int ret;
186
187         if (!vx_check(0, VX_ADMIN))
188                 return -ENOSYS;
189         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
190                 return -EFAULT;
191
192         ret = user_path_walk_link(vc_data.name, &nd);
193         if (!ret) {
194                 struct super_block *sb;
195                 struct dl_info *dli;
196
197                 ret = -EINVAL;
198                 if (!nd.dentry->d_inode)
199                         goto out_release;
200                 if (!(sb = nd.dentry->d_inode->i_sb))
201                         goto out_release;
202
203                 dli = __alloc_dl_info(sb, id);
204                 spin_lock(&dl_info_hash_lock);
205
206                 ret = -EEXIST;
207                 if (__lookup_dl_info(sb, id))
208                         goto out_unlock;
209                 __hash_dl_info(dli);
210                 dli = NULL;
211                 ret = 0;
212
213         out_unlock:
214                 spin_unlock(&dl_info_hash_lock);
215                 if (dli)
216                         __dealloc_dl_info(dli);
217         out_release:
218                 path_release(&nd);
219         }
220         return ret;
221 }
222
223
224 int vc_rem_dlimit(uint32_t id, void __user *data)
225 {
226         struct nameidata nd;
227         struct vcmd_ctx_dlimit_base_v0 vc_data;
228         int ret;
229
230         if (!vx_check(0, VX_ADMIN))
231                 return -ENOSYS;
232         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
233                 return -EFAULT;
234
235         ret = user_path_walk_link(vc_data.name, &nd);
236         if (!ret) {
237                 struct super_block *sb;
238                 struct dl_info *dli;
239
240                 ret = -EINVAL;
241                 if (!nd.dentry->d_inode)
242                         goto out_release;
243                 if (!(sb = nd.dentry->d_inode->i_sb))
244                         goto out_release;
245
246                 spin_lock(&dl_info_hash_lock);
247                 dli = __lookup_dl_info(sb, id);
248
249                 ret = -ESRCH;
250                 if (!dli)
251                         goto out_unlock;
252
253                 __unhash_dl_info(dli);
254                 ret = 0;
255
256         out_unlock:
257                 spin_unlock(&dl_info_hash_lock);
258         out_release:
259                 path_release(&nd);
260         }
261         return ret;
262 }
263
264
265 int vc_set_dlimit(uint32_t id, void __user *data)
266 {
267         struct nameidata nd;
268         struct vcmd_ctx_dlimit_v0 vc_data;
269         int ret;
270
271         if (!vx_check(0, VX_ADMIN))
272                 return -ENOSYS;
273         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
274                 return -EFAULT;
275
276         ret = user_path_walk_link(vc_data.name, &nd);
277         if (!ret) {
278                 struct super_block *sb;
279                 struct dl_info *dli;
280
281                 ret = -EINVAL;
282                 if (!nd.dentry->d_inode)
283                         goto out_release;
284                 if (!(sb = nd.dentry->d_inode->i_sb))
285                         goto out_release;
286                 if ((vc_data.reserved != (uint32_t)CDLIM_KEEP &&
287                         vc_data.reserved > 100) ||
288                         (vc_data.inodes_used != (uint32_t)CDLIM_KEEP &&
289                         vc_data.inodes_used > vc_data.inodes_total) ||
290                         (vc_data.space_used != (uint32_t)CDLIM_KEEP &&
291                         vc_data.space_used > vc_data.space_total))
292                         goto out_release;
293
294                 ret = -ESRCH;
295                 dli = locate_dl_info(sb, id);
296                 if (!dli)
297                         goto out_release;
298
299                 spin_lock(&dli->dl_lock);
300
301                 if (vc_data.inodes_used != (uint32_t)CDLIM_KEEP)
302                         dli->dl_inodes_used = vc_data.inodes_used;
303                 if (vc_data.inodes_total != (uint32_t)CDLIM_KEEP)
304                         dli->dl_inodes_total = vc_data.inodes_total;
305                 if (vc_data.space_used != (uint32_t)CDLIM_KEEP) {
306                         dli->dl_space_used = vc_data.space_used;
307                         dli->dl_space_used <<= 10;
308                 }
309                 if (vc_data.space_total == (uint32_t)CDLIM_INFINITY)
310                         dli->dl_space_total = (uint64_t)CDLIM_INFINITY;
311                 else if (vc_data.space_total != (uint32_t)CDLIM_KEEP) {
312                         dli->dl_space_total = vc_data.space_total;
313                         dli->dl_space_total <<= 10;
314                 }
315                 if (vc_data.reserved != (uint32_t)CDLIM_KEEP)
316                         dli->dl_nrlmult = (1 << 10) * (100 - vc_data.reserved) / 100;
317
318                 spin_unlock(&dli->dl_lock);
319
320                 put_dl_info(dli);
321                 ret = 0;
322
323         out_release:
324                 path_release(&nd);
325         }
326         return ret;
327 }
328
329 int vc_get_dlimit(uint32_t id, void __user *data)
330 {
331         struct nameidata nd;
332         struct vcmd_ctx_dlimit_v0 vc_data;
333         int ret;
334
335         if (!vx_check(0, VX_ADMIN))
336                 return -ENOSYS;
337         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
338                 return -EFAULT;
339
340         ret = user_path_walk_link(vc_data.name, &nd);
341         if (!ret) {
342                 struct super_block *sb;
343                 struct dl_info *dli;
344
345                 ret = -EINVAL;
346                 if (!nd.dentry->d_inode)
347                         goto out_release;
348                 if (!(sb = nd.dentry->d_inode->i_sb))
349                         goto out_release;
350                 if (vc_data.reserved > 100 ||
351                         vc_data.inodes_used > vc_data.inodes_total ||
352                         vc_data.space_used > vc_data.space_total)
353                         goto out_release;
354
355                 ret = -ESRCH;
356                 dli = locate_dl_info(sb, id);
357                 if (!dli)
358                         goto out_release;
359
360                 spin_lock(&dli->dl_lock);
361                 vc_data.inodes_used = dli->dl_inodes_used;
362                 vc_data.inodes_total = dli->dl_inodes_total;
363                 vc_data.space_used = dli->dl_space_used >> 10;
364                 if (dli->dl_space_total == (uint64_t)CDLIM_INFINITY)
365                         vc_data.space_total = (uint32_t)CDLIM_INFINITY;
366                 else
367                         vc_data.space_total = dli->dl_space_total >> 10;
368
369                 vc_data.reserved = 100 - ((dli->dl_nrlmult * 100 + 512) >> 10);
370                 spin_unlock(&dli->dl_lock);
371
372                 put_dl_info(dli);
373                 ret = -EFAULT;
374                 if (copy_to_user(data, &vc_data, sizeof(vc_data)))
375                         goto out_release;
376
377                 ret = 0;
378         out_release:
379                 path_release(&nd);
380         }
381         return ret;
382 }
383
384
385 void vx_vsi_statfs(struct super_block *sb, struct kstatfs *buf)
386 {
387         struct dl_info *dli;
388         __u64 blimit, bfree, bavail;
389         __u32 ifree;
390
391         dli = locate_dl_info(sb, vx_current_xid());
392         if (!dli)
393                 return;
394
395         spin_lock(&dli->dl_lock);
396         if (dli->dl_inodes_total == (uint32_t)CDLIM_INFINITY)
397                 goto no_ilim;
398
399         /* reduce max inodes available to limit */
400         if (buf->f_files > dli->dl_inodes_total)
401                 buf->f_files = dli->dl_inodes_total;
402
403         ifree = dli->dl_inodes_total - dli->dl_inodes_used;
404         /* reduce free inodes to min */
405         if (ifree < buf->f_ffree)
406                 buf->f_ffree = ifree;
407
408 no_ilim:
409         if (dli->dl_space_total == (uint64_t)CDLIM_INFINITY)
410                 goto no_blim;
411
412         blimit = dli->dl_space_total >> sb->s_blocksize_bits;
413
414         if (dli->dl_space_total < dli->dl_space_used)
415                 bfree = 0;
416         else
417                 bfree = (dli->dl_space_total - dli->dl_space_used)
418                         >> sb->s_blocksize_bits;
419
420         bavail = ((dli->dl_space_total >> 10) * dli->dl_nrlmult);
421         if (bavail < dli->dl_space_used)
422                 bavail = 0;
423         else
424                 bavail = (bavail - dli->dl_space_used)
425                         >> sb->s_blocksize_bits;
426
427         /* reduce max space available to limit */
428         if (buf->f_blocks > blimit)
429                 buf->f_blocks = blimit;
430
431         /* reduce free space to min */
432         if (bfree < buf->f_bfree)
433                 buf->f_bfree = bfree;
434
435         /* reduce avail space to min */
436         if (bavail < buf->f_bavail)
437                 buf->f_bavail = bavail;
438
439 no_blim:
440         spin_unlock(&dli->dl_lock);
441         put_dl_info(dli);
442
443         return;
444 }
445
446 #include <linux/module.h>
447
448 EXPORT_SYMBOL_GPL(locate_dl_info);
449 EXPORT_SYMBOL_GPL(rcu_free_dl_info);
450