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: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> Signed-off-by: Philippe Bergheaud <felix@linux.vnet.ibm.com> Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> [mpe: Simplify tidr clearing on fork() and ctx switch code] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
This commit is contained in:
parent
ece4e51291
commit
ec233ede4c
|
@ -329,6 +329,7 @@ struct thread_struct {
|
||||||
*/
|
*/
|
||||||
int dscr_inherit;
|
int dscr_inherit;
|
||||||
unsigned long ppr; /* used to save/restore SMT priority */
|
unsigned long ppr; /* used to save/restore SMT priority */
|
||||||
|
unsigned long tidr;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_PPC_BOOK3S_64
|
#ifdef CONFIG_PPC_BOOK3S_64
|
||||||
unsigned long tar;
|
unsigned long tar;
|
||||||
|
|
|
@ -91,4 +91,7 @@ static inline void clear_task_ebb(struct task_struct *t)
|
||||||
#endif
|
#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 */
|
#endif /* _ASM_POWERPC_SWITCH_TO_H */
|
||||||
|
|
|
@ -1136,6 +1136,10 @@ static inline void restore_sprs(struct thread_struct *old_thread,
|
||||||
if (old_thread->tar != new_thread->tar)
|
if (old_thread->tar != new_thread->tar)
|
||||||
mtspr(SPRN_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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1451,6 +1455,116 @@ void flush_thread(void)
|
||||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
|
#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
|
void
|
||||||
release_thread(struct task_struct *t)
|
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))
|
if (cpu_has_feature(CPU_FTR_HAS_PPR))
|
||||||
p->thread.ppr = INIT_PPR;
|
p->thread.ppr = INIT_PPR;
|
||||||
|
|
||||||
|
p->thread.tidr = 0;
|
||||||
#endif
|
#endif
|
||||||
kregs->nip = ppc_function_entry(f);
|
kregs->nip = ppc_function_entry(f);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue