summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-07-25 11:36:29 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-07-25 11:36:29 -0400
commita29a0a467e2c02fe4287c2d4eff86c9eb6beff0c (patch)
tree069e48d9d1520b54805861d54cbac6ac53b93021
parentbed38c3e2dca01b358a62b5e73b46e875742fd75 (diff)
parentd7852fbd0f0423937fa287a598bfde188bb68c22 (diff)
Merge branch 'access-creds'
The access() (and faccessat()) credentials change can cause an unnecessary load on the RCU machinery because every access() call ends up freeing the temporary access credential using RCU. This isn't really noticeable on small machines, but if you have hundreds of cores you can cause huge slowdowns due to RCU storms. It's easy to avoid: the temporary access crededntials aren't actually normally accessed using RCU at all, so we can avoid the whole issue by just marking them as such. * access-creds: access: avoid the RCU grace period for the temporary subjective credentials
-rw-r--r--fs/open.c19
-rw-r--r--include/linux/cred.h8
-rw-r--r--kernel/cred.c21
3 files changed, 45 insertions, 3 deletions
diff --git a/fs/open.c b/fs/open.c
index b5b80469b93d..a59abe3c669a 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -374,6 +374,25 @@ long do_faccessat(int dfd, const char __user *filename, int mode)
374 override_cred->cap_permitted; 374 override_cred->cap_permitted;
375 } 375 }
376 376
377 /*
378 * The new set of credentials can *only* be used in
379 * task-synchronous circumstances, and does not need
380 * RCU freeing, unless somebody then takes a separate
381 * reference to it.
382 *
383 * NOTE! This is _only_ true because this credential
384 * is used purely for override_creds() that installs
385 * it as the subjective cred. Other threads will be
386 * accessing ->real_cred, not the subjective cred.
387 *
388 * If somebody _does_ make a copy of this (using the
389 * 'get_current_cred()' function), that will clear the
390 * non_rcu field, because now that other user may be
391 * expecting RCU freeing. But normal thread-synchronous
392 * cred accesses will keep things non-RCY.
393 */
394 override_cred->non_rcu = 1;
395
377 old_cred = override_creds(override_cred); 396 old_cred = override_creds(override_cred);
378retry: 397retry:
379 res = user_path_at(dfd, filename, lookup_flags, &path); 398 res = user_path_at(dfd, filename, lookup_flags, &path);
diff --git a/include/linux/cred.h b/include/linux/cred.h
index 7eb43a038330..f7a30e0099be 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -145,7 +145,11 @@ struct cred {
145 struct user_struct *user; /* real user ID subscription */ 145 struct user_struct *user; /* real user ID subscription */
146 struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */ 146 struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
147 struct group_info *group_info; /* supplementary groups for euid/fsgid */ 147 struct group_info *group_info; /* supplementary groups for euid/fsgid */
148 struct rcu_head rcu; /* RCU deletion hook */ 148 /* RCU deletion */
149 union {
150 int non_rcu; /* Can we skip RCU deletion? */
151 struct rcu_head rcu; /* RCU deletion hook */
152 };
149} __randomize_layout; 153} __randomize_layout;
150 154
151extern void __put_cred(struct cred *); 155extern void __put_cred(struct cred *);
@@ -246,6 +250,7 @@ static inline const struct cred *get_cred(const struct cred *cred)
246 if (!cred) 250 if (!cred)
247 return cred; 251 return cred;
248 validate_creds(cred); 252 validate_creds(cred);
253 nonconst_cred->non_rcu = 0;
249 return get_new_cred(nonconst_cred); 254 return get_new_cred(nonconst_cred);
250} 255}
251 256
@@ -257,6 +262,7 @@ static inline const struct cred *get_cred_rcu(const struct cred *cred)
257 if (!atomic_inc_not_zero(&nonconst_cred->usage)) 262 if (!atomic_inc_not_zero(&nonconst_cred->usage))
258 return NULL; 263 return NULL;
259 validate_creds(cred); 264 validate_creds(cred);
265 nonconst_cred->non_rcu = 0;
260 return cred; 266 return cred;
261} 267}
262 268
diff --git a/kernel/cred.c b/kernel/cred.c
index f9a0ce66c9c3..c0a4c12d38b2 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -144,7 +144,10 @@ void __put_cred(struct cred *cred)
144 BUG_ON(cred == current->cred); 144 BUG_ON(cred == current->cred);
145 BUG_ON(cred == current->real_cred); 145 BUG_ON(cred == current->real_cred);
146 146
147 call_rcu(&cred->rcu, put_cred_rcu); 147 if (cred->non_rcu)
148 put_cred_rcu(&cred->rcu);
149 else
150 call_rcu(&cred->rcu, put_cred_rcu);
148} 151}
149EXPORT_SYMBOL(__put_cred); 152EXPORT_SYMBOL(__put_cred);
150 153
@@ -261,6 +264,7 @@ struct cred *prepare_creds(void)
261 old = task->cred; 264 old = task->cred;
262 memcpy(new, old, sizeof(struct cred)); 265 memcpy(new, old, sizeof(struct cred));
263 266
267 new->non_rcu = 0;
264 atomic_set(&new->usage, 1); 268 atomic_set(&new->usage, 1);
265 set_cred_subscribers(new, 0); 269 set_cred_subscribers(new, 0);
266 get_group_info(new->group_info); 270 get_group_info(new->group_info);
@@ -544,7 +548,19 @@ const struct cred *override_creds(const struct cred *new)
544 548
545 validate_creds(old); 549 validate_creds(old);
546 validate_creds(new); 550 validate_creds(new);
547 get_cred(new); 551
552 /*
553 * NOTE! This uses 'get_new_cred()' rather than 'get_cred()'.
554 *
555 * That means that we do not clear the 'non_rcu' flag, since
556 * we are only installing the cred into the thread-synchronous
557 * '->cred' pointer, not the '->real_cred' pointer that is
558 * visible to other threads under RCU.
559 *
560 * Also note that we did validate_creds() manually, not depending
561 * on the validation in 'get_cred()'.
562 */
563 get_new_cred((struct cred *)new);
548 alter_cred_subscribers(new, 1); 564 alter_cred_subscribers(new, 1);
549 rcu_assign_pointer(current->cred, new); 565 rcu_assign_pointer(current->cred, new);
550 alter_cred_subscribers(old, -1); 566 alter_cred_subscribers(old, -1);
@@ -681,6 +697,7 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
681 validate_creds(old); 697 validate_creds(old);
682 698
683 *new = *old; 699 *new = *old;
700 new->non_rcu = 0;
684 atomic_set(&new->usage, 1); 701 atomic_set(&new->usage, 1);
685 set_cred_subscribers(new, 0); 702 set_cred_subscribers(new, 0);
686 get_uid(new->user); 703 get_uid(new->user);