diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2015-01-10 17:53:21 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2015-01-25 23:17:28 -0500 |
commit | 59eda0e07f43c950d31756213b607af673e551f0 (patch) | |
tree | f40f7b67133576c36a65a4cba9aca5df68d00f34 /kernel | |
parent | fdab684d7202774bfd8762d4a656a553b787c8ec (diff) |
new fs_pin killing logics
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/acct.c | 81 |
1 files changed, 34 insertions, 47 deletions
diff --git a/kernel/acct.c b/kernel/acct.c index cf6588ab517b..e6c10d1a4058 100644 --- a/kernel/acct.c +++ b/kernel/acct.c | |||
@@ -76,7 +76,6 @@ 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; |
@@ -91,6 +90,8 @@ struct bsd_acct_struct { | |||
91 | struct completion done; | 90 | struct completion done; |
92 | }; | 91 | }; |
93 | 92 | ||
93 | static void do_acct_process(struct bsd_acct_struct *acct); | ||
94 | |||
94 | /* | 95 | /* |
95 | * Check the amount of free space and suspend/resume accordingly. | 96 | * Check the amount of free space and suspend/resume accordingly. |
96 | */ | 97 | */ |
@@ -132,13 +133,18 @@ static void acct_put(struct bsd_acct_struct *p) | |||
132 | kfree_rcu(p, rcu); | 133 | kfree_rcu(p, rcu); |
133 | } | 134 | } |
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 | |||
135 | static struct bsd_acct_struct *acct_get(struct pid_namespace *ns) | 141 | static struct bsd_acct_struct *acct_get(struct pid_namespace *ns) |
136 | { | 142 | { |
137 | struct bsd_acct_struct *res; | 143 | struct bsd_acct_struct *res; |
138 | again: | 144 | again: |
139 | smp_rmb(); | 145 | smp_rmb(); |
140 | rcu_read_lock(); | 146 | rcu_read_lock(); |
141 | res = ACCESS_ONCE(ns->bacct); | 147 | res = to_acct(ACCESS_ONCE(ns->bacct)); |
142 | if (!res) { | 148 | if (!res) { |
143 | rcu_read_unlock(); | 149 | rcu_read_unlock(); |
144 | return NULL; | 150 | return NULL; |
@@ -150,7 +156,7 @@ again: | |||
150 | } | 156 | } |
151 | rcu_read_unlock(); | 157 | rcu_read_unlock(); |
152 | mutex_lock(&res->lock); | 158 | mutex_lock(&res->lock); |
153 | if (!res->ns) { | 159 | if (res != to_acct(ACCESS_ONCE(ns->bacct))) { |
154 | mutex_unlock(&res->lock); | 160 | mutex_unlock(&res->lock); |
155 | acct_put(res); | 161 | acct_put(res); |
156 | goto again; | 162 | goto again; |
@@ -158,6 +164,19 @@ again: | |||
158 | return res; | 164 | return res; |
159 | } | 165 | } |
160 | 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 | |||
161 | static void close_work(struct work_struct *work) | 180 | static void close_work(struct work_struct *work) |
162 | { | 181 | { |
163 | 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); |
@@ -168,49 +187,13 @@ static void close_work(struct work_struct *work) | |||
168 | complete(&acct->done); | 187 | complete(&acct->done); |
169 | } | 188 | } |
170 | 189 | ||
171 | static void acct_kill(struct bsd_acct_struct *acct) | ||
172 | { | ||
173 | if (acct) { | ||
174 | struct pid_namespace *ns = acct->ns; | ||
175 | do_acct_process(acct); | ||
176 | INIT_WORK(&acct->work, close_work); | ||
177 | init_completion(&acct->done); | ||
178 | schedule_work(&acct->work); | ||
179 | wait_for_completion(&acct->done); | ||
180 | pin_remove(&acct->pin); | ||
181 | cmpxchg(&ns->bacct, acct, NULL); | ||
182 | acct->ns = NULL; | ||
183 | atomic_long_dec(&acct->count); | ||
184 | mutex_unlock(&acct->lock); | ||
185 | acct_put(acct); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | static void acct_pin_kill(struct fs_pin *pin) | ||
190 | { | ||
191 | struct bsd_acct_struct *acct; | ||
192 | acct = container_of(pin, struct bsd_acct_struct, pin); | ||
193 | if (!atomic_long_inc_not_zero(&acct->count)) { | ||
194 | rcu_read_unlock(); | ||
195 | cpu_relax(); | ||
196 | return; | ||
197 | } | ||
198 | rcu_read_unlock(); | ||
199 | mutex_lock(&acct->lock); | ||
200 | if (!acct->ns) { | ||
201 | mutex_unlock(&acct->lock); | ||
202 | acct_put(acct); | ||
203 | acct = NULL; | ||
204 | } | ||
205 | acct_kill(acct); | ||
206 | } | ||
207 | |||
208 | static int acct_on(struct filename *pathname) | 190 | static int acct_on(struct filename *pathname) |
209 | { | 191 | { |
210 | struct file *file; | 192 | struct file *file; |
211 | struct vfsmount *mnt, *internal; | 193 | struct vfsmount *mnt, *internal; |
212 | struct pid_namespace *ns = task_active_pid_ns(current); | 194 | struct pid_namespace *ns = task_active_pid_ns(current); |
213 | struct bsd_acct_struct *acct, *old; | 195 | struct bsd_acct_struct *acct; |
196 | struct fs_pin *old; | ||
214 | int err; | 197 | int err; |
215 | 198 | ||
216 | acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL); | 199 | acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL); |
@@ -252,18 +235,20 @@ static int acct_on(struct filename *pathname) | |||
252 | file->f_path.mnt = internal; | 235 | file->f_path.mnt = internal; |
253 | 236 | ||
254 | atomic_long_set(&acct->count, 1); | 237 | atomic_long_set(&acct->count, 1); |
255 | acct->pin.kill = acct_pin_kill; | 238 | init_fs_pin(&acct->pin, acct_pin_kill); |
256 | acct->file = file; | 239 | acct->file = file; |
257 | acct->needcheck = jiffies; | 240 | acct->needcheck = jiffies; |
258 | acct->ns = ns; | 241 | acct->ns = ns; |
259 | mutex_init(&acct->lock); | 242 | mutex_init(&acct->lock); |
243 | INIT_WORK(&acct->work, close_work); | ||
244 | init_completion(&acct->done); | ||
260 | mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */ | 245 | mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */ |
261 | pin_insert(&acct->pin, mnt); | 246 | pin_insert(&acct->pin, mnt); |
262 | 247 | ||
263 | old = acct_get(ns); | 248 | rcu_read_lock(); |
264 | ns->bacct = acct; | 249 | old = xchg(&ns->bacct, &acct->pin); |
265 | acct_kill(old); | ||
266 | mutex_unlock(&acct->lock); | 250 | mutex_unlock(&acct->lock); |
251 | pin_kill(old); | ||
267 | mnt_drop_write(mnt); | 252 | mnt_drop_write(mnt); |
268 | mntput(mnt); | 253 | mntput(mnt); |
269 | return 0; | 254 | return 0; |
@@ -299,7 +284,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name) | |||
299 | mutex_unlock(&acct_on_mutex); | 284 | mutex_unlock(&acct_on_mutex); |
300 | putname(tmp); | 285 | putname(tmp); |
301 | } else { | 286 | } else { |
302 | acct_kill(acct_get(task_active_pid_ns(current))); | 287 | rcu_read_lock(); |
288 | pin_kill(task_active_pid_ns(current)->bacct); | ||
303 | } | 289 | } |
304 | 290 | ||
305 | return error; | 291 | return error; |
@@ -307,7 +293,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name) | |||
307 | 293 | ||
308 | void acct_exit_ns(struct pid_namespace *ns) | 294 | void acct_exit_ns(struct pid_namespace *ns) |
309 | { | 295 | { |
310 | acct_kill(acct_get(ns)); | 296 | rcu_read_lock(); |
297 | pin_kill(ns->bacct); | ||
311 | } | 298 | } |
312 | 299 | ||
313 | /* | 300 | /* |