Skip to content

Commit ec233ed

Browse files
Sukadev Bhattiprolumpe
authored andcommitted
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 <[email protected]> Signed-off-by: Philippe Bergheaud <[email protected]> Signed-off-by: Christophe Lombard <[email protected]> [mpe: Simplify tidr clearing on fork() and ctx switch code] Signed-off-by: Michael Ellerman <[email protected]>
1 parent ece4e51 commit ec233ed

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

arch/powerpc/include/asm/processor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ struct thread_struct {
329329
*/
330330
int dscr_inherit;
331331
unsigned long ppr; /* used to save/restore SMT priority */
332+
unsigned long tidr;
332333
#endif
333334
#ifdef CONFIG_PPC_BOOK3S_64
334335
unsigned long tar;

arch/powerpc/include/asm/switch_to.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,7 @@ static inline void clear_task_ebb(struct task_struct *t)
9191
#endif
9292
}
9393

94+
extern int set_thread_tidr(struct task_struct *t);
95+
extern void clear_thread_tidr(struct task_struct *t);
96+
9497
#endif /* _ASM_POWERPC_SWITCH_TO_H */

arch/powerpc/kernel/process.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,10 @@ static inline void restore_sprs(struct thread_struct *old_thread,
11361136
if (old_thread->tar != new_thread->tar)
11371137
mtspr(SPRN_TAR, new_thread->tar);
11381138
}
1139+
1140+
if (cpu_has_feature(CPU_FTR_ARCH_300) &&
1141+
old_thread->tidr != new_thread->tidr)
1142+
mtspr(SPRN_TIDR, new_thread->tidr);
11391143
#endif
11401144
}
11411145

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

1458+
#ifdef CONFIG_PPC64
1459+
static DEFINE_SPINLOCK(vas_thread_id_lock);
1460+
static DEFINE_IDA(vas_thread_ida);
1461+
1462+
/*
1463+
* We need to assign a unique thread id to each thread in a process.
1464+
*
1465+
* This thread id, referred to as TIDR, and separate from the Linux's tgid,
1466+
* is intended to be used to direct an ASB_Notify from the hardware to the
1467+
* thread, when a suitable event occurs in the system.
1468+
*
1469+
* One such event is a "paste" instruction in the context of Fast Thread
1470+
* Wakeup (aka Core-to-core wake up in the Virtual Accelerator Switchboard
1471+
* (VAS) in POWER9.
1472+
*
1473+
* To get a unique TIDR per process we could simply reuse task_pid_nr() but
1474+
* the problem is that task_pid_nr() is not yet available copy_thread() is
1475+
* called. Fixing that would require changing more intrusive arch-neutral
1476+
* code in code path in copy_process()?.
1477+
*
1478+
* Further, to assign unique TIDRs within each process, we need an atomic
1479+
* field (or an IDR) in task_struct, which again intrudes into the arch-
1480+
* neutral code. So try to assign globally unique TIDRs for now.
1481+
*
1482+
* NOTE: TIDR 0 indicates that the thread does not need a TIDR value.
1483+
* For now, only threads that expect to be notified by the VAS
1484+
* hardware need a TIDR value and we assign values > 0 for those.
1485+
*/
1486+
#define MAX_THREAD_CONTEXT ((1 << 16) - 1)
1487+
static int assign_thread_tidr(void)
1488+
{
1489+
int index;
1490+
int err;
1491+
1492+
again:
1493+
if (!ida_pre_get(&vas_thread_ida, GFP_KERNEL))
1494+
return -ENOMEM;
1495+
1496+
spin_lock(&vas_thread_id_lock);
1497+
err = ida_get_new_above(&vas_thread_ida, 1, &index);
1498+
spin_unlock(&vas_thread_id_lock);
1499+
1500+
if (err == -EAGAIN)
1501+
goto again;
1502+
else if (err)
1503+
return err;
1504+
1505+
if (index > MAX_THREAD_CONTEXT) {
1506+
spin_lock(&vas_thread_id_lock);
1507+
ida_remove(&vas_thread_ida, index);
1508+
spin_unlock(&vas_thread_id_lock);
1509+
return -ENOMEM;
1510+
}
1511+
1512+
return index;
1513+
}
1514+
1515+
static void free_thread_tidr(int id)
1516+
{
1517+
spin_lock(&vas_thread_id_lock);
1518+
ida_remove(&vas_thread_ida, id);
1519+
spin_unlock(&vas_thread_id_lock);
1520+
}
1521+
1522+
/*
1523+
* Clear any TIDR value assigned to this thread.
1524+
*/
1525+
void clear_thread_tidr(struct task_struct *t)
1526+
{
1527+
if (!t->thread.tidr)
1528+
return;
1529+
1530+
if (!cpu_has_feature(CPU_FTR_ARCH_300)) {
1531+
WARN_ON_ONCE(1);
1532+
return;
1533+
}
1534+
1535+
mtspr(SPRN_TIDR, 0);
1536+
free_thread_tidr(t->thread.tidr);
1537+
t->thread.tidr = 0;
1538+
}
1539+
1540+
void arch_release_task_struct(struct task_struct *t)
1541+
{
1542+
clear_thread_tidr(t);
1543+
}
1544+
1545+
/*
1546+
* Assign a unique TIDR (thread id) for task @t and set it in the thread
1547+
* structure. For now, we only support setting TIDR for 'current' task.
1548+
*/
1549+
int set_thread_tidr(struct task_struct *t)
1550+
{
1551+
if (!cpu_has_feature(CPU_FTR_ARCH_300))
1552+
return -EINVAL;
1553+
1554+
if (t != current)
1555+
return -EINVAL;
1556+
1557+
t->thread.tidr = assign_thread_tidr();
1558+
if (t->thread.tidr < 0)
1559+
return t->thread.tidr;
1560+
1561+
mtspr(SPRN_TIDR, t->thread.tidr);
1562+
1563+
return 0;
1564+
}
1565+
1566+
#endif /* CONFIG_PPC64 */
1567+
14541568
void
14551569
release_thread(struct task_struct *t)
14561570
{
@@ -1597,6 +1711,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
15971711
}
15981712
if (cpu_has_feature(CPU_FTR_HAS_PPR))
15991713
p->thread.ppr = INIT_PPR;
1714+
1715+
p->thread.tidr = 0;
16001716
#endif
16011717
kregs->nip = ppc_function_entry(f);
16021718
return 0;

0 commit comments

Comments
 (0)