@@ -179,6 +179,34 @@ static struct bb_info *alloc_bb_info(void)
return bb_info;
}
+static struct bb_info *get_bb_info(struct hwb_private_data *pdata, u8 cmg, u8 bb)
+{
+ struct bb_info *bb_info;
+
+ if (cmg >= _hwinfo.num_cmg || bb >= _hwinfo.num_bb) {
+ pr_err("CMG/BB number is invalid: %u/%u\n", cmg, bb);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!test_bit(bb, &_hwinfo.used_bb_bmap[cmg])) {
+ pr_err("BB is not allocated: %u/%u\n", cmg, bb);
+ return ERR_PTR(-ENOENT);
+ }
+
+ spin_lock(&pdata->list_lock);
+ list_for_each_entry(bb_info, &pdata->bb_list, node) {
+ if (bb_info->cmg == cmg && bb_info->bb == bb) {
+ kref_get(&bb_info->kref);
+ spin_unlock(&pdata->list_lock);
+ return bb_info;
+ }
+ }
+ spin_unlock(&pdata->list_lock);
+
+ pr_err("BB is not allocated by this process: %u/%u\n", cmg, bb);
+ return ERR_PTR(-EPERM);
+}
+
static inline void put_bb_info(struct bb_info *bb_info)
{
kref_put(&bb_info->kref, free_bb_info);
@@ -347,6 +375,162 @@ static int ioc_bb_alloc(struct file *filp, void __user *argp)
return ret;
}
+static bool is_bound_only_one_pe(void)
+{
+ if (current->nr_cpus_allowed == 1)
+ return true;
+
+ pr_err("Thread must be bound to one PE between assign and unassign\n");
+ return false;
+}
+
+/* Check if this PE can be assignable and set window number to be used to @bw_ctl->window */
+static int is_bw_assignable(struct bb_info *bb_info, struct fujitsu_hwb_ioc_bw_ctl *bw_ctl, int cpu)
+{
+ int i;
+
+ if (!cpumask_test_cpu(cpu, bb_info->pemask)) {
+ pr_err("This pe is not supposed to join sync, %u/%u/%d\n",
+ bb_info->cmg, bb_info->bb, cpu);
+ return -EINVAL;
+ }
+
+ if (cpumask_test_cpu(cpu, bb_info->assigned_pemask)) {
+ pr_err("This pe is already assigned to window: %u/%u/%d\n",
+ bb_info->cmg, bb_info->bb, cpu);
+ return -EINVAL;
+ }
+
+ if (bw_ctl->window >= 0) {
+ /* User specifies window number to use. Check if available */
+ if (bw_ctl->window >= _hwinfo.num_bw) {
+ pr_err("Window number is invalid: %u/%u/%d/%u\n",
+ bb_info->cmg, bb_info->bb, cpu, bw_ctl->window);
+ return -EINVAL;
+ }
+
+ if (test_bit(bw_ctl->window, &_hwinfo.used_bw_bmap[cpu])) {
+ pr_err("Window is already used: %u/%u/%d/%u\n",
+ bb_info->cmg, bb_info->bb, cpu, bw_ctl->window);
+ return -EBUSY;
+ }
+ } else {
+ /* User does not specify window number. Use free window */
+ i = ffz(_hwinfo.used_bw_bmap[cpu]);
+ if (i == _hwinfo.num_bw) {
+ pr_err("There is no free window: %u/%u/%d\n",
+ bb_info->cmg, bb_info->bb, cpu);
+ return -EBUSY;
+ }
+
+ bw_ctl->window = i;
+ }
+
+ return 0;
+}
+
+static void setup_ctl_reg(struct bb_info *bb_info, int cpu)
+{
+ u64 val;
+
+ if (_hwinfo.used_bw_bmap[cpu] != 0)
+ /* Already setup. Nothing todo */
+ return;
+
+ /*
+ * This is the first assign on this PE.
+ * Setup ctrl reg to allow access to BST_SYNC/LBSY_SYNC from EL0
+ */
+ val = (FHWB_CTRL_EL1_EL1AE | FHWB_CTRL_EL1_EL0AE);
+ write_sysreg_s(val, FHWB_CTRL_EL1);
+
+ pr_debug("Setup ctl reg. cpu: %d\n", cpu);
+}
+
+static void write_bw_reg(u8 window, u64 val)
+{
+ switch (window) {
+ case 0:
+ write_sysreg_s(val, FHWB_ASSIGN_SYNC_W0_EL1);
+ break;
+ case 1:
+ write_sysreg_s(val, FHWB_ASSIGN_SYNC_W1_EL1);
+ break;
+ case 2:
+ write_sysreg_s(val, FHWB_ASSIGN_SYNC_W2_EL1);
+ break;
+ case 3:
+ write_sysreg_s(val, FHWB_ASSIGN_SYNC_W3_EL1);
+ break;
+ }
+}
+
+static void setup_bw(struct bb_info *bb_info, struct fujitsu_hwb_ioc_bw_ctl *bw_ctl, int cpu)
+{
+ u64 val;
+ u8 ppe;
+
+ /* Set valid bit and bb number */
+ val = (FHWB_ASSIGN_SYNC_W_EL1_VALID | bw_ctl->bb);
+ write_bw_reg(bw_ctl->window, val);
+
+ /* Update bitmap info */
+ ppe = _hwinfo.core_map[cpu].ppe;
+ set_bit(bw_ctl->window, &_hwinfo.used_bw_bmap[cpu]);
+ cpumask_set_cpu(cpu, bb_info->assigned_pemask);
+ bb_info->bw[ppe] = bw_ctl->window;
+
+ pr_debug("Setup bw. cpu: %d, window: %u, BB: %u, bw_bmap: %lx, assigned_pemask: %*pbl\n",
+ cpu, bw_ctl->window, bw_ctl->bb,
+ _hwinfo.used_bw_bmap[cpu], cpumask_pr_args(bb_info->assigned_pemask));
+}
+
+static int ioc_bw_assign(struct file *filp, void __user *argp)
+{
+ struct hwb_private_data *pdata = (struct hwb_private_data *)filp->private_data;
+ struct fujitsu_hwb_ioc_bw_ctl bw_ctl;
+ struct bb_info *bb_info;
+ int ret;
+ int cpu;
+ u8 cmg;
+
+ if (!is_bound_only_one_pe())
+ return -EPERM;
+
+ if (copy_from_user(&bw_ctl, (struct fujitsu_hwb_ioc_bw_ctl __user *)argp,
+ sizeof(struct fujitsu_hwb_ioc_bw_ctl)))
+ return -EFAULT;
+
+ cpu = smp_processor_id();
+ cmg = _hwinfo.core_map[cpu].cmg;
+ bb_info = get_bb_info(pdata, cmg, bw_ctl.bb);
+ if (IS_ERR(bb_info))
+ return PTR_ERR(bb_info);
+
+ /*
+ * Barrier window register and control register is each PE's resource.
+ * context switch is not supported and mutual exclusion is needed for
+ * assign and unassign on this PE
+ */
+ preempt_disable();
+ ret = is_bw_assignable(bb_info, &bw_ctl, cpu);
+ if (!ret) {
+ setup_ctl_reg(bb_info, cpu);
+ setup_bw(bb_info, &bw_ctl, cpu);
+ }
+ preempt_enable();
+
+ put_bb_info(bb_info);
+
+ /* Copy back window number to be used to user */
+ if (!ret && copy_to_user((struct fujitsu_hwb_ioc_bw_ctl __user *)argp, &bw_ctl,
+ sizeof(struct fujitsu_hwb_ioc_bw_ctl)))
+ /* Leave cleanup to f_op->release() */
+ return -EFAULT;
+
+ return ret;
+}
+
static long fujitsu_hwb_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
@@ -356,6 +540,9 @@ static long fujitsu_hwb_dev_ioctl(struct file *filp, unsigned int cmd, unsigned
case FUJITSU_HWB_IOC_BB_ALLOC:
ret = ioc_bb_alloc(filp, argp);
break;
+ case FUJITSU_HWB_IOC_BW_ASSIGN:
+ ret = ioc_bw_assign(filp, argp);
+ break;
default:
ret = -ENOTTY;
break;
@@ -17,7 +17,14 @@ struct fujitsu_hwb_ioc_bb_ctl {
unsigned long __user *pemask;
};
+struct fujitsu_hwb_ioc_bw_ctl {
+ __u8 bb;
+ __s8 window;
+};
+
#define FUJITSU_HWB_IOC_BB_ALLOC _IOWR(__FUJITSU_IOCTL_MAGIC, \
0x00, struct fujitsu_hwb_ioc_bb_ctl)
+#define FUJITSU_HWB_IOC_BW_ASSIGN _IOWR(__FUJITSU_IOCTL_MAGIC, \
+ 0x01, struct fujitsu_hwb_ioc_bw_ctl)
#endif /* _UAPI_LINUX_FUJITSU_HPC_IOC_H */
IOC_BW_ASSIGN ioctl sets up control register and window register on each PE. Therefore, this ioctl will be called as many times as the number of PEs joining synchronization. Also, the caller thread is expected to be bound to one PE at this point. Since barrier window and control register is per-PE resource and context switch is not supported at this point, we forbid concurrent running of ioc_bw_assign() on the same PE by disabling preemption. After this ioctl returns successfully, user program (EL0) can access BST_SYNC/LBSY_SYNC registers directly to realize synchronization. Signed-off-by: Misono Tomohiro <misono.tomohiro@jp.fujitsu.com> --- drivers/soc/fujitsu/fujitsu_hwb.c | 187 +++++++++++++++++++++++++ include/uapi/linux/fujitsu_hpc_ioctl.h | 7 + 2 files changed, 194 insertions(+)