diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2014-08-07 07:04:28 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2014-08-07 14:40:08 -0400 |
commit | 2798d4ce61601808b965253d60624bbf201b51b0 (patch) | |
tree | e938df0fd8040a69da38a277c753aac42272c292 | |
parent | 215752fce31c80f3b3a1530bc7cddb3ba6a69b3a (diff) |
acct: get rid of acct_lock for acct->count
* make acct->count atomic and acct freeing - rcu-delayed.
* instead of grabbing acct_lock around the places where we take a reference,
do that under rcu_read_lock() with atomic_long_inc_not_zero().
* have the new acct locked before making ns->bacct point to it
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | kernel/acct.c | 85 |
1 files changed, 52 insertions, 33 deletions
diff --git a/kernel/acct.c b/kernel/acct.c index 21fbb3c27c2a..6fd375f15626 100644 --- a/kernel/acct.c +++ b/kernel/acct.c | |||
@@ -79,9 +79,14 @@ int acct_parm[3] = {4, 2, 30}; | |||
79 | static void do_acct_process(struct bsd_acct_struct *acct); | 79 | static void do_acct_process(struct bsd_acct_struct *acct); |
80 | 80 | ||
81 | struct bsd_acct_struct { | 81 | struct bsd_acct_struct { |
82 | long count; | 82 | atomic_long_t count; |
83 | struct hlist_node s_list; | 83 | union { |
84 | struct hlist_node m_list; | 84 | struct { |
85 | struct hlist_node s_list; | ||
86 | struct hlist_node m_list; | ||
87 | }; | ||
88 | struct rcu_head rcu; | ||
89 | }; | ||
85 | struct mutex lock; | 90 | struct mutex lock; |
86 | int active; | 91 | int active; |
87 | unsigned long needcheck; | 92 | unsigned long needcheck; |
@@ -89,6 +94,11 @@ struct bsd_acct_struct { | |||
89 | struct pid_namespace *ns; | 94 | struct pid_namespace *ns; |
90 | }; | 95 | }; |
91 | 96 | ||
97 | static void acct_free_rcu(struct rcu_head *head) | ||
98 | { | ||
99 | kfree(container_of(head, struct bsd_acct_struct, rcu)); | ||
100 | } | ||
101 | |||
92 | static DEFINE_SPINLOCK(acct_lock); | 102 | static DEFINE_SPINLOCK(acct_lock); |
93 | 103 | ||
94 | /* | 104 | /* |
@@ -128,22 +138,22 @@ out: | |||
128 | 138 | ||
129 | static void acct_put(struct bsd_acct_struct *p) | 139 | static void acct_put(struct bsd_acct_struct *p) |
130 | { | 140 | { |
131 | spin_lock(&acct_lock); | 141 | if (atomic_long_dec_and_test(&p->count)) |
132 | if (!--p->count) | 142 | call_rcu(&p->rcu, acct_free_rcu); |
133 | kfree(p); | ||
134 | spin_unlock(&acct_lock); | ||
135 | } | 143 | } |
136 | 144 | ||
137 | static struct bsd_acct_struct *__acct_get(struct bsd_acct_struct *res) | 145 | static struct bsd_acct_struct *__acct_get(struct bsd_acct_struct *res) |
138 | { | 146 | { |
139 | res->count++; | 147 | if (!atomic_long_inc_not_zero(&res->count)) { |
140 | spin_unlock(&acct_lock); | 148 | rcu_read_unlock(); |
149 | cpu_relax(); | ||
150 | return NULL; | ||
151 | } | ||
152 | rcu_read_unlock(); | ||
141 | mutex_lock(&res->lock); | 153 | mutex_lock(&res->lock); |
142 | if (!res->ns) { | 154 | if (!res->ns) { |
143 | mutex_unlock(&res->lock); | 155 | mutex_unlock(&res->lock); |
144 | spin_lock(&acct_lock); | 156 | acct_put(res); |
145 | if (!--res->count) | ||
146 | kfree(res); | ||
147 | return NULL; | 157 | return NULL; |
148 | } | 158 | } |
149 | return res; | 159 | return res; |
@@ -152,13 +162,15 @@ static struct bsd_acct_struct *__acct_get(struct bsd_acct_struct *res) | |||
152 | static struct bsd_acct_struct *acct_get(struct pid_namespace *ns) | 162 | static struct bsd_acct_struct *acct_get(struct pid_namespace *ns) |
153 | { | 163 | { |
154 | struct bsd_acct_struct *res; | 164 | struct bsd_acct_struct *res; |
155 | spin_lock(&acct_lock); | ||
156 | again: | 165 | again: |
157 | if (!ns->bacct) { | 166 | smp_rmb(); |
158 | spin_unlock(&acct_lock); | 167 | rcu_read_lock(); |
168 | res = ACCESS_ONCE(ns->bacct); | ||
169 | if (!res) { | ||
170 | rcu_read_unlock(); | ||
159 | return NULL; | 171 | return NULL; |
160 | } | 172 | } |
161 | res = __acct_get(ns->bacct); | 173 | res = __acct_get(res); |
162 | if (!res) | 174 | if (!res) |
163 | goto again; | 175 | goto again; |
164 | return res; | 176 | return res; |
@@ -170,26 +182,27 @@ static void acct_kill(struct bsd_acct_struct *acct, | |||
170 | if (acct) { | 182 | if (acct) { |
171 | struct file *file = acct->file; | 183 | struct file *file = acct->file; |
172 | struct pid_namespace *ns = acct->ns; | 184 | struct pid_namespace *ns = acct->ns; |
185 | do_acct_process(acct); | ||
186 | mnt_unpin(file->f_path.mnt); | ||
187 | filp_close(file, NULL); | ||
173 | spin_lock(&acct_lock); | 188 | spin_lock(&acct_lock); |
174 | hlist_del(&acct->m_list); | 189 | hlist_del(&acct->m_list); |
175 | hlist_del(&acct->s_list); | 190 | hlist_del(&acct->s_list); |
176 | mnt_unpin(file->f_path.mnt); | ||
177 | spin_unlock(&acct_lock); | 191 | spin_unlock(&acct_lock); |
178 | do_acct_process(acct); | ||
179 | filp_close(file, NULL); | ||
180 | spin_lock(&acct_lock); | ||
181 | ns->bacct = new; | 192 | ns->bacct = new; |
182 | if (new) { | 193 | if (new) { |
183 | struct vfsmount *m = new->file->f_path.mnt; | 194 | struct vfsmount *m = new->file->f_path.mnt; |
184 | mnt_pin(m); | 195 | mnt_pin(m); |
196 | spin_lock(&acct_lock); | ||
185 | hlist_add_head(&new->s_list, &m->mnt_sb->s_pins); | 197 | hlist_add_head(&new->s_list, &m->mnt_sb->s_pins); |
186 | hlist_add_head(&new->m_list, &real_mount(m)->mnt_pins); | 198 | hlist_add_head(&new->m_list, &real_mount(m)->mnt_pins); |
199 | spin_unlock(&acct_lock); | ||
200 | mutex_unlock(&new->lock); | ||
187 | } | 201 | } |
188 | acct->ns = NULL; | 202 | acct->ns = NULL; |
203 | atomic_long_dec(&acct->count); | ||
189 | mutex_unlock(&acct->lock); | 204 | mutex_unlock(&acct->lock); |
190 | if (!(acct->count -= 2)) | 205 | acct_put(acct); |
191 | kfree(acct); | ||
192 | spin_unlock(&acct_lock); | ||
193 | } | 206 | } |
194 | } | 207 | } |
195 | 208 | ||
@@ -223,7 +236,7 @@ static int acct_on(struct filename *pathname) | |||
223 | return -EIO; | 236 | return -EIO; |
224 | } | 237 | } |
225 | 238 | ||
226 | acct->count = 1; | 239 | atomic_long_set(&acct->count, 1); |
227 | acct->file = file; | 240 | acct->file = file; |
228 | acct->needcheck = jiffies; | 241 | acct->needcheck = jiffies; |
229 | acct->ns = ns; | 242 | acct->ns = ns; |
@@ -231,15 +244,17 @@ static int acct_on(struct filename *pathname) | |||
231 | mnt = file->f_path.mnt; | 244 | mnt = file->f_path.mnt; |
232 | 245 | ||
233 | old = acct_get(ns); | 246 | old = acct_get(ns); |
247 | mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */ | ||
234 | if (old) { | 248 | if (old) { |
235 | acct_kill(old, acct); | 249 | acct_kill(old, acct); |
236 | } else { | 250 | } else { |
237 | spin_lock(&acct_lock); | ||
238 | ns->bacct = acct; | 251 | ns->bacct = acct; |
252 | spin_lock(&acct_lock); | ||
239 | mnt_pin(mnt); | 253 | mnt_pin(mnt); |
240 | hlist_add_head(&acct->s_list, &mnt->mnt_sb->s_pins); | 254 | hlist_add_head(&acct->s_list, &mnt->mnt_sb->s_pins); |
241 | hlist_add_head(&acct->m_list, &real_mount(mnt)->mnt_pins); | 255 | hlist_add_head(&acct->m_list, &real_mount(mnt)->mnt_pins); |
242 | spin_unlock(&acct_lock); | 256 | spin_unlock(&acct_lock); |
257 | mutex_unlock(&acct->lock); | ||
243 | } | 258 | } |
244 | mntput(mnt); /* it's pinned, now give up active reference */ | 259 | mntput(mnt); /* it's pinned, now give up active reference */ |
245 | return 0; | 260 | return 0; |
@@ -282,28 +297,32 @@ SYSCALL_DEFINE1(acct, const char __user *, name) | |||
282 | 297 | ||
283 | void acct_auto_close_mnt(struct hlist_head *list) | 298 | void acct_auto_close_mnt(struct hlist_head *list) |
284 | { | 299 | { |
300 | rcu_read_lock(); | ||
285 | while (1) { | 301 | while (1) { |
286 | spin_lock(&acct_lock); | 302 | struct hlist_node *p = ACCESS_ONCE(list->first); |
287 | if (!list->first) | 303 | if (!p) |
288 | break; | 304 | break; |
289 | acct_kill(__acct_get(hlist_entry(list->first, | 305 | acct_kill(__acct_get(hlist_entry(p, |
290 | struct bsd_acct_struct, | 306 | struct bsd_acct_struct, |
291 | m_list)), NULL); | 307 | m_list)), NULL); |
308 | rcu_read_lock(); | ||
292 | } | 309 | } |
293 | spin_unlock(&acct_lock); | 310 | rcu_read_unlock(); |
294 | } | 311 | } |
295 | 312 | ||
296 | void acct_auto_close(struct hlist_head *list) | 313 | void acct_auto_close(struct hlist_head *list) |
297 | { | 314 | { |
315 | rcu_read_lock(); | ||
298 | while (1) { | 316 | while (1) { |
299 | spin_lock(&acct_lock); | 317 | struct hlist_node *p = ACCESS_ONCE(list->first); |
300 | if (!list->first) | 318 | if (!p) |
301 | break; | 319 | break; |
302 | acct_kill(__acct_get(hlist_entry(list->first, | 320 | acct_kill(__acct_get(hlist_entry(p, |
303 | struct bsd_acct_struct, | 321 | struct bsd_acct_struct, |
304 | s_list)), NULL); | 322 | s_list)), NULL); |
323 | rcu_read_lock(); | ||
305 | } | 324 | } |
306 | spin_unlock(&acct_lock); | 325 | rcu_read_unlock(); |
307 | } | 326 | } |
308 | 327 | ||
309 | void acct_exit_ns(struct pid_namespace *ns) | 328 | void acct_exit_ns(struct pid_namespace *ns) |