diff options
Diffstat (limited to 'kernel/acct.c')
-rw-r--r-- | kernel/acct.c | 222 |
1 files changed, 144 insertions, 78 deletions
diff --git a/kernel/acct.c b/kernel/acct.c index 91e1cfd734d2..dd68b9059418 100644 --- a/kernel/acct.c +++ b/kernel/acct.c | |||
@@ -75,37 +75,39 @@ int acct_parm[3] = {4, 2, 30}; | |||
75 | /* | 75 | /* |
76 | * External references and all of the globals. | 76 | * External references and all of the globals. |
77 | */ | 77 | */ |
78 | static void do_acct_process(struct pid_namespace *ns, struct file *); | 78 | static void do_acct_process(struct bsd_acct_struct *acct, |
79 | struct pid_namespace *ns, struct file *); | ||
79 | 80 | ||
80 | /* | 81 | /* |
81 | * This structure is used so that all the data protected by lock | 82 | * This structure is used so that all the data protected by lock |
82 | * can be placed in the same cache line as the lock. This primes | 83 | * can be placed in the same cache line as the lock. This primes |
83 | * the cache line to have the data after getting the lock. | 84 | * the cache line to have the data after getting the lock. |
84 | */ | 85 | */ |
85 | struct acct_glbs { | 86 | struct bsd_acct_struct { |
86 | spinlock_t lock; | ||
87 | volatile int active; | 87 | volatile int active; |
88 | volatile int needcheck; | 88 | volatile int needcheck; |
89 | struct file *file; | 89 | struct file *file; |
90 | struct pid_namespace *ns; | 90 | struct pid_namespace *ns; |
91 | struct timer_list timer; | 91 | struct timer_list timer; |
92 | struct list_head list; | ||
92 | }; | 93 | }; |
93 | 94 | ||
94 | static struct acct_glbs acct_globals __cacheline_aligned = | 95 | static DEFINE_SPINLOCK(acct_lock); |
95 | {__SPIN_LOCK_UNLOCKED(acct_globals.lock)}; | 96 | static LIST_HEAD(acct_list); |
96 | 97 | ||
97 | /* | 98 | /* |
98 | * Called whenever the timer says to check the free space. | 99 | * Called whenever the timer says to check the free space. |
99 | */ | 100 | */ |
100 | static void acct_timeout(unsigned long unused) | 101 | static void acct_timeout(unsigned long x) |
101 | { | 102 | { |
102 | acct_globals.needcheck = 1; | 103 | struct bsd_acct_struct *acct = (struct bsd_acct_struct *)x; |
104 | acct->needcheck = 1; | ||
103 | } | 105 | } |
104 | 106 | ||
105 | /* | 107 | /* |
106 | * Check the amount of free space and suspend/resume accordingly. | 108 | * Check the amount of free space and suspend/resume accordingly. |
107 | */ | 109 | */ |
108 | static int check_free_space(struct file *file) | 110 | static int check_free_space(struct bsd_acct_struct *acct, struct file *file) |
109 | { | 111 | { |
110 | struct kstatfs sbuf; | 112 | struct kstatfs sbuf; |
111 | int res; | 113 | int res; |
@@ -113,11 +115,11 @@ static int check_free_space(struct file *file) | |||
113 | sector_t resume; | 115 | sector_t resume; |
114 | sector_t suspend; | 116 | sector_t suspend; |
115 | 117 | ||
116 | spin_lock(&acct_globals.lock); | 118 | spin_lock(&acct_lock); |
117 | res = acct_globals.active; | 119 | res = acct->active; |
118 | if (!file || !acct_globals.needcheck) | 120 | if (!file || !acct->needcheck) |
119 | goto out; | 121 | goto out; |
120 | spin_unlock(&acct_globals.lock); | 122 | spin_unlock(&acct_lock); |
121 | 123 | ||
122 | /* May block */ | 124 | /* May block */ |
123 | if (vfs_statfs(file->f_path.dentry, &sbuf)) | 125 | if (vfs_statfs(file->f_path.dentry, &sbuf)) |
@@ -136,35 +138,35 @@ static int check_free_space(struct file *file) | |||
136 | act = 0; | 138 | act = 0; |
137 | 139 | ||
138 | /* | 140 | /* |
139 | * If some joker switched acct_globals.file under us we'ld better be | 141 | * If some joker switched acct->file under us we'ld better be |
140 | * silent and _not_ touch anything. | 142 | * silent and _not_ touch anything. |
141 | */ | 143 | */ |
142 | spin_lock(&acct_globals.lock); | 144 | spin_lock(&acct_lock); |
143 | if (file != acct_globals.file) { | 145 | if (file != acct->file) { |
144 | if (act) | 146 | if (act) |
145 | res = act>0; | 147 | res = act>0; |
146 | goto out; | 148 | goto out; |
147 | } | 149 | } |
148 | 150 | ||
149 | if (acct_globals.active) { | 151 | if (acct->active) { |
150 | if (act < 0) { | 152 | if (act < 0) { |
151 | acct_globals.active = 0; | 153 | acct->active = 0; |
152 | printk(KERN_INFO "Process accounting paused\n"); | 154 | printk(KERN_INFO "Process accounting paused\n"); |
153 | } | 155 | } |
154 | } else { | 156 | } else { |
155 | if (act > 0) { | 157 | if (act > 0) { |
156 | acct_globals.active = 1; | 158 | acct->active = 1; |
157 | printk(KERN_INFO "Process accounting resumed\n"); | 159 | printk(KERN_INFO "Process accounting resumed\n"); |
158 | } | 160 | } |
159 | } | 161 | } |
160 | 162 | ||
161 | del_timer(&acct_globals.timer); | 163 | del_timer(&acct->timer); |
162 | acct_globals.needcheck = 0; | 164 | acct->needcheck = 0; |
163 | acct_globals.timer.expires = jiffies + ACCT_TIMEOUT*HZ; | 165 | acct->timer.expires = jiffies + ACCT_TIMEOUT*HZ; |
164 | add_timer(&acct_globals.timer); | 166 | add_timer(&acct->timer); |
165 | res = acct_globals.active; | 167 | res = acct->active; |
166 | out: | 168 | out: |
167 | spin_unlock(&acct_globals.lock); | 169 | spin_unlock(&acct_lock); |
168 | return res; | 170 | return res; |
169 | } | 171 | } |
170 | 172 | ||
@@ -172,39 +174,41 @@ out: | |||
172 | * Close the old accounting file (if currently open) and then replace | 174 | * Close the old accounting file (if currently open) and then replace |
173 | * it with file (if non-NULL). | 175 | * it with file (if non-NULL). |
174 | * | 176 | * |
175 | * NOTE: acct_globals.lock MUST be held on entry and exit. | 177 | * NOTE: acct_lock MUST be held on entry and exit. |
176 | */ | 178 | */ |
177 | static void acct_file_reopen(struct file *file) | 179 | static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file, |
180 | struct pid_namespace *ns) | ||
178 | { | 181 | { |
179 | struct file *old_acct = NULL; | 182 | struct file *old_acct = NULL; |
180 | struct pid_namespace *old_ns = NULL; | 183 | struct pid_namespace *old_ns = NULL; |
181 | 184 | ||
182 | if (acct_globals.file) { | 185 | if (acct->file) { |
183 | old_acct = acct_globals.file; | 186 | old_acct = acct->file; |
184 | old_ns = acct_globals.ns; | 187 | old_ns = acct->ns; |
185 | del_timer(&acct_globals.timer); | 188 | del_timer(&acct->timer); |
186 | acct_globals.active = 0; | 189 | acct->active = 0; |
187 | acct_globals.needcheck = 0; | 190 | acct->needcheck = 0; |
188 | acct_globals.file = NULL; | 191 | acct->file = NULL; |
192 | acct->ns = NULL; | ||
193 | list_del(&acct->list); | ||
189 | } | 194 | } |
190 | if (file) { | 195 | if (file) { |
191 | acct_globals.file = file; | 196 | acct->file = file; |
192 | acct_globals.ns = get_pid_ns(task_active_pid_ns(current)); | 197 | acct->ns = ns; |
193 | acct_globals.needcheck = 0; | 198 | acct->needcheck = 0; |
194 | acct_globals.active = 1; | 199 | acct->active = 1; |
200 | list_add(&acct->list, &acct_list); | ||
195 | /* It's been deleted if it was used before so this is safe */ | 201 | /* It's been deleted if it was used before so this is safe */ |
196 | init_timer(&acct_globals.timer); | 202 | setup_timer(&acct->timer, acct_timeout, (unsigned long)acct); |
197 | acct_globals.timer.function = acct_timeout; | 203 | acct->timer.expires = jiffies + ACCT_TIMEOUT*HZ; |
198 | acct_globals.timer.expires = jiffies + ACCT_TIMEOUT*HZ; | 204 | add_timer(&acct->timer); |
199 | add_timer(&acct_globals.timer); | ||
200 | } | 205 | } |
201 | if (old_acct) { | 206 | if (old_acct) { |
202 | mnt_unpin(old_acct->f_path.mnt); | 207 | mnt_unpin(old_acct->f_path.mnt); |
203 | spin_unlock(&acct_globals.lock); | 208 | spin_unlock(&acct_lock); |
204 | do_acct_process(old_ns, old_acct); | 209 | do_acct_process(acct, old_ns, old_acct); |
205 | filp_close(old_acct, NULL); | 210 | filp_close(old_acct, NULL); |
206 | put_pid_ns(old_ns); | 211 | spin_lock(&acct_lock); |
207 | spin_lock(&acct_globals.lock); | ||
208 | } | 212 | } |
209 | } | 213 | } |
210 | 214 | ||
@@ -212,6 +216,8 @@ static int acct_on(char *name) | |||
212 | { | 216 | { |
213 | struct file *file; | 217 | struct file *file; |
214 | int error; | 218 | int error; |
219 | struct pid_namespace *ns; | ||
220 | struct bsd_acct_struct *acct = NULL; | ||
215 | 221 | ||
216 | /* Difference from BSD - they don't do O_APPEND */ | 222 | /* Difference from BSD - they don't do O_APPEND */ |
217 | file = filp_open(name, O_WRONLY|O_APPEND|O_LARGEFILE, 0); | 223 | file = filp_open(name, O_WRONLY|O_APPEND|O_LARGEFILE, 0); |
@@ -228,18 +234,34 @@ static int acct_on(char *name) | |||
228 | return -EIO; | 234 | return -EIO; |
229 | } | 235 | } |
230 | 236 | ||
237 | ns = task_active_pid_ns(current); | ||
238 | if (ns->bacct == NULL) { | ||
239 | acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL); | ||
240 | if (acct == NULL) { | ||
241 | filp_close(file, NULL); | ||
242 | return -ENOMEM; | ||
243 | } | ||
244 | } | ||
245 | |||
231 | error = security_acct(file); | 246 | error = security_acct(file); |
232 | if (error) { | 247 | if (error) { |
248 | kfree(acct); | ||
233 | filp_close(file, NULL); | 249 | filp_close(file, NULL); |
234 | return error; | 250 | return error; |
235 | } | 251 | } |
236 | 252 | ||
237 | spin_lock(&acct_globals.lock); | 253 | spin_lock(&acct_lock); |
254 | if (ns->bacct == NULL) { | ||
255 | ns->bacct = acct; | ||
256 | acct = NULL; | ||
257 | } | ||
258 | |||
238 | mnt_pin(file->f_path.mnt); | 259 | mnt_pin(file->f_path.mnt); |
239 | acct_file_reopen(file); | 260 | acct_file_reopen(ns->bacct, file, ns); |
240 | spin_unlock(&acct_globals.lock); | 261 | spin_unlock(&acct_lock); |
241 | 262 | ||
242 | mntput(file->f_path.mnt); /* it's pinned, now give up active reference */ | 263 | mntput(file->f_path.mnt); /* it's pinned, now give up active reference */ |
264 | kfree(acct); | ||
243 | 265 | ||
244 | return 0; | 266 | return 0; |
245 | } | 267 | } |
@@ -269,11 +291,17 @@ asmlinkage long sys_acct(const char __user *name) | |||
269 | error = acct_on(tmp); | 291 | error = acct_on(tmp); |
270 | putname(tmp); | 292 | putname(tmp); |
271 | } else { | 293 | } else { |
294 | struct bsd_acct_struct *acct; | ||
295 | |||
296 | acct = task_active_pid_ns(current)->bacct; | ||
297 | if (acct == NULL) | ||
298 | return 0; | ||
299 | |||
272 | error = security_acct(NULL); | 300 | error = security_acct(NULL); |
273 | if (!error) { | 301 | if (!error) { |
274 | spin_lock(&acct_globals.lock); | 302 | spin_lock(&acct_lock); |
275 | acct_file_reopen(NULL); | 303 | acct_file_reopen(acct, NULL, NULL); |
276 | spin_unlock(&acct_globals.lock); | 304 | spin_unlock(&acct_lock); |
277 | } | 305 | } |
278 | } | 306 | } |
279 | return error; | 307 | return error; |
@@ -288,10 +316,16 @@ asmlinkage long sys_acct(const char __user *name) | |||
288 | */ | 316 | */ |
289 | void acct_auto_close_mnt(struct vfsmount *m) | 317 | void acct_auto_close_mnt(struct vfsmount *m) |
290 | { | 318 | { |
291 | spin_lock(&acct_globals.lock); | 319 | struct bsd_acct_struct *acct; |
292 | if (acct_globals.file && acct_globals.file->f_path.mnt == m) | 320 | |
293 | acct_file_reopen(NULL); | 321 | spin_lock(&acct_lock); |
294 | spin_unlock(&acct_globals.lock); | 322 | restart: |
323 | list_for_each_entry(acct, &acct_list, list) | ||
324 | if (acct->file && acct->file->f_path.mnt == m) { | ||
325 | acct_file_reopen(acct, NULL, NULL); | ||
326 | goto restart; | ||
327 | } | ||
328 | spin_unlock(&acct_lock); | ||
295 | } | 329 | } |
296 | 330 | ||
297 | /** | 331 | /** |
@@ -303,12 +337,31 @@ void acct_auto_close_mnt(struct vfsmount *m) | |||
303 | */ | 337 | */ |
304 | void acct_auto_close(struct super_block *sb) | 338 | void acct_auto_close(struct super_block *sb) |
305 | { | 339 | { |
306 | spin_lock(&acct_globals.lock); | 340 | struct bsd_acct_struct *acct; |
307 | if (acct_globals.file && | 341 | |
308 | acct_globals.file->f_path.mnt->mnt_sb == sb) { | 342 | spin_lock(&acct_lock); |
309 | acct_file_reopen(NULL); | 343 | restart: |
344 | list_for_each_entry(acct, &acct_list, list) | ||
345 | if (acct->file && acct->file->f_path.mnt->mnt_sb == sb) { | ||
346 | acct_file_reopen(acct, NULL, NULL); | ||
347 | goto restart; | ||
348 | } | ||
349 | spin_unlock(&acct_lock); | ||
350 | } | ||
351 | |||
352 | void acct_exit_ns(struct pid_namespace *ns) | ||
353 | { | ||
354 | struct bsd_acct_struct *acct; | ||
355 | |||
356 | spin_lock(&acct_lock); | ||
357 | acct = ns->bacct; | ||
358 | if (acct != NULL) { | ||
359 | if (acct->file != NULL) | ||
360 | acct_file_reopen(acct, NULL, NULL); | ||
361 | |||
362 | kfree(acct); | ||
310 | } | 363 | } |
311 | spin_unlock(&acct_globals.lock); | 364 | spin_unlock(&acct_lock); |
312 | } | 365 | } |
313 | 366 | ||
314 | /* | 367 | /* |
@@ -425,7 +478,8 @@ static u32 encode_float(u64 value) | |||
425 | /* | 478 | /* |
426 | * do_acct_process does all actual work. Caller holds the reference to file. | 479 | * do_acct_process does all actual work. Caller holds the reference to file. |
427 | */ | 480 | */ |
428 | static void do_acct_process(struct pid_namespace *ns, struct file *file) | 481 | static void do_acct_process(struct bsd_acct_struct *acct, |
482 | struct pid_namespace *ns, struct file *file) | ||
429 | { | 483 | { |
430 | struct pacct_struct *pacct = ¤t->signal->pacct; | 484 | struct pacct_struct *pacct = ¤t->signal->pacct; |
431 | acct_t ac; | 485 | acct_t ac; |
@@ -440,7 +494,7 @@ static void do_acct_process(struct pid_namespace *ns, struct file *file) | |||
440 | * First check to see if there is enough free_space to continue | 494 | * First check to see if there is enough free_space to continue |
441 | * the process accounting system. | 495 | * the process accounting system. |
442 | */ | 496 | */ |
443 | if (!check_free_space(file)) | 497 | if (!check_free_space(acct, file)) |
444 | return; | 498 | return; |
445 | 499 | ||
446 | /* | 500 | /* |
@@ -577,34 +631,46 @@ void acct_collect(long exitcode, int group_dead) | |||
577 | spin_unlock_irq(¤t->sighand->siglock); | 631 | spin_unlock_irq(¤t->sighand->siglock); |
578 | } | 632 | } |
579 | 633 | ||
580 | /** | 634 | static void acct_process_in_ns(struct pid_namespace *ns) |
581 | * acct_process - now just a wrapper around do_acct_process | ||
582 | * @exitcode: task exit code | ||
583 | * | ||
584 | * handles process accounting for an exiting task | ||
585 | */ | ||
586 | void acct_process(void) | ||
587 | { | 635 | { |
588 | struct file *file = NULL; | 636 | struct file *file = NULL; |
589 | struct pid_namespace *ns; | 637 | struct bsd_acct_struct *acct; |
590 | 638 | ||
639 | acct = ns->bacct; | ||
591 | /* | 640 | /* |
592 | * accelerate the common fastpath: | 641 | * accelerate the common fastpath: |
593 | */ | 642 | */ |
594 | if (!acct_globals.file) | 643 | if (!acct || !acct->file) |
595 | return; | 644 | return; |
596 | 645 | ||
597 | spin_lock(&acct_globals.lock); | 646 | spin_lock(&acct_lock); |
598 | file = acct_globals.file; | 647 | file = acct->file; |
599 | if (unlikely(!file)) { | 648 | if (unlikely(!file)) { |
600 | spin_unlock(&acct_globals.lock); | 649 | spin_unlock(&acct_lock); |
601 | return; | 650 | return; |
602 | } | 651 | } |
603 | get_file(file); | 652 | get_file(file); |
604 | ns = get_pid_ns(acct_globals.ns); | 653 | spin_unlock(&acct_lock); |
605 | spin_unlock(&acct_globals.lock); | ||
606 | 654 | ||
607 | do_acct_process(ns, file); | 655 | do_acct_process(acct, ns, file); |
608 | fput(file); | 656 | fput(file); |
609 | put_pid_ns(ns); | 657 | } |
658 | |||
659 | /** | ||
660 | * acct_process - now just a wrapper around acct_process_in_ns, | ||
661 | * which in turn is a wrapper around do_acct_process. | ||
662 | * | ||
663 | * handles process accounting for an exiting task | ||
664 | */ | ||
665 | void acct_process(void) | ||
666 | { | ||
667 | struct pid_namespace *ns; | ||
668 | |||
669 | /* | ||
670 | * This loop is safe lockless, since current is still | ||
671 | * alive and holds its namespace, which in turn holds | ||
672 | * its parent. | ||
673 | */ | ||
674 | for (ns = task_active_pid_ns(current); ns != NULL; ns = ns->parent) | ||
675 | acct_process_in_ns(ns); | ||
610 | } | 676 | } |