2 * linux/kernel/vserver/dlimit.c
4 * Virtual Server: Context Disk Limits
6 * Copyright (C) 2004-2005 Herbert Pƶtzl
8 * V0.01 initial version
9 * V0.02 compat32 splitup
14 #include <linux/namespace.h>
15 #include <linux/namei.h>
16 #include <linux/statfs.h>
17 #include <linux/compat.h>
18 #include <linux/vs_base.h>
19 #include <linux/vs_context.h>
20 #include <linux/vs_dlimit.h>
21 #include <linux/vserver/switch.h>
22 #include <linux/vserver/dlimit_cmd.h>
24 #include <asm/errno.h>
25 #include <asm/uaccess.h>
29 * allocate an initialized dl_info struct
30 * doesn't make it visible (hash) */
32 static struct dl_info *__alloc_dl_info(struct super_block *sb, xid_t xid)
34 struct dl_info *new = NULL;
36 vxdprintk(VXD_CBIT(dlim, 5),
37 "alloc_dl_info(%p,%d)*", sb, xid);
39 /* would this benefit from a slab cache? */
40 new = kmalloc(sizeof(struct dl_info), GFP_KERNEL);
44 memset (new, 0, sizeof(struct dl_info));
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);
53 /* rest of init goes here */
55 vxdprintk(VXD_CBIT(dlim, 4),
56 "alloc_dl_info(%p,%d) = %p", sb, xid, new);
60 /* __dealloc_dl_info()
62 * final disposal of dl_info */
64 static void __dealloc_dl_info(struct dl_info *dli)
66 vxdprintk(VXD_CBIT(dlim, 4),
67 "dealloc_dl_info(%p)", dli);
69 dli->dl_hlist.next = LIST_POISON1;
73 BUG_ON(atomic_read(&dli->dl_usecnt));
74 BUG_ON(atomic_read(&dli->dl_refcnt));
80 /* hash table for dl_info hash */
82 #define DL_HASH_SIZE 13
84 struct hlist_head dl_info_hash[DL_HASH_SIZE];
86 static spinlock_t dl_info_hash_lock = SPIN_LOCK_UNLOCKED;
89 static inline unsigned int __hashval(struct super_block *sb, xid_t xid)
91 return ((xid ^ (unsigned long)sb) % DL_HASH_SIZE);
98 * add the dli to the global hash table
99 * requires the hash_lock to be held */
101 static inline void __hash_dl_info(struct dl_info *dli)
103 struct hlist_head *head;
105 vxdprintk(VXD_CBIT(dlim, 6),
106 "__hash_dl_info: %p[#%d]", dli, dli->dl_xid);
108 head = &dl_info_hash[__hashval(dli->dl_sb, dli->dl_xid)];
109 hlist_add_head_rcu(&dli->dl_hlist, head);
112 /* __unhash_dl_info()
114 * remove the dli from the global hash table
115 * requires the hash_lock to be held */
117 static inline void __unhash_dl_info(struct dl_info *dli)
119 vxdprintk(VXD_CBIT(dlim, 6),
120 "__unhash_dl_info: %p[#%d]", dli, dli->dl_xid);
121 hlist_del_rcu(&dli->dl_hlist);
126 /* __lookup_dl_info()
128 * requires the rcu_read_lock()
129 * doesn't increment the dl_refcnt */
131 static inline struct dl_info *__lookup_dl_info(struct super_block *sb, xid_t xid)
133 struct hlist_head *head = &dl_info_hash[__hashval(sb, xid)];
134 struct hlist_node *pos;
137 hlist_for_each_entry_rcu(dli, pos, head, dl_hlist) {
139 if (dli->dl_xid == xid && dli->dl_sb == sb) {
147 struct dl_info *locate_dl_info(struct super_block *sb, xid_t xid)
152 dli = get_dl_info(__lookup_dl_info(sb, xid));
153 vxdprintk(VXD_CBIT(dlim, 7),
154 "locate_dl_info(%p,#%d) = %p", sb, xid, dli);
159 void rcu_free_dl_info(struct rcu_head *head)
161 struct dl_info *dli = container_of(head, struct dl_info, dl_rcu);
164 BUG_ON(!dli || !head);
166 usecnt = atomic_read(&dli->dl_usecnt);
169 refcnt = atomic_read(&dli->dl_refcnt);
172 vxdprintk(VXD_CBIT(dlim, 3),
173 "rcu_free_dl_info(%p)", dli);
175 __dealloc_dl_info(dli);
177 printk("!!! rcu didn't free\n");
183 static int do_addrem_dlimit(uint32_t id, const char __user *name,
184 uint32_t flags, int add)
189 ret = user_path_walk_link(name, &nd);
191 struct super_block *sb;
195 if (!nd.dentry->d_inode)
197 if (!(sb = nd.dentry->d_inode->i_sb))
201 dli = __alloc_dl_info(sb, id);
202 spin_lock(&dl_info_hash_lock);
205 if (__lookup_dl_info(sb, id))
210 spin_lock(&dl_info_hash_lock);
211 dli = __lookup_dl_info(sb, id);
216 __unhash_dl_info(dli);
220 spin_unlock(&dl_info_hash_lock);
222 __dealloc_dl_info(dli);
229 int vc_add_dlimit(uint32_t id, void __user *data)
231 struct vcmd_ctx_dlimit_base_v0 vc_data;
233 if (!vx_check(0, VX_ADMIN))
235 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
238 return do_addrem_dlimit(id, vc_data.name, vc_data.flags, 1);
241 int vc_rem_dlimit(uint32_t id, void __user *data)
243 struct vcmd_ctx_dlimit_base_v0 vc_data;
245 if (!vx_check(0, VX_ADMIN))
247 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
250 return do_addrem_dlimit(id, vc_data.name, vc_data.flags, 0);
255 int vc_add_dlimit_x32(uint32_t id, void __user *data)
257 struct vcmd_ctx_dlimit_base_v0_x32 vc_data;
259 if (!vx_check(0, VX_ADMIN))
261 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
264 return do_addrem_dlimit(id,
265 compat_ptr(vc_data.name_ptr), vc_data.flags, 1);
268 int vc_rem_dlimit_x32(uint32_t id, void __user *data)
270 struct vcmd_ctx_dlimit_base_v0_x32 vc_data;
272 if (!vx_check(0, VX_ADMIN))
274 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
277 return do_addrem_dlimit(id,
278 compat_ptr(vc_data.name_ptr), vc_data.flags, 0);
281 #endif /* CONFIG_COMPAT */
285 int do_set_dlimit(uint32_t id, const char __user *name,
286 uint32_t space_used, uint32_t space_total,
287 uint32_t inodes_used, uint32_t inodes_total,
288 uint32_t reserved, uint32_t flags)
293 ret = user_path_walk_link(name, &nd);
295 struct super_block *sb;
299 if (!nd.dentry->d_inode)
301 if (!(sb = nd.dentry->d_inode->i_sb))
303 if ((reserved != (uint32_t)CDLIM_KEEP &&
305 (inodes_used != (uint32_t)CDLIM_KEEP &&
306 inodes_used > inodes_total) ||
307 (space_used != (uint32_t)CDLIM_KEEP &&
308 space_used > space_total))
312 dli = locate_dl_info(sb, id);
316 spin_lock(&dli->dl_lock);
318 if (inodes_used != (uint32_t)CDLIM_KEEP)
319 dli->dl_inodes_used = inodes_used;
320 if (inodes_total != (uint32_t)CDLIM_KEEP)
321 dli->dl_inodes_total = inodes_total;
322 if (space_used != (uint32_t)CDLIM_KEEP) {
323 dli->dl_space_used = space_used;
324 dli->dl_space_used <<= 10;
326 if (space_total == (uint32_t)CDLIM_INFINITY)
327 dli->dl_space_total = (uint64_t)CDLIM_INFINITY;
328 else if (space_total != (uint32_t)CDLIM_KEEP) {
329 dli->dl_space_total = space_total;
330 dli->dl_space_total <<= 10;
332 if (reserved != (uint32_t)CDLIM_KEEP)
333 dli->dl_nrlmult = (1 << 10) * (100 - reserved) / 100;
335 spin_unlock(&dli->dl_lock);
346 int vc_set_dlimit(uint32_t id, void __user *data)
348 struct vcmd_ctx_dlimit_v0 vc_data;
350 if (!vx_check(0, VX_ADMIN))
352 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
355 return do_set_dlimit(id, vc_data.name,
356 vc_data.space_used, vc_data.space_total,
357 vc_data.inodes_used, vc_data.inodes_total,
358 vc_data.reserved, vc_data.flags);
363 int vc_set_dlimit_x32(uint32_t id, void __user *data)
365 struct vcmd_ctx_dlimit_v0_x32 vc_data;
367 if (!vx_check(0, VX_ADMIN))
369 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
372 return do_set_dlimit(id, compat_ptr(vc_data.name_ptr),
373 vc_data.space_used, vc_data.space_total,
374 vc_data.inodes_used, vc_data.inodes_total,
375 vc_data.reserved, vc_data.flags);
378 #endif /* CONFIG_COMPAT */
382 int do_get_dlimit(uint32_t id, const char __user *name,
383 uint32_t *space_used, uint32_t *space_total,
384 uint32_t *inodes_used, uint32_t *inodes_total,
385 uint32_t *reserved, uint32_t *flags)
390 ret = user_path_walk_link(name, &nd);
392 struct super_block *sb;
396 if (!nd.dentry->d_inode)
398 if (!(sb = nd.dentry->d_inode->i_sb))
402 dli = locate_dl_info(sb, id);
406 spin_lock(&dli->dl_lock);
407 *inodes_used = dli->dl_inodes_used;
408 *inodes_total = dli->dl_inodes_total;
409 *space_used = dli->dl_space_used >> 10;
410 if (dli->dl_space_total == (uint64_t)CDLIM_INFINITY)
411 *space_total = (uint32_t)CDLIM_INFINITY;
413 *space_total = dli->dl_space_total >> 10;
415 *reserved = 100 - ((dli->dl_nrlmult * 100 + 512) >> 10);
416 spin_unlock(&dli->dl_lock);
429 int vc_get_dlimit(uint32_t id, void __user *data)
431 struct vcmd_ctx_dlimit_v0 vc_data;
434 if (!vx_check(0, VX_ADMIN))
436 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
439 ret = do_get_dlimit(id, vc_data.name,
440 &vc_data.space_used, &vc_data.space_total,
441 &vc_data.inodes_used, &vc_data.inodes_total,
442 &vc_data.reserved, &vc_data.flags);
446 if (copy_to_user(data, &vc_data, sizeof(vc_data)))
453 int vc_get_dlimit_x32(uint32_t id, void __user *data)
455 struct vcmd_ctx_dlimit_v0_x32 vc_data;
458 if (!vx_check(0, VX_ADMIN))
460 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
463 ret = do_get_dlimit(id, compat_ptr(vc_data.name_ptr),
464 &vc_data.space_used, &vc_data.space_total,
465 &vc_data.inodes_used, &vc_data.inodes_total,
466 &vc_data.reserved, &vc_data.flags);
470 if (copy_to_user(data, &vc_data, sizeof(vc_data)))
475 #endif /* CONFIG_COMPAT */
478 void vx_vsi_statfs(struct super_block *sb, struct kstatfs *buf)
481 __u64 blimit, bfree, bavail;
484 dli = locate_dl_info(sb, vx_current_xid());
488 spin_lock(&dli->dl_lock);
489 if (dli->dl_inodes_total == (uint32_t)CDLIM_INFINITY)
492 /* reduce max inodes available to limit */
493 if (buf->f_files > dli->dl_inodes_total)
494 buf->f_files = dli->dl_inodes_total;
496 /* inode hack for reiserfs */
497 if ((buf->f_files == 0) && (dli->dl_inodes_total > 0)) {
498 buf->f_files = dli->dl_inodes_total;
499 buf->f_ffree = dli->dl_inodes_total;
502 ifree = dli->dl_inodes_total - dli->dl_inodes_used;
503 /* reduce free inodes to min */
504 if (ifree < buf->f_ffree)
505 buf->f_ffree = ifree;
508 if (dli->dl_space_total == (uint64_t)CDLIM_INFINITY)
511 blimit = dli->dl_space_total >> sb->s_blocksize_bits;
513 if (dli->dl_space_total < dli->dl_space_used)
516 bfree = (dli->dl_space_total - dli->dl_space_used)
517 >> sb->s_blocksize_bits;
519 bavail = ((dli->dl_space_total >> 10) * dli->dl_nrlmult);
520 if (bavail < dli->dl_space_used)
523 bavail = (bavail - dli->dl_space_used)
524 >> sb->s_blocksize_bits;
526 /* reduce max space available to limit */
527 if (buf->f_blocks > blimit)
528 buf->f_blocks = blimit;
530 /* reduce free space to min */
531 if (bfree < buf->f_bfree)
532 buf->f_bfree = bfree;
534 /* reduce avail space to min */
535 if (bavail < buf->f_bavail)
536 buf->f_bavail = bavail;
539 spin_unlock(&dli->dl_lock);
545 #include <linux/module.h>
547 EXPORT_SYMBOL_GPL(locate_dl_info);
548 EXPORT_SYMBOL_GPL(rcu_free_dl_info);