diff options
Diffstat (limited to 'kernel/acct.c')
| -rw-r--r-- | kernel/acct.c | 94 |
1 files changed, 46 insertions, 48 deletions
diff --git a/kernel/acct.c b/kernel/acct.c index 33738ef972f3..e6c10d1a4058 100644 --- a/kernel/acct.c +++ b/kernel/acct.c | |||
| @@ -76,10 +76,11 @@ int acct_parm[3] = {4, 2, 30}; | |||
| 76 | /* | 76 | /* |
| 77 | * External references and all of the globals. | 77 | * External references and all of the globals. |
| 78 | */ | 78 | */ |
| 79 | static void do_acct_process(struct bsd_acct_struct *acct); | ||
| 80 | 79 | ||
| 81 | struct bsd_acct_struct { | 80 | struct bsd_acct_struct { |
| 82 | struct fs_pin pin; | 81 | struct fs_pin pin; |
| 82 | atomic_long_t count; | ||
| 83 | struct rcu_head rcu; | ||
| 83 | struct mutex lock; | 84 | struct mutex lock; |
| 84 | int active; | 85 | int active; |
| 85 | unsigned long needcheck; | 86 | unsigned long needcheck; |
| @@ -89,6 +90,8 @@ struct bsd_acct_struct { | |||
| 89 | struct completion done; | 90 | struct completion done; |
| 90 | }; | 91 | }; |
| 91 | 92 | ||
| 93 | static void do_acct_process(struct bsd_acct_struct *acct); | ||
| 94 | |||
| 92 | /* | 95 | /* |
| 93 | * Check the amount of free space and suspend/resume accordingly. | 96 | * Check the amount of free space and suspend/resume accordingly. |
| 94 | */ | 97 | */ |
| @@ -124,32 +127,56 @@ out: | |||
| 124 | return acct->active; | 127 | return acct->active; |
| 125 | } | 128 | } |
| 126 | 129 | ||
| 130 | static void acct_put(struct bsd_acct_struct *p) | ||
| 131 | { | ||
| 132 | if (atomic_long_dec_and_test(&p->count)) | ||
| 133 | kfree_rcu(p, rcu); | ||
| 134 | } | ||
| 135 | |||
| 136 | static inline struct bsd_acct_struct *to_acct(struct fs_pin *p) | ||
| 137 | { | ||
| 138 | return p ? container_of(p, struct bsd_acct_struct, pin) : NULL; | ||
| 139 | } | ||
| 140 | |||
| 127 | static struct bsd_acct_struct *acct_get(struct pid_namespace *ns) | 141 | static struct bsd_acct_struct *acct_get(struct pid_namespace *ns) |
| 128 | { | 142 | { |
| 129 | struct bsd_acct_struct *res; | 143 | struct bsd_acct_struct *res; |
| 130 | again: | 144 | again: |
| 131 | smp_rmb(); | 145 | smp_rmb(); |
| 132 | rcu_read_lock(); | 146 | rcu_read_lock(); |
| 133 | res = ACCESS_ONCE(ns->bacct); | 147 | res = to_acct(ACCESS_ONCE(ns->bacct)); |
| 134 | if (!res) { | 148 | if (!res) { |
| 135 | rcu_read_unlock(); | 149 | rcu_read_unlock(); |
| 136 | return NULL; | 150 | return NULL; |
| 137 | } | 151 | } |
| 138 | if (!atomic_long_inc_not_zero(&res->pin.count)) { | 152 | if (!atomic_long_inc_not_zero(&res->count)) { |
| 139 | rcu_read_unlock(); | 153 | rcu_read_unlock(); |
| 140 | cpu_relax(); | 154 | cpu_relax(); |
| 141 | goto again; | 155 | goto again; |
| 142 | } | 156 | } |
| 143 | rcu_read_unlock(); | 157 | rcu_read_unlock(); |
| 144 | mutex_lock(&res->lock); | 158 | mutex_lock(&res->lock); |
| 145 | if (!res->ns) { | 159 | if (res != to_acct(ACCESS_ONCE(ns->bacct))) { |
| 146 | mutex_unlock(&res->lock); | 160 | mutex_unlock(&res->lock); |
| 147 | pin_put(&res->pin); | 161 | acct_put(res); |
| 148 | goto again; | 162 | goto again; |
| 149 | } | 163 | } |
| 150 | return res; | 164 | return res; |
| 151 | } | 165 | } |
| 152 | 166 | ||
| 167 | static void acct_pin_kill(struct fs_pin *pin) | ||
| 168 | { | ||
| 169 | struct bsd_acct_struct *acct = to_acct(pin); | ||
| 170 | mutex_lock(&acct->lock); | ||
| 171 | do_acct_process(acct); | ||
| 172 | schedule_work(&acct->work); | ||
| 173 | wait_for_completion(&acct->done); | ||
| 174 | cmpxchg(&acct->ns->bacct, pin, NULL); | ||
| 175 | mutex_unlock(&acct->lock); | ||
| 176 | pin_remove(pin); | ||
| 177 | acct_put(acct); | ||
| 178 | } | ||
| 179 | |||
| 153 | static void close_work(struct work_struct *work) | 180 | static void close_work(struct work_struct *work) |
| 154 | { | 181 | { |
| 155 | struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work); | 182 | struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work); |
| @@ -160,44 +187,13 @@ static void close_work(struct work_struct *work) | |||
| 160 | complete(&acct->done); | 187 | complete(&acct->done); |
| 161 | } | 188 | } |
| 162 | 189 | ||
| 163 | static void acct_kill(struct bsd_acct_struct *acct, | ||
| 164 | struct bsd_acct_struct *new) | ||
| 165 | { | ||
| 166 | if (acct) { | ||
| 167 | struct pid_namespace *ns = acct->ns; | ||
| 168 | do_acct_process(acct); | ||
| 169 | INIT_WORK(&acct->work, close_work); | ||
| 170 | init_completion(&acct->done); | ||
| 171 | schedule_work(&acct->work); | ||
| 172 | wait_for_completion(&acct->done); | ||
| 173 | pin_remove(&acct->pin); | ||
| 174 | ns->bacct = new; | ||
| 175 | acct->ns = NULL; | ||
| 176 | atomic_long_dec(&acct->pin.count); | ||
| 177 | mutex_unlock(&acct->lock); | ||
| 178 | pin_put(&acct->pin); | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | static void acct_pin_kill(struct fs_pin *pin) | ||
| 183 | { | ||
| 184 | struct bsd_acct_struct *acct; | ||
| 185 | acct = container_of(pin, struct bsd_acct_struct, pin); | ||
| 186 | mutex_lock(&acct->lock); | ||
| 187 | if (!acct->ns) { | ||
| 188 | mutex_unlock(&acct->lock); | ||
| 189 | pin_put(pin); | ||
| 190 | acct = NULL; | ||
| 191 | } | ||
| 192 | acct_kill(acct, NULL); | ||
| 193 | } | ||
| 194 | |||
| 195 | static int acct_on(struct filename *pathname) | 190 | static int acct_on(struct filename *pathname) |
| 196 | { | 191 | { |
| 197 | struct file *file; | 192 | struct file *file; |
| 198 | struct vfsmount *mnt, *internal; | 193 | struct vfsmount *mnt, *internal; |
| 199 | struct pid_namespace *ns = task_active_pid_ns(current); | 194 | struct pid_namespace *ns = task_active_pid_ns(current); |
| 200 | struct bsd_acct_struct *acct, *old; | 195 | struct bsd_acct_struct *acct; |
| 196 | struct fs_pin *old; | ||
| 201 | int err; | 197 | int err; |
| 202 | 198 | ||
| 203 | acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL); | 199 | acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL); |
| @@ -238,21 +234,21 @@ static int acct_on(struct filename *pathname) | |||
| 238 | mnt = file->f_path.mnt; | 234 | mnt = file->f_path.mnt; |
| 239 | file->f_path.mnt = internal; | 235 | file->f_path.mnt = internal; |
| 240 | 236 | ||
| 241 | atomic_long_set(&acct->pin.count, 1); | 237 | atomic_long_set(&acct->count, 1); |
| 242 | acct->pin.kill = acct_pin_kill; | 238 | init_fs_pin(&acct->pin, acct_pin_kill); |
| 243 | acct->file = file; | 239 | acct->file = file; |
| 244 | acct->needcheck = jiffies; | 240 | acct->needcheck = jiffies; |
| 245 | acct->ns = ns; | 241 | acct->ns = ns; |
| 246 | mutex_init(&acct->lock); | 242 | mutex_init(&acct->lock); |
| 243 | INIT_WORK(&acct->work, close_work); | ||
| 244 | init_completion(&acct->done); | ||
| 247 | mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */ | 245 | mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */ |
| 248 | pin_insert(&acct->pin, mnt); | 246 | pin_insert(&acct->pin, mnt); |
| 249 | 247 | ||
| 250 | old = acct_get(ns); | 248 | rcu_read_lock(); |
| 251 | if (old) | 249 | old = xchg(&ns->bacct, &acct->pin); |
| 252 | acct_kill(old, acct); | ||
| 253 | else | ||
| 254 | ns->bacct = acct; | ||
| 255 | mutex_unlock(&acct->lock); | 250 | mutex_unlock(&acct->lock); |
| 251 | pin_kill(old); | ||
| 256 | mnt_drop_write(mnt); | 252 | mnt_drop_write(mnt); |
| 257 | mntput(mnt); | 253 | mntput(mnt); |
| 258 | return 0; | 254 | return 0; |
| @@ -288,7 +284,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name) | |||
| 288 | mutex_unlock(&acct_on_mutex); | 284 | mutex_unlock(&acct_on_mutex); |
| 289 | putname(tmp); | 285 | putname(tmp); |
| 290 | } else { | 286 | } else { |
| 291 | acct_kill(acct_get(task_active_pid_ns(current)), NULL); | 287 | rcu_read_lock(); |
| 288 | pin_kill(task_active_pid_ns(current)->bacct); | ||
| 292 | } | 289 | } |
| 293 | 290 | ||
| 294 | return error; | 291 | return error; |
| @@ -296,7 +293,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name) | |||
| 296 | 293 | ||
| 297 | void acct_exit_ns(struct pid_namespace *ns) | 294 | void acct_exit_ns(struct pid_namespace *ns) |
| 298 | { | 295 | { |
| 299 | acct_kill(acct_get(ns), NULL); | 296 | rcu_read_lock(); |
| 297 | pin_kill(ns->bacct); | ||
| 300 | } | 298 | } |
| 301 | 299 | ||
| 302 | /* | 300 | /* |
| @@ -576,7 +574,7 @@ static void slow_acct_process(struct pid_namespace *ns) | |||
| 576 | if (acct) { | 574 | if (acct) { |
| 577 | do_acct_process(acct); | 575 | do_acct_process(acct); |
| 578 | mutex_unlock(&acct->lock); | 576 | mutex_unlock(&acct->lock); |
| 579 | pin_put(&acct->pin); | 577 | acct_put(acct); |
| 580 | } | 578 | } |
| 581 | } | 579 | } |
| 582 | } | 580 | } |
