Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ec233ede authored by Sukadev Bhattiprolu's avatar Sukadev Bhattiprolu Committed by Michael Ellerman
Browse files

powerpc: Add support for setting SPRN_TIDR



We need the SPRN_TIDR to be set for use with fast thread-wakeup (core-
to-core wakeup) and also with CAPI.

Each thread in a process needs to have a unique id within the process.
But for now, we assign globally unique thread ids to all threads in
the system.

Signed-off-by: default avatarSukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
Signed-off-by: default avatarPhilippe Bergheaud <felix@linux.vnet.ibm.com>
Signed-off-by: default avatarChristophe Lombard <clombard@linux.vnet.ibm.com>
[mpe: Simplify tidr clearing on fork() and ctx switch code]
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent ece4e512
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -329,6 +329,7 @@ struct thread_struct {
	 */
	int		dscr_inherit;
	unsigned long	ppr;	/* used to save/restore SMT priority */
	unsigned long	tidr;
#endif
#ifdef CONFIG_PPC_BOOK3S_64
	unsigned long	tar;
+3 −0
Original line number Diff line number Diff line
@@ -91,4 +91,7 @@ static inline void clear_task_ebb(struct task_struct *t)
#endif
}

extern int set_thread_tidr(struct task_struct *t);
extern void clear_thread_tidr(struct task_struct *t);

#endif /* _ASM_POWERPC_SWITCH_TO_H */
+116 −0
Original line number Diff line number Diff line
@@ -1136,6 +1136,10 @@ static inline void restore_sprs(struct thread_struct *old_thread,
		if (old_thread->tar != new_thread->tar)
			mtspr(SPRN_TAR, new_thread->tar);
	}

	if (cpu_has_feature(CPU_FTR_ARCH_300) &&
	    old_thread->tidr != new_thread->tidr)
		mtspr(SPRN_TIDR, new_thread->tidr);
#endif
}

@@ -1451,6 +1455,116 @@ void flush_thread(void)
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
}

#ifdef CONFIG_PPC64
static DEFINE_SPINLOCK(vas_thread_id_lock);
static DEFINE_IDA(vas_thread_ida);

/*
 * We need to assign a unique thread id to each thread in a process.
 *
 * This thread id, referred to as TIDR, and separate from the Linux's tgid,
 * is intended to be used to direct an ASB_Notify from the hardware to the
 * thread, when a suitable event occurs in the system.
 *
 * One such event is a "paste" instruction in the context of Fast Thread
 * Wakeup (aka Core-to-core wake up in the Virtual Accelerator Switchboard
 * (VAS) in POWER9.
 *
 * To get a unique TIDR per process we could simply reuse task_pid_nr() but
 * the problem is that task_pid_nr() is not yet available copy_thread() is
 * called. Fixing that would require changing more intrusive arch-neutral
 * code in code path in copy_process()?.
 *
 * Further, to assign unique TIDRs within each process, we need an atomic
 * field (or an IDR) in task_struct, which again intrudes into the arch-
 * neutral code. So try to assign globally unique TIDRs for now.
 *
 * NOTE: TIDR 0 indicates that the thread does not need a TIDR value.
 *	 For now, only threads that expect to be notified by the VAS
 *	 hardware need a TIDR value and we assign values > 0 for those.
 */
#define MAX_THREAD_CONTEXT	((1 << 16) - 1)
static int assign_thread_tidr(void)
{
	int index;
	int err;

again:
	if (!ida_pre_get(&vas_thread_ida, GFP_KERNEL))
		return -ENOMEM;

	spin_lock(&vas_thread_id_lock);
	err = ida_get_new_above(&vas_thread_ida, 1, &index);
	spin_unlock(&vas_thread_id_lock);

	if (err == -EAGAIN)
		goto again;
	else if (err)
		return err;

	if (index > MAX_THREAD_CONTEXT) {
		spin_lock(&vas_thread_id_lock);
		ida_remove(&vas_thread_ida, index);
		spin_unlock(&vas_thread_id_lock);
		return -ENOMEM;
	}

	return index;
}

static void free_thread_tidr(int id)
{
	spin_lock(&vas_thread_id_lock);
	ida_remove(&vas_thread_ida, id);
	spin_unlock(&vas_thread_id_lock);
}

/*
 * Clear any TIDR value assigned to this thread.
 */
void clear_thread_tidr(struct task_struct *t)
{
	if (!t->thread.tidr)
		return;

	if (!cpu_has_feature(CPU_FTR_ARCH_300)) {
		WARN_ON_ONCE(1);
		return;
	}

	mtspr(SPRN_TIDR, 0);
	free_thread_tidr(t->thread.tidr);
	t->thread.tidr = 0;
}

void arch_release_task_struct(struct task_struct *t)
{
	clear_thread_tidr(t);
}

/*
 * Assign a unique TIDR (thread id) for task @t and set it in the thread
 * structure. For now, we only support setting TIDR for 'current' task.
 */
int set_thread_tidr(struct task_struct *t)
{
	if (!cpu_has_feature(CPU_FTR_ARCH_300))
		return -EINVAL;

	if (t != current)
		return -EINVAL;

	t->thread.tidr = assign_thread_tidr();
	if (t->thread.tidr < 0)
		return t->thread.tidr;

	mtspr(SPRN_TIDR, t->thread.tidr);

	return 0;
}

#endif /* CONFIG_PPC64 */

void
release_thread(struct task_struct *t)
{
@@ -1597,6 +1711,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
	}
	if (cpu_has_feature(CPU_FTR_HAS_PPR))
		p->thread.ppr = INIT_PPR;

	p->thread.tidr = 0;
#endif
	kregs->nip = ppc_function_entry(f);
	return 0;