linuxkpi: reduce number of stray mm_struct allocations

Only allocate struct_mm after we checked that other threads do not carry
useful mm_struct.  If they don't, drop process lock, allocate, and recheck.

Note that for M_NOWAIT allocations we could avoid dropping process lock,
but I do not think that this increased complexity is useful.

Reviewed by:	hselasky
Sponsored by:	Mellanox Technologies/NVidia Networking
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2021-03-11 08:48:22 +02:00 committed by Konstantin Belousov
parent 165ba13fb8
commit fad437ba61

View File

@ -48,15 +48,35 @@ static eventhandler_tag linuxkpi_thread_dtor_tag;
static uma_zone_t linux_current_zone;
static uma_zone_t linux_mm_zone;
/* check if another thread already has a mm_struct */
static struct mm_struct *
find_other_mm(struct proc *p)
{
struct thread *td;
struct task_struct *ts;
struct mm_struct *mm;
PROC_LOCK_ASSERT(p, MA_OWNED);
FOREACH_THREAD_IN_PROC(p, td) {
ts = td->td_lkpi_task;
if (ts == NULL)
continue;
mm = ts->mm;
if (mm == NULL)
continue;
/* try to share other mm_struct */
if (atomic_inc_not_zero(&mm->mm_users))
return (mm);
}
return (NULL);
}
int
linux_alloc_current(struct thread *td, int flags)
{
struct proc *proc;
struct thread *td_other;
struct task_struct *ts;
struct task_struct *ts_other;
struct mm_struct *mm;
struct mm_struct *mm_other;
struct mm_struct *mm, *mm_other;
MPASS(td->td_lkpi_task == NULL);
@ -71,14 +91,7 @@ linux_alloc_current(struct thread *td, int flags)
panic("linux_alloc_current: failed to allocate task");
return (ENOMEM);
}
mm = uma_zalloc(linux_mm_zone, flags | M_ZERO);
if (mm == NULL) {
if ((flags & (M_WAITOK | M_NOWAIT)) == M_WAITOK)
panic("linux_alloc_current: failed to allocate mm");
uma_zfree(linux_current_zone, mm);
return (ENOMEM);
}
mm = NULL;
/* setup new task structure */
atomic_set(&ts->kthread_flags, 0);
@ -93,35 +106,37 @@ linux_alloc_current(struct thread *td, int flags)
proc = td->td_proc;
/* check if another thread already has a mm_struct */
PROC_LOCK(proc);
FOREACH_THREAD_IN_PROC(proc, td_other) {
ts_other = td_other->td_lkpi_task;
if (ts_other == NULL)
continue;
mm_other = ts_other->mm;
if (mm_other == NULL)
continue;
/* try to share other mm_struct */
if (atomic_inc_not_zero(&mm_other->mm_users)) {
/* set mm_struct pointer */
ts->mm = mm_other;
break;
}
}
mm_other = find_other_mm(proc);
/* use allocated mm_struct as a fallback */
if (ts->mm == NULL) {
/* setup new mm_struct */
init_rwsem(&mm->mmap_sem);
atomic_set(&mm->mm_count, 1);
atomic_set(&mm->mm_users, 1);
/* set mm_struct pointer */
ts->mm = mm;
/* clear pointer to not free memory */
mm = NULL;
if (mm_other == NULL) {
PROC_UNLOCK(proc);
mm = uma_zalloc(linux_mm_zone, flags | M_ZERO);
if (mm == NULL) {
if ((flags & (M_WAITOK | M_NOWAIT)) == M_WAITOK)
panic(
"linux_alloc_current: failed to allocate mm");
uma_zfree(linux_current_zone, mm);
return (ENOMEM);
}
PROC_LOCK(proc);
mm_other = find_other_mm(proc);
if (mm_other == NULL) {
/* setup new mm_struct */
init_rwsem(&mm->mmap_sem);
atomic_set(&mm->mm_count, 1);
atomic_set(&mm->mm_users, 1);
/* set mm_struct pointer */
ts->mm = mm;
/* clear pointer to not free memory */
mm = NULL;
} else {
ts->mm = mm_other;
}
} else {
ts->mm = mm_other;
}
/* store pointer to task struct */