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