2 * linux/kernel/vserver/dlimit.c
4 * Virtual Server: Context Disk Limits
6 * Copyright (C) 2004 Herbert Pƶtzl
8 * V0.01 initial version
12 #include <linux/config.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_base.h>
19 #include <linux/vs_context.h>
20 #include <linux/vs_dlimit.h>
22 #include <asm/errno.h>
23 #include <asm/uaccess.h>
27 * allocate an initialized dl_info struct
28 * doesn't make it visible (hash) */
30 static struct dl_info *__alloc_dl_info(struct super_block *sb, xid_t xid)
32 struct dl_info *new = NULL;
34 vxdprintk(VXD_CBIT(dlim, 5),
35 "alloc_dl_info(%p,%d)*", sb, xid);
37 /* would this benefit from a slab cache? */
38 new = kmalloc(sizeof(struct dl_info), GFP_KERNEL);
42 memset (new, 0, sizeof(struct dl_info));
45 INIT_RCU_HEAD(&new->dl_rcu);
46 INIT_HLIST_NODE(&new->dl_hlist);
47 spin_lock_init(&new->dl_lock);
48 atomic_set(&new->dl_refcnt, 0);
49 atomic_set(&new->dl_usecnt, 0);
51 /* rest of init goes here */
53 vxdprintk(VXD_CBIT(dlim, 4),
54 "alloc_dl_info(%p,%d) = %p", sb, xid, new);
58 /* __dealloc_dl_info()
60 * final disposal of dl_info */
62 static void __dealloc_dl_info(struct dl_info *dli)
64 vxdprintk(VXD_CBIT(dlim, 4),
65 "dealloc_dl_info(%p)", dli);
67 dli->dl_hlist.next = LIST_POISON1;
71 BUG_ON(atomic_read(&dli->dl_usecnt));
72 BUG_ON(atomic_read(&dli->dl_refcnt));
78 /* hash table for dl_info hash */
80 #define DL_HASH_SIZE 13
82 struct hlist_head dl_info_hash[DL_HASH_SIZE];
84 static spinlock_t dl_info_hash_lock = SPIN_LOCK_UNLOCKED;
87 static inline unsigned int __hashval(struct super_block *sb, xid_t xid)
89 return ((xid ^ (unsigned long)sb) % DL_HASH_SIZE);
96 * add the dli to the global hash table
97 * requires the hash_lock to be held */
99 static inline void __hash_dl_info(struct dl_info *dli)
101 struct hlist_head *head;
103 vxdprintk(VXD_CBIT(dlim, 6),
104 "__hash_dl_info: %p[#%d]", dli, dli->dl_xid);
106 head = &dl_info_hash[__hashval(dli->dl_sb, dli->dl_xid)];
107 hlist_add_head_rcu(&dli->dl_hlist, head);
110 /* __unhash_dl_info()
112 * remove the dli from the global hash table
113 * requires the hash_lock to be held */
115 static inline void __unhash_dl_info(struct dl_info *dli)
117 vxdprintk(VXD_CBIT(dlim, 6),
118 "__unhash_dl_info: %p[#%d]", dli, dli->dl_xid);
119 hlist_del_rcu(&dli->dl_hlist);
124 #define hlist_for_each_rcu(pos, head) \
125 for (pos = (head)->first; pos && ({ prefetch(pos->next); 1;}); \
126 pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
129 /* __lookup_dl_info()
131 * requires the rcu_read_lock()
132 * doesn't increment the dl_refcnt */
134 static inline struct dl_info *__lookup_dl_info(struct super_block *sb, xid_t xid)
136 struct hlist_head *head = &dl_info_hash[__hashval(sb, xid)];
137 struct hlist_node *pos;
139 hlist_for_each_rcu(pos, head) {
140 struct dl_info *dli =
141 hlist_entry(pos, struct dl_info, dl_hlist);
143 if (dli->dl_xid == xid && dli->dl_sb == sb) {
151 struct dl_info *locate_dl_info(struct super_block *sb, xid_t xid)
156 dli = get_dl_info(__lookup_dl_info(sb, xid));
157 vxdprintk(VXD_CBIT(dlim, 7),
158 "locate_dl_info(%p,#%d) = %p", sb, xid, dli);
163 void rcu_free_dl_info(struct rcu_head *head)
165 struct dl_info *dli = container_of(head, struct dl_info, dl_rcu);
168 BUG_ON(!dli || !head);
170 usecnt = atomic_read(&dli->dl_usecnt);
173 refcnt = atomic_read(&dli->dl_refcnt);
176 vxdprintk(VXD_CBIT(dlim, 3),
177 "rcu_free_dl_info(%p)", dli);
179 __dealloc_dl_info(dli);
181 printk("!!! rcu didn't free\n");
187 int vc_add_dlimit(uint32_t id, void __user *data)
190 struct vcmd_ctx_dlimit_base_v0 vc_data;
193 if (!vx_check(0, VX_ADMIN))
195 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
198 ret = user_path_walk_link(vc_data.name, &nd);
200 struct super_block *sb;
204 if (!nd.dentry->d_inode)
206 if (!(sb = nd.dentry->d_inode->i_sb))
209 dli = __alloc_dl_info(sb, id);
210 spin_lock(&dl_info_hash_lock);
213 if (__lookup_dl_info(sb, id))
220 spin_unlock(&dl_info_hash_lock);
222 __dealloc_dl_info(dli);
230 int vc_rem_dlimit(uint32_t id, void __user *data)
233 struct vcmd_ctx_dlimit_base_v0 vc_data;
236 if (!vx_check(0, VX_ADMIN))
238 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
241 ret = user_path_walk_link(vc_data.name, &nd);
243 struct super_block *sb;
247 if (!nd.dentry->d_inode)
249 if (!(sb = nd.dentry->d_inode->i_sb))
252 spin_lock(&dl_info_hash_lock);
253 dli = __lookup_dl_info(sb, id);
259 __unhash_dl_info(dli);
263 spin_unlock(&dl_info_hash_lock);
271 int vc_set_dlimit(uint32_t id, void __user *data)
274 struct vcmd_ctx_dlimit_v0 vc_data;
277 if (!vx_check(0, VX_ADMIN))
279 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
282 ret = user_path_walk_link(vc_data.name, &nd);
284 struct super_block *sb;
288 if (!nd.dentry->d_inode)
290 if (!(sb = nd.dentry->d_inode->i_sb))
292 if (vc_data.reserved > 100 ||
293 vc_data.inodes_used > vc_data.inodes_total ||
294 vc_data.space_used > vc_data.space_total)
298 dli = locate_dl_info(sb, id);
302 spin_lock(&dli->dl_lock);
304 if (vc_data.inodes_used != (uint32_t)CDLIM_KEEP)
305 dli->dl_inodes_used = vc_data.inodes_used;
306 if (vc_data.inodes_total != (uint32_t)CDLIM_KEEP)
307 dli->dl_inodes_total = vc_data.inodes_total;
308 if (vc_data.space_used != (uint32_t)CDLIM_KEEP) {
309 dli->dl_space_used = vc_data.space_used;
310 dli->dl_space_used <<= 10;
312 if (vc_data.space_total == (uint32_t)CDLIM_INFINITY)
313 dli->dl_space_total = (uint64_t)CDLIM_INFINITY;
314 else if (vc_data.space_total != (uint32_t)CDLIM_KEEP) {
315 dli->dl_space_total = vc_data.space_total;
316 dli->dl_space_total <<= 10;
318 if (vc_data.reserved != (uint32_t)CDLIM_KEEP)
319 dli->dl_nrlmult = (1 << 10) * (100 - vc_data.reserved) / 100;
321 spin_unlock(&dli->dl_lock);
332 int vc_get_dlimit(uint32_t id, void __user *data)
335 struct vcmd_ctx_dlimit_v0 vc_data;
338 if (!vx_check(0, VX_ADMIN))
340 if (copy_from_user (&vc_data, data, sizeof(vc_data)))
343 ret = user_path_walk_link(vc_data.name, &nd);
345 struct super_block *sb;
349 if (!nd.dentry->d_inode)
351 if (!(sb = nd.dentry->d_inode->i_sb))
353 if (vc_data.reserved > 100 ||
354 vc_data.inodes_used > vc_data.inodes_total ||
355 vc_data.space_used > vc_data.space_total)
359 dli = locate_dl_info(sb, id);
363 spin_lock(&dli->dl_lock);
364 vc_data.inodes_used = dli->dl_inodes_used;
365 vc_data.inodes_total = dli->dl_inodes_total;
366 vc_data.space_used = dli->dl_space_used >> 10;
367 if (dli->dl_space_total == (uint64_t)CDLIM_INFINITY)
368 vc_data.space_total = (uint32_t)CDLIM_INFINITY;
370 vc_data.space_total = dli->dl_space_total >> 10;
372 vc_data.reserved = 100 - ((dli->dl_nrlmult * 100 + 512) >> 10);
373 spin_unlock(&dli->dl_lock);
377 if (copy_to_user(data, &vc_data, sizeof(vc_data)))
388 void vx_vsi_statfs(struct super_block *sb, struct kstatfs *buf)
391 __u64 blimit, bfree, bavail;
394 dli = locate_dl_info(sb, current->xid);
398 spin_lock(&dli->dl_lock);
399 if (dli->dl_inodes_total == (uint32_t)CDLIM_INFINITY)
402 /* reduce max inodes available to limit */
403 if (buf->f_files > dli->dl_inodes_total)
404 buf->f_files = dli->dl_inodes_total;
406 ifree = dli->dl_inodes_total - dli->dl_inodes_used;
407 /* reduce free inodes to min */
408 if (ifree < buf->f_ffree)
409 buf->f_ffree = ifree;
412 if (dli->dl_space_total == (uint64_t)CDLIM_INFINITY)
415 blimit = dli->dl_space_total >> sb->s_blocksize_bits;
417 if (dli->dl_space_total < dli->dl_space_used)
420 bfree = (dli->dl_space_total - dli->dl_space_used)
421 >> sb->s_blocksize_bits;
423 bavail = ((dli->dl_space_total >> 10) * dli->dl_nrlmult);
424 if (bavail < dli->dl_space_used)
427 bavail = (bavail - dli->dl_space_used)
428 >> sb->s_blocksize_bits;
430 /* reduce max space available to limit */
431 if (buf->f_blocks > blimit)
432 buf->f_blocks = blimit;
434 /* reduce free space to min */
435 if (bfree < buf->f_bfree)
436 buf->f_bfree = bfree;
438 /* reduce avail space to min */
439 if (bavail < buf->f_bavail)
440 buf->f_bavail = bavail;
443 spin_unlock(&dli->dl_lock);
449 #include <linux/module.h>
451 EXPORT_SYMBOL_GPL(locate_dl_info);
452 EXPORT_SYMBOL_GPL(rcu_free_dl_info);
453 // EXPORT_SYMBOL_GPL(dl_info_hash_lock);
454 // EXPORT_SYMBOL_GPL(unhash_dl_info);