This commit was manufactured by cvs2svn to create branch 'vserver'.
[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  Herbert Pƶtzl
7  *
8  *  V0.01  initial version
9  *
10  */
11
12 #include <linux/config.h>
13 #include <linux/fs.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>
21
22 #include <asm/errno.h>
23 #include <asm/uaccess.h>
24
25 /*      __alloc_dl_info()
26
27         * allocate an initialized dl_info struct
28         * doesn't make it visible (hash)                        */
29
30 static struct dl_info *__alloc_dl_info(struct super_block *sb, xid_t xid)
31 {
32         struct dl_info *new = NULL;
33         
34         vxdprintk("alloc_dl_info(%p,%d)\n", sb, xid);
35
36         /* would this benefit from a slab cache? */
37         new = kmalloc(sizeof(struct dl_info), GFP_KERNEL);
38         if (!new)
39                 return 0;
40
41         memset (new, 0, sizeof(struct dl_info));
42         new->dl_xid = xid;
43         new->dl_sb = sb;
44         INIT_RCU_HEAD(&new->dl_rcu);
45         INIT_HLIST_NODE(&new->dl_hlist);
46         spin_lock_init(&new->dl_lock);
47         atomic_set(&new->dl_refcnt, 0);
48         atomic_set(&new->dl_usecnt, 0);
49
50         /* rest of init goes here */
51
52         vxdprintk("alloc_dl_info(%p,%d) = %p\n", sb, xid, new);
53         return new;
54 }
55
56 /*      __dealloc_dl_info()
57
58         * final disposal of dl_info                             */
59
60 static void __dealloc_dl_info(struct dl_info *dli)
61 {
62         vxdprintk("dealloc_dl_info(%p)\n", dli);
63
64         dli->dl_hlist.next = LIST_POISON1;
65         dli->dl_xid = -1;
66         dli->dl_sb = 0;
67
68         BUG_ON(atomic_read(&dli->dl_usecnt));
69         BUG_ON(atomic_read(&dli->dl_refcnt));
70
71         kfree(dli);
72 }
73
74
75 /*      hash table for dl_info hash */
76
77 #define DL_HASH_SIZE    13
78
79 struct hlist_head dl_info_hash[DL_HASH_SIZE];
80
81 static spinlock_t dl_info_hash_lock = SPIN_LOCK_UNLOCKED;
82
83
84 static inline unsigned int __hashval(struct super_block *sb, xid_t xid)
85 {
86         return ((xid ^ (unsigned int)sb) % DL_HASH_SIZE);
87 }
88
89
90
91 /*      __hash_dl_info()
92
93         * add the dli to the global hash table
94         * requires the hash_lock to be held                     */
95
96 static inline void __hash_dl_info(struct dl_info *dli)
97 {
98         struct hlist_head *head;
99         
100         vxdprintk("__hash_dl_info: %p[#%d]\n", dli, dli->dl_xid);
101         get_dl_info(dli);
102         head = &dl_info_hash[__hashval(dli->dl_sb, dli->dl_xid)];
103         hlist_add_head_rcu(&dli->dl_hlist, head);
104 }
105
106 /*      __unhash_dl_info()
107
108         * remove the dli from the global hash table
109         * requires the hash_lock to be held                     */
110
111 static inline void __unhash_dl_info(struct dl_info *dli)
112 {
113         vxdprintk("__unhash_dl_info: %p[#%d]\n", dli, dli->dl_xid);
114         hlist_del_rcu(&dli->dl_hlist);
115         put_dl_info(dli);
116 }
117
118
119 #define hlist_for_each_rcu(pos, head) \
120         for (pos = (head)->first; pos && ({ prefetch(pos->next); 1;}); \
121                 pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
122
123
124 /*      __lookup_dl_info()
125
126         * requires the rcu_read_lock()
127         * doesn't increment the dl_refcnt                       */
128
129 static inline struct dl_info *__lookup_dl_info(struct super_block *sb, xid_t xid)
130 {
131         struct hlist_head *head = &dl_info_hash[__hashval(sb, xid)];
132         struct hlist_node *pos;
133
134         hlist_for_each_rcu(pos, head) {
135                 struct dl_info *dli =
136                         hlist_entry(pos, struct dl_info, dl_hlist);
137
138                 if (dli->dl_xid == xid && dli->dl_sb == sb) {
139                         return dli;
140                 }
141         }
142         return NULL;
143 }
144
145
146 struct dl_info *locate_dl_info(struct super_block *sb, xid_t xid)
147 {
148         struct dl_info *dli;
149
150         rcu_read_lock();
151         dli = get_dl_info(__lookup_dl_info(sb, xid));
152         rcu_read_unlock();
153         return dli;
154 }
155
156 void rcu_free_dl_info(void *obj)
157 {
158         struct dl_info *dli = obj;
159         int usecnt, refcnt;
160
161         BUG_ON(!dli);
162
163         usecnt = atomic_read(&dli->dl_usecnt);
164         BUG_ON(usecnt < 0);
165
166         refcnt = atomic_read(&dli->dl_refcnt);
167         BUG_ON(refcnt < 0);
168
169         if (!usecnt)
170                 __dealloc_dl_info(dli);
171         else
172                 printk("!!! rcu didn't free\n");
173 }
174
175
176
177
178 int vc_add_dlimit(uint32_t id, void __user *data)
179 {
180         struct nameidata nd;
181         struct vcmd_ctx_dlimit_base_v0 vc_data;
182         int ret;
183
184         if (!vx_check(0, VX_ADMIN))
185                 return -ENOSYS;
186         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
187                 return -EFAULT;
188
189         ret = user_path_walk_link(vc_data.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                 dli = __alloc_dl_info(sb, id);
201                 spin_lock(&dl_info_hash_lock);          
202
203                 ret = -EEXIST;
204                 if (__lookup_dl_info(sb, id))
205                         goto out_unlock;        
206                 __hash_dl_info(dli);
207                 dli = NULL;
208                 ret = 0;
209
210         out_unlock:
211                 spin_unlock(&dl_info_hash_lock);                
212                 if (dli)
213                         __dealloc_dl_info(dli);
214         out_release:
215                 path_release(&nd);
216         }
217         return ret;
218 }
219
220
221 int vc_rem_dlimit(uint32_t id, void __user *data)
222 {
223         struct nameidata nd;
224         struct vcmd_ctx_dlimit_base_v0 vc_data;
225         int ret;
226
227         if (!vx_check(0, VX_ADMIN))
228                 return -ENOSYS;
229         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
230                 return -EFAULT;
231
232         ret = user_path_walk_link(vc_data.name, &nd);
233         if (!ret) {
234                 struct super_block *sb;
235                 struct dl_info *dli;
236
237                 ret = -EINVAL;
238                 if (!nd.dentry->d_inode)
239                         goto out_release;
240                 if (!(sb = nd.dentry->d_inode->i_sb))
241                         goto out_release;       
242                 
243                 spin_lock(&dl_info_hash_lock);          
244                 dli = __lookup_dl_info(sb, id);
245
246                 ret = -ESRCH;
247                 if (!dli)
248                         goto out_unlock;
249                 
250                 __unhash_dl_info(dli);
251                 ret = 0;
252
253         out_unlock:
254                 spin_unlock(&dl_info_hash_lock);                
255         out_release:
256                 path_release(&nd);
257         }
258         return ret;
259 }
260
261
262 int vc_set_dlimit(uint32_t id, void __user *data)
263 {
264         struct nameidata nd;
265         struct vcmd_ctx_dlimit_v0 vc_data;
266         int ret;
267
268         if (!vx_check(0, VX_ADMIN))
269                 return -ENOSYS;
270         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
271                 return -EFAULT;
272
273         ret = user_path_walk_link(vc_data.name, &nd);
274         if (!ret) {
275                 struct super_block *sb;
276                 struct dl_info *dli;
277
278                 ret = -EINVAL;
279                 if (!nd.dentry->d_inode)
280                         goto out_release;
281                 if (!(sb = nd.dentry->d_inode->i_sb))
282                         goto out_release;       
283                 if (vc_data.reserved > 100 ||
284                         vc_data.inodes_used > vc_data.inodes_total ||
285                         vc_data.space_used > vc_data.space_total)
286                         goto out_release;
287
288                 ret = -ESRCH;
289                 dli = locate_dl_info(sb, id);
290                 if (!dli)
291                         goto out_release;
292
293                 spin_lock(&dli->dl_lock);               
294
295                 if (vc_data.inodes_used != (uint32_t)CDLIM_KEEP)
296                         dli->dl_inodes_used = vc_data.inodes_used;
297                 if (vc_data.inodes_total != (uint32_t)CDLIM_KEEP)
298                         dli->dl_inodes_total = vc_data.inodes_total;
299                 if (vc_data.space_used != (uint32_t)CDLIM_KEEP) {
300                         dli->dl_space_used = vc_data.space_used;
301                         dli->dl_space_used <<= 10;
302                 }
303                 if (vc_data.space_total == (uint32_t)CDLIM_INFINITY)
304                         dli->dl_space_total = (uint64_t)CDLIM_INFINITY;
305                 else if (vc_data.space_total != (uint32_t)CDLIM_KEEP) {
306                         dli->dl_space_total = vc_data.space_total;
307                         dli->dl_space_total <<= 10;
308                 }
309                 if (vc_data.reserved != (uint32_t)CDLIM_KEEP)
310                         dli->dl_nrlmult = (1 << 10) * (100 - vc_data.reserved) / 100;
311
312                 spin_unlock(&dli->dl_lock);             
313                 
314                 put_dl_info(dli);
315                 ret = 0;
316
317         out_release:
318                 path_release(&nd);
319         }
320         return ret;
321 }
322
323 int vc_get_dlimit(uint32_t id, void __user *data)
324 {
325         struct nameidata nd;
326         struct vcmd_ctx_dlimit_v0 vc_data;
327         int ret;
328
329         if (!vx_check(0, VX_ADMIN))
330                 return -ENOSYS;
331         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
332                 return -EFAULT;
333
334         ret = user_path_walk_link(vc_data.name, &nd);
335         if (!ret) {
336                 struct super_block *sb;
337                 struct dl_info *dli;
338
339                 ret = -EINVAL;
340                 if (!nd.dentry->d_inode)
341                         goto out_release;
342                 if (!(sb = nd.dentry->d_inode->i_sb))
343                         goto out_release;       
344                 if (vc_data.reserved > 100 ||
345                         vc_data.inodes_used > vc_data.inodes_total ||
346                         vc_data.space_used > vc_data.space_total)
347                         goto out_release;
348
349                 ret = -ESRCH;
350                 dli = locate_dl_info(sb, id);
351                 if (!dli)
352                         goto out_release;
353
354                 spin_lock(&dli->dl_lock);               
355                 vc_data.inodes_used = dli->dl_inodes_used;
356                 vc_data.inodes_total = dli->dl_inodes_total;
357                 vc_data.space_used = dli->dl_space_used >> 10;
358                 if (dli->dl_space_total == (uint64_t)CDLIM_INFINITY)
359                         vc_data.space_total = (uint32_t)CDLIM_INFINITY;
360                 else
361                         vc_data.space_total = dli->dl_space_total >> 10;
362
363                 vc_data.reserved = 100 - ((dli->dl_nrlmult * 100 + 512) >> 10);
364                 spin_unlock(&dli->dl_lock);             
365                 
366                 put_dl_info(dli);
367                 ret = -EFAULT;
368                 if (copy_to_user(data, &vc_data, sizeof(vc_data)))
369                         goto out_release;
370
371                 ret = 0;
372         out_release:
373                 path_release(&nd);
374         }
375         return ret;
376 }
377
378
379 void vx_vsi_statfs(struct super_block *sb, struct kstatfs *buf)
380 {
381         struct dl_info *dli;
382         __u64 blimit, bfree, bavail;
383         __u32 ifree;
384                 
385         dli = locate_dl_info(sb, current->xid);
386         if (!dli)
387                 return;
388
389         spin_lock(&dli->dl_lock);
390         if (dli->dl_inodes_total == (uint32_t)CDLIM_INFINITY)
391                 goto no_ilim;
392
393         /* reduce max inodes available to limit */
394         if (buf->f_files > dli->dl_inodes_total)
395                 buf->f_files = dli->dl_inodes_total;
396
397         ifree = dli->dl_inodes_total - dli->dl_inodes_used;
398         /* reduce free inodes to min */
399         if (ifree < buf->f_ffree)
400                 buf->f_ffree = ifree;
401
402 no_ilim:
403         if (dli->dl_space_total == (uint64_t)CDLIM_INFINITY)
404                 goto no_blim;
405
406         blimit = dli->dl_space_total >> sb->s_blocksize_bits;
407
408         if (dli->dl_space_total < dli->dl_space_used)
409                 bfree = 0;
410         else
411                 bfree = (dli->dl_space_total - dli->dl_space_used)
412                         >> sb->s_blocksize_bits;
413
414         bavail = ((dli->dl_space_total >> 10) * dli->dl_nrlmult);
415         if (bavail < dli->dl_space_used)
416                 bavail = 0;
417         else
418                 bavail = (bavail - dli->dl_space_used)
419                         >> sb->s_blocksize_bits;
420
421         /* reduce max space available to limit */
422         if (buf->f_blocks > blimit)
423                 buf->f_blocks = blimit;
424
425         /* reduce free space to min */
426         if (bfree < buf->f_bfree)
427                 buf->f_bfree = bfree;
428
429         /* reduce avail space to min */
430         if (bavail < buf->f_bavail)
431                 buf->f_bavail = bavail;
432
433 no_blim:
434         spin_unlock(&dli->dl_lock);
435         put_dl_info(dli);
436         
437         return; 
438 }
439