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