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/vserver/switch.h>
19 #include <linux/vs_context.h>
20 #include <linux/vs_dlimit.h>
21 #include <linux/vserver/dlimit_cmd.h>
23 #include <asm/errno.h>
24 #include <asm/uaccess.h>
28 * allocate an initialized dl_info struct
29 * doesn't make it visible (hash) */
31 static struct dl_info *__alloc_dl_info(struct super_block *sb, xid_t xid)
33 struct dl_info *new = NULL;
35 vxdprintk(VXD_CBIT(dlim, 5),
36 "alloc_dl_info(%p,%d)*", sb, xid);
38 /* would this benefit from a slab cache? */
39 new = kmalloc(sizeof(struct dl_info), GFP_KERNEL);
43 memset (new, 0, sizeof(struct dl_info));
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);
52 /* rest of init goes here */
54 vxdprintk(VXD_CBIT(dlim, 4),
55 "alloc_dl_info(%p,%d) = %p", sb, xid, new);
59 /* __dealloc_dl_info()
61 * final disposal of dl_info */
63 static void __dealloc_dl_info(struct dl_info *dli)
65 vxdprintk(VXD_CBIT(dlim, 4),
66 "dealloc_dl_info(%p)", dli);
68 dli->dl_hlist.next = LIST_POISON1;
72 BUG_ON(atomic_read(&dli->dl_usecnt));
73 BUG_ON(atomic_read(&dli->dl_refcnt));
79 /* hash table for dl_info hash */
81 #define DL_HASH_SIZE 13
83 struct hlist_head dl_info_hash[DL_HASH_SIZE];
85 static spinlock_t dl_info_hash_lock = SPIN_LOCK_UNLOCKED;
88 static inline unsigned int __hashval(struct super_block *sb, xid_t xid)
90 return ((xid ^ (unsigned long)sb) % DL_HASH_SIZE);
97 * add the dli to the global hash table
98 * requires the hash_lock to be held */
100 static inline void __hash_dl_info(struct dl_info *dli)
102 struct hlist_head *head;
104 vxdprintk(VXD_CBIT(dlim, 6),
105 "__hash_dl_info: %p[#%d]", dli, dli->dl_xid);
107 head = &dl_info_hash[__hashval(dli->dl_sb, dli->dl_xid)];
108 hlist_add_head_rcu(&dli->dl_hlist, head);
111 /* __unhash_dl_info()
113 * remove the dli from the global hash table
114 * requires the hash_lock to be held */
116 static inline void __unhash_dl_info(struct dl_info *dli)
118 vxdprintk(VXD_CBIT(dlim, 6),
119 "__unhash_dl_info: %p[#%d]", dli, dli->dl_xid);
120 hlist_del_rcu(&dli->dl_hlist);
125 /* __lookup_dl_info()
127 * requires the rcu_read_lock()
128 * doesn't increment the dl_refcnt */
130 static inline struct dl_info *__lookup_dl_info(struct super_block *sb, xid_t xid)
132 struct hlist_head *head = &dl_info_hash[__hashval(sb, xid)];
133 struct hlist_node *pos;
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);
141 if (dli->dl_xid == xid && dli->dl_sb == sb) {
149 struct dl_info *locate_dl_info(struct super_block *sb, xid_t xid)
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);
161 void rcu_free_dl_info(struct rcu_head *head)
163 struct dl_info *dli = container_of(head, struct dl_info, dl_rcu);
166 BUG_ON(!dli || !head);
168 usecnt = atomic_read(&dli->dl_usecnt);
171 refcnt = atomic_read(&dli->dl_refcnt);
174 vxdprintk(VXD_CBIT(dlim, 3),
175 "rcu_free_dl_info(%p)", dli);
177 __dealloc_dl_info(dli);
179 printk("!!! rcu didn't free\n");
185 static int do_addrem_dlimit(uint32_t id, const char __user *name,
186 uint32_t flags, int add)
191 ret = user_path_walk_link(name, &nd);
193 struct super_block *sb;
197 if (!nd.dentry->d_inode)
199 if (!(sb = nd.dentry->d_inode->i_sb))
203 dli = __alloc_dl_info(sb, id);
204 spin_lock(&dl_info_hash_lock);
207 if (__lookup_dl_info(sb, id))
212 spin_lock(&dl_info_hash_lock);
213 dli = __lookup_dl_info(sb, id);
218 __unhash_dl_info(dli);
222 spin_unlock(&dl_info_hash_lock);
224 __dealloc_dl_info(dli);
231 int vc_add_dlimit(uint32_t id, void __user *data)
233 struct vcmd_ctx_dlimit_base_v0 vc_data;
235 if (!vx_check(0, VX_ADMIN))
237 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
240 return do_addrem_dlimit(id, vc_data.name, vc_data.flags, 1);
243 int vc_rem_dlimit(uint32_t id, void __user *data)
245 struct vcmd_ctx_dlimit_base_v0 vc_data;
247 if (!vx_check(0, VX_ADMIN))
249 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
252 return do_addrem_dlimit(id, vc_data.name, vc_data.flags, 0);
257 int vc_add_dlimit_x32(uint32_t id, void __user *data)
259 struct vcmd_ctx_dlimit_base_v0_x32 vc_data;
261 if (!vx_check(0, VX_ADMIN))
263 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
266 return do_addrem_dlimit(id,
267 compat_ptr(vc_data.name_ptr), vc_data.flags, 1);
270 int vc_rem_dlimit_x32(uint32_t id, void __user *data)
272 struct vcmd_ctx_dlimit_base_v0_x32 vc_data;
274 if (!vx_check(0, VX_ADMIN))
276 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
279 return do_addrem_dlimit(id,
280 compat_ptr(vc_data.name_ptr), vc_data.flags, 0);
283 #endif /* CONFIG_COMPAT */
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)
295 ret = user_path_walk_link(name, &nd);
297 struct super_block *sb;
301 if (!nd.dentry->d_inode)
303 if (!(sb = nd.dentry->d_inode->i_sb))
305 if ((reserved != (uint32_t)CDLIM_KEEP &&
307 (inodes_used != (uint32_t)CDLIM_KEEP &&
308 inodes_used > inodes_total) ||
309 (space_used != (uint32_t)CDLIM_KEEP &&
310 space_used > space_total))
314 dli = locate_dl_info(sb, id);
318 spin_lock(&dli->dl_lock);
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;
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;
334 if (reserved != (uint32_t)CDLIM_KEEP)
335 dli->dl_nrlmult = (1 << 10) * (100 - reserved) / 100;
337 spin_unlock(&dli->dl_lock);
348 int vc_set_dlimit(uint32_t id, void __user *data)
350 struct vcmd_ctx_dlimit_v0 vc_data;
352 if (!vx_check(0, VX_ADMIN))
354 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
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);
365 int vc_set_dlimit_x32(uint32_t id, void __user *data)
367 struct vcmd_ctx_dlimit_v0_x32 vc_data;
369 if (!vx_check(0, VX_ADMIN))
371 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
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);
380 #endif /* CONFIG_COMPAT */
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)
392 ret = user_path_walk_link(name, &nd);
394 struct super_block *sb;
398 if (!nd.dentry->d_inode)
400 if (!(sb = nd.dentry->d_inode->i_sb))
404 dli = locate_dl_info(sb, id);
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;
415 *space_total = dli->dl_space_total >> 10;
417 *reserved = 100 - ((dli->dl_nrlmult * 100 + 512) >> 10);
418 spin_unlock(&dli->dl_lock);
431 int vc_get_dlimit(uint32_t id, void __user *data)
433 struct vcmd_ctx_dlimit_v0 vc_data;
436 if (!vx_check(0, VX_ADMIN))
438 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
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);
448 if (copy_to_user(data, &vc_data, sizeof(vc_data)))
455 int vc_get_dlimit_x32(uint32_t id, void __user *data)
457 struct vcmd_ctx_dlimit_v0_x32 vc_data;
460 if (!vx_check(0, VX_ADMIN))
462 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
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);
472 if (copy_to_user(data, &vc_data, sizeof(vc_data)))
477 #endif /* CONFIG_COMPAT */
480 void vx_vsi_statfs(struct super_block *sb, struct kstatfs *buf)
483 __u64 blimit, bfree, bavail;
486 dli = locate_dl_info(sb, vx_current_xid());
490 spin_lock(&dli->dl_lock);
491 if (dli->dl_inodes_total == (uint32_t)CDLIM_INFINITY)
494 /* reduce max inodes available to limit */
495 if (buf->f_files > dli->dl_inodes_total)
496 buf->f_files = dli->dl_inodes_total;
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;
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;
510 if (dli->dl_space_total == (uint64_t)CDLIM_INFINITY)
513 blimit = dli->dl_space_total >> sb->s_blocksize_bits;
515 if (dli->dl_space_total < dli->dl_space_used)
518 bfree = (dli->dl_space_total - dli->dl_space_used)
519 >> sb->s_blocksize_bits;
521 bavail = ((dli->dl_space_total >> 10) * dli->dl_nrlmult);
522 if (bavail < dli->dl_space_used)
525 bavail = (bavail - dli->dl_space_used)
526 >> sb->s_blocksize_bits;
528 /* reduce max space available to limit */
529 if (buf->f_blocks > blimit)
530 buf->f_blocks = blimit;
532 /* reduce free space to min */
533 if (bfree < buf->f_bfree)
534 buf->f_bfree = bfree;
536 /* reduce avail space to min */
537 if (bavail < buf->f_bavail)
538 buf->f_bavail = bavail;
541 spin_unlock(&dli->dl_lock);
547 #include <linux/module.h>
549 EXPORT_SYMBOL_GPL(locate_dl_info);
550 EXPORT_SYMBOL_GPL(rcu_free_dl_info);