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