From: Linus Torvalds Date: Thu, 25 Jul 2019 15:36:29 +0000 (-0700) Subject: Merge branch 'access-creds' X-Git-Tag: v5.3-rc2~33 X-Git-Url: https://repo.jachan.dev/J-linux.git/commitdiff_plain/a29a0a467e2c02fe4287c2d4eff86c9eb6beff0c?hp=-c 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 --- a29a0a467e2c02fe4287c2d4eff86c9eb6beff0c diff --combined kernel/cred.c index f9a0ce66c9c3,153ae369e024..c0a4c12d38b2 --- a/kernel/cred.c +++ b/kernel/cred.c @@@ -144,7 -144,10 +144,10 @@@ void __put_cred(struct cred *cred BUG_ON(cred == current->cred); BUG_ON(cred == current->real_cred); - call_rcu(&cred->rcu, put_cred_rcu); + if (cred->non_rcu) + put_cred_rcu(&cred->rcu); + else + call_rcu(&cred->rcu, put_cred_rcu); } EXPORT_SYMBOL(__put_cred); @@@ -170,11 -173,6 +173,11 @@@ void exit_creds(struct task_struct *tsk validate_creds(cred); alter_cred_subscribers(cred, -1); put_cred(cred); + +#ifdef CONFIG_KEYS_REQUEST_CACHE + key_put(current->cached_requested_key); + current->cached_requested_key = NULL; +#endif } /** @@@ -261,6 -259,7 +264,7 @@@ struct cred *prepare_creds(void old = task->cred; memcpy(new, old, sizeof(struct cred)); + new->non_rcu = 0; atomic_set(&new->usage, 1); set_cred_subscribers(new, 0); get_group_info(new->group_info); @@@ -328,10 -327,6 +332,10 @@@ int copy_creds(struct task_struct *p, u struct cred *new; int ret; +#ifdef CONFIG_KEYS_REQUEST_CACHE + p->cached_requested_key = NULL; +#endif + if ( #ifdef CONFIG_KEYS !p->cred->thread_keyring && @@@ -469,9 -464,9 +473,9 @@@ int commit_creds(struct cred *new /* alter the thread keyring */ if (!uid_eq(new->fsuid, old->fsuid)) - key_fsuid_changed(task); + key_fsuid_changed(new); if (!gid_eq(new->fsgid, old->fsgid)) - key_fsgid_changed(task); + key_fsgid_changed(new); /* do it * RLIMIT_NPROC limits on user->processes have already been checked @@@ -544,7 -539,19 +548,19 @@@ const struct cred *override_creds(cons validate_creds(old); validate_creds(new); - get_cred(new); + + /* + * NOTE! This uses 'get_new_cred()' rather than 'get_cred()'. + * + * That means that we do not clear the 'non_rcu' flag, since + * we are only installing the cred into the thread-synchronous + * '->cred' pointer, not the '->real_cred' pointer that is + * visible to other threads under RCU. + * + * Also note that we did validate_creds() manually, not depending + * on the validation in 'get_cred()'. + */ + get_new_cred((struct cred *)new); alter_cred_subscribers(new, 1); rcu_assign_pointer(current->cred, new); alter_cred_subscribers(old, -1); @@@ -681,6 -688,7 +697,7 @@@ struct cred *prepare_kernel_cred(struc validate_creds(old); *new = *old; + new->non_rcu = 0; atomic_set(&new->usage, 1); set_cred_subscribers(new, 0); get_uid(new->user);