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 | } |