Subject: [RFC PATCH 00/10] Add Fujitsu A64FX soc entry/hardware barrier driver
Hello,
This series adds Fujitsu A64FX SoC entry in drivers/soc and hardware
barrier driver for it.
[Driver Description]
A64FX CPU has several functions for HPC workload and hardware barrier
is one of them. It is a mechanism to realize fast synchronization by
PEs belonging to the same L3 cache domain by using implementation
defined hardware registers.
For more details, see A64FX HPC extension specification in
https://github.com/fujitsu/A64FX
The driver mainly offers a set of ioctls to manipulate related registers.
Patch 1-9 implements driver code and patch 10 finally adds kconfig,
Makefile and MAINTAINER entry for the driver.
Also, C library and test program for this driver is available on:
https://github.com/fujitsu/hardware_barrier
The driver is based on v5.11-rc2 and tested on FX700 environment.
[RFC]
This is the first time we upstream drivers for our chip and I want to
confirm driver location and patch submission process.
Based on my observation it seems drivers/soc folder is right place to put
this driver, so I added Kconfig entry for arm64 platform config, created
soc/fujitsu folder and updated MAINTAINER entry accordingly (last patch).
Is it right?
Also for final submission I think I need to 1) create some public git
tree to push driver code (github or something), 2) make pull request to
SOC team (soc@kernel.org). Is it a correct procedure?
I will appreciate any help/comments.
sidenote: We plan to post other drivers for A64FX HPC extension
(prefetch control and cache control) too anytime soon.
Misono Tomohiro (10):
soc: fujitsu: hwb: Add hardware barrier driver init/exit code
soc: fujtisu: hwb: Add open operation
soc: fujitsu: hwb: Add IOC_BB_ALLOC ioctl
soc: fujitsu: hwb: Add IOC_BW_ASSIGN ioctl
soc: fujitsu: hwb: Add IOC_BW_UNASSIGN ioctl
soc: fujitsu: hwb: Add IOC_BB_FREE ioctl
soc: fujitsu: hwb: Add IOC_GET_PE_INFO ioctl
soc: fujitsu: hwb: Add release operation
soc: fujitsu: hwb: Add sysfs entry
soc: fujitsu: hwb: Add Kconfig/Makefile to build fujitsu_hwb driver
MAINTAINERS | 7 +
arch/arm64/Kconfig.platforms | 5 +
drivers/soc/Kconfig | 1 +
drivers/soc/Makefile | 1 +
drivers/soc/fujitsu/Kconfig | 24 +
drivers/soc/fujitsu/Makefile | 2 +
drivers/soc/fujitsu/fujitsu_hwb.c | 1253 ++++++++++++++++++++++++
include/uapi/linux/fujitsu_hpc_ioctl.h | 41 +
8 files changed, 1334 insertions(+)
create mode 100644 drivers/soc/fujitsu/Kconfig
create mode 100644 drivers/soc/fujitsu/Makefile
create mode 100644 drivers/soc/fujitsu/fujitsu_hwb.c
create mode 100644 include/uapi/linux/fujitsu_hpc_ioctl.h
--
2.26.2
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
IOC_BB_FREE ioctl resets what IOC_BB_ALLOC ioctl did.
We need to forbid assign/unassign operation happens during free
operation, so we set the flag to indicate it and also wait
ongoing assign/unassign to finish first.
If there exist PEs on which IOC_BW_UNASSIGN is not called,
we send IPI to do effectively the same operation as IOC_BW_UNASSIGN.
Signed-off-by: Misono Tomohiro <redacted>
---
drivers/soc/fujitsu/fujitsu_hwb.c | 125 ++++++++++++++++++++++++-
include/uapi/linux/fujitsu_hpc_ioctl.h | 2 +
2 files changed, 122 insertions(+), 5 deletions(-)
@@ -196,6 +196,12 @@ static struct bb_info *get_bb_info(struct hwb_private_data *pdata, u8 cmg, u8 bbspin_lock(&pdata->list_lock);list_for_each_entry(bb_info,&pdata->bb_list,node){if(bb_info->cmg==cmg&&bb_info->bb==bb){+if(test_bit(BB_FREEING,&bb_info->flag)){+pr_err("BB is currently being freed: %u/%u\n",cmg,bb);+spin_unlock(&pdata->list_lock);+returnERR_PTR(-EPERM);+}+kref_get(&bb_info->kref);spin_unlock(&pdata->list_lock);returnbb_info;
@@ -389,6 +395,11 @@ static int is_bw_assignable(struct bb_info *bb_info, struct fujitsu_hwb_ioc_bw_c{inti;+if(test_bit(BB_FREEING,&bb_info->flag)){+pr_err("BB is currently being freed: %u/%u/%d\n",bb_info->cmg,bb_info->bb,cpu);+return-EPERM;+}+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);
@@ -507,18 +519,27 @@ static int ioc_bw_assign(struct file *filp, void __user *argp)if(IS_ERR(bb_info))returnPTR_ERR(bb_info);+/* Increment counter to avoid this BB being freed during assign operation */+atomic_inc(&bb_info->ongoing_assign_count);+/**BarrierwindowregisterandcontrolregisteriseachPE'sresource.*contextswitchisnotsupportedandmutualexclusionisneededfor-*assignandunassignonthisPE+*assignandunassignonthisPE.Ascleanup_bw()mightbeexecuted+*ininterruptcontextviaon_each_cpu_mask,disablingirqisneeded*/-preempt_disable();+local_irq_save(flags);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();+local_irq_restore(flags);++/* Wakeup if there is a process waiting in ioc_bb_free() */+if(atomic_dec_and_test(&bb_info->ongoing_assign_count)&&+test_bit(BB_FREEING,&bb_info->flag))+wake_up(&bb_info->wq);put_bb_info(bb_info);
@@ -535,6 +556,12 @@ static int is_bw_unassignable(struct bb_info *bb_info, int cpu){u8ppe;+if(test_bit(BB_FREEING,&bb_info->flag)){+pr_err("This bb is currently being freed: %u/%u/%d\n",+bb_info->cmg,bb_info->bb,cpu);+return-EPERM;+}+if(!cpumask_test_and_clear_cpu(cpu,bb_info->assigned_pemask)){pr_err("This pe is not assigned: %u/%u/%d\n",bb_info->cmg,bb_info->bb,cpu);return-EINVAL;
@@ -608,19 +636,103 @@ static int ioc_bw_unassign(struct file *filp, void __user *argp)returnPTR_ERR(bb_info);/* See comments in ioc_bw_assign() */-preempt_disable();+atomic_inc(&bb_info->ongoing_assign_count);++local_irq_save(flags);ret=is_bw_unassignable(bb_info,cpu);if(!ret){teardown_bw(bb_info,cpu);teardown_ctl_reg(bb_info,cpu);}-preempt_enable();+local_irq_restore(flags);++if(atomic_dec_and_test(&bb_info->ongoing_assign_count)&&+test_bit(BB_FREEING,&bb_info->flag))+wake_up(&bb_info->wq);put_bb_info(bb_info);returnret;}+staticvoidcleanup_bw_func(void*args)+{+structbb_info*bb_info=(structbb_info*)args;+intcpu=smp_processor_id();++teardown_bw(bb_info,cpu);+teardown_ctl_reg(bb_info,cpu);+}++/* Send IPI to reset INIT_SYNC register */+staticvoidteardown_bb(structbb_info*bb_info)+{+structinit_sync_argsargs={0};+intcpu;++/* Reset BW on each PE if IOC_BW_UNASSIGN is not called properly */+if(cpumask_weight(bb_info->assigned_pemask)!=0){+pr_warn("unassign is not called properly. CMG: %d, BB: %d, unassigned PE: %*pbl\n",+bb_info->cmg,bb_info->bb,cpumask_pr_args(bb_info->assigned_pemask));+on_each_cpu_mask(bb_info->assigned_pemask,cleanup_bw_func,bb_info,1);+}++/* INIT_SYNC register is shared resource in CMG. Pick one PE */+cpu=cpumask_any(bb_info->pemask);++args.bb=bb_info->bb;+/* Just clear all bits */+args.val=0;+on_each_cpu_mask(cpumask_of(cpu),write_init_sync_reg,&args,1);++clear_bit(bb_info->bb,&_hwinfo.used_bb_bmap[bb_info->cmg]);++pr_debug("Teardown bb: cpu: %d, CMG: %u, BB: %u, bitmap: %lx\n",+cpu,bb_info->cmg,bb_info->bb,_hwinfo.used_bb_bmap[bb_info->cmg]);+}++staticintioc_bb_free(structfile*filp,void__user*argp)+{+structhwb_private_data*pdata=(structhwb_private_data*)filp->private_data;+structfujitsu_hwb_ioc_bb_ctlbb_ctl;+structbb_info*bb_info;++if(copy_from_user(&bb_ctl,(structfujitsu_hwb_ioc_bb_ctl__user*)argp,+sizeof(structfujitsu_hwb_ioc_bb_ctl)))+return-EFAULT;++bb_info=get_bb_info(pdata,bb_ctl.cmg,bb_ctl.bb);+if(IS_ERR(bb_info))+returnPTR_ERR(bb_info);++/* Forbid free/assign/unassign operation from now on */+if(test_and_set_bit(BB_FREEING,&bb_info->flag)){+pr_err("IOC_BB_FREE is already called. CMG: %u, BB: %u\n",bb_ctl.cmg,bb_ctl.bb);+put_bb_info(bb_info);+return-EPERM;+}++/* Wait current ongoing assign/unassign operation to finish */+if(wait_event_interruptible(bb_info->wq,+(atomic_read(&bb_info->ongoing_assign_count)==0))){+clear_bit(BB_FREEING,&bb_info->flag);+put_bb_info(bb_info);+pr_debug("IOC_BB_FREE is interrupted. CMG: %u, BB: %u\n",bb_ctl.cmg,bb_ctl.bb);+return-EINTR;+}++teardown_bb(bb_info);+spin_lock(&pdata->list_lock);+list_del_init(&bb_info->node);+spin_unlock(&pdata->list_lock);++/* 1 put for get_bb_info, 1 for alloc_bb_info */+put_bb_info(bb_info);+put_bb_info(bb_info);++return0;+}+staticlongfujitsu_hwb_dev_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg){void__user*argp=(void__user*)arg;
@@ -636,6 +748,9 @@ static long fujitsu_hwb_dev_ioctl(struct file *filp, unsigned int cmd, unsignedcaseFUJITSU_HWB_IOC_BW_UNASSIGN:ret=ioc_bw_unassign(filp,argp);break;+caseFUJITSU_HWB_IOC_BB_FREE:+ret=ioc_bb_free(filp,argp);+break;default:ret=-ENOTTY;break;
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 <redacted>
---
drivers/soc/fujitsu/fujitsu_hwb.c | 187 +++++++++++++++++++++++++
include/uapi/linux/fujitsu_hpc_ioctl.h | 7 +
2 files changed, 194 insertions(+)
@@ -179,6 +179,34 @@ static struct bb_info *alloc_bb_info(void)returnbb_info;}+staticstructbb_info*get_bb_info(structhwb_private_data*pdata,u8cmg,u8bb)+{+structbb_info*bb_info;++if(cmg>=_hwinfo.num_cmg||bb>=_hwinfo.num_bb){+pr_err("CMG/BB number is invalid: %u/%u\n",cmg,bb);+returnERR_PTR(-EINVAL);+}++if(!test_bit(bb,&_hwinfo.used_bb_bmap[cmg])){+pr_err("BB is not allocated: %u/%u\n",cmg,bb);+returnERR_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);+returnbb_info;+}+}+spin_unlock(&pdata->list_lock);++pr_err("BB is not allocated by this process: %u/%u\n",cmg,bb);+returnERR_PTR(-EPERM);+}+staticinlinevoidput_bb_info(structbb_info*bb_info){kref_put(&bb_info->kref,free_bb_info);
This is an infomative ioctl to tell users CMG/PE number of currently
running PE.
Signed-off-by: Misono Tomohiro <redacted>
---
drivers/soc/fujitsu/fujitsu_hwb.c | 18 ++++++++++++++++++
include/uapi/linux/fujitsu_hpc_ioctl.h | 7 +++++++
2 files changed, 25 insertions(+)
IOC_BB_ALLOC ioctl initialize INIT_SYNC register which represents
PEs in a CMG joining synchronization. Although we get cpumask of
PEs from userspace, INIT_SYNC register requires mask value based
on physical PE number which is written in each PE's BST register.
So we perform conversion of cpumask value in validate_and_conver_pemask().
Since INIT_SYNC register is a shared resource per CMG, we pick
up one PE and send IPI to it to write the register.
Signed-off-by: Misono Tomohiro <redacted>
---
drivers/soc/fujitsu/fujitsu_hwb.c | 223 +++++++++++++++++++++++++
include/uapi/linux/fujitsu_hpc_ioctl.h | 23 +++
2 files changed, 246 insertions(+)
create mode 100644 include/uapi/linux/fujitsu_hpc_ioctl.h
@@ -142,6 +144,226 @@ struct bb_info {};staticstructkmem_cache*bb_info_cachep;+staticvoidfree_bb_info(structkref*kref)+{+structbb_info*bb_info=container_of(kref,structbb_info,kref);++free_cpumask_var(bb_info->assigned_pemask);+free_cpumask_var(bb_info->pemask);+kfree(bb_info->bw);+kmem_cache_free(bb_info_cachep,bb_info);+}++staticstructbb_info*alloc_bb_info(void)+{+structbb_info*bb_info;++bb_info=kmem_cache_zalloc(bb_info_cachep,GFP_KERNEL);+if(!bb_info)+returnNULL;++bb_info->bw=kcalloc(_hwinfo.max_pe_per_cmg,sizeof(u8),GFP_KERNEL);+if(!bb_info->bw){+free_bb_info(&bb_info->kref);+returnNULL;+}+if(!zalloc_cpumask_var(&bb_info->pemask,GFP_KERNEL)||+!zalloc_cpumask_var(&bb_info->assigned_pemask,GFP_KERNEL)){+free_bb_info(&bb_info->kref);+returnNULL;+}++init_waitqueue_head(&bb_info->wq);+kref_init(&bb_info->kref);++returnbb_info;+}++staticinlinevoidput_bb_info(structbb_info*bb_info)+{+kref_put(&bb_info->kref,free_bb_info);+}++/* Validate pemask's range and convert it to a mask based on physical PE number */+staticintvalidate_and_convert_pemask(structbb_info*bb_info,unsignedlong*phys_pemask)+{+intcpu;+u8cmg;++if(cpumask_weight(bb_info->pemask)<2){+pr_err("pemask needs at least two bit set: %*pbl\n",+cpumask_pr_args(bb_info->pemask));+return-EINVAL;+}++if(!cpumask_subset(bb_info->pemask,cpu_online_mask)){+pr_err("pemask needs to be subset of online cpu: %*pbl, %*pbl\n",+cpumask_pr_args(bb_info->pemask),cpumask_pr_args(cpu_online_mask));+return-EINVAL;+}++/*+*INIT_SYNCregisterrequiresamaskvaluebasedonphysicalPEnumber.+*SoconvertpemasktoitwhilecheckingifallPEsbelongstothesameCMG+*/+cpu=cpumask_first(bb_info->pemask);+cmg=_hwinfo.core_map[cpu].cmg;+*phys_pemask=0;+for_each_cpu(cpu,bb_info->pemask){+if(_hwinfo.core_map[cpu].cmg!=cmg){+pr_err("All PEs must belong to the same CMG: %*pbl\n",+cpumask_pr_args(bb_info->pemask));+return-EINVAL;+}+set_bit(_hwinfo.core_map[cpu].ppe,phys_pemask);+}+bb_info->cmg=cmg;++pr_debug("pemask: %*pbl, physical_pemask: %lx\n",+cpumask_pr_args(bb_info->pemask),*phys_pemask);++return0;+}++/* Search free BB in_hwinfo->used_bb_bitmap[cmg] */+staticintsearch_free_bb(u8cmg)+{+inti;++for(i=0;i<_hwinfo.num_bb;i++){+if(!test_and_set_bit(i,&_hwinfo.used_bb_bmap[cmg])){+pr_debug("Use BB %u in CMG %u, bitmap: %lx\n",+i,cmg,_hwinfo.used_bb_bmap[cmg]);+returni;+}+}++pr_err("All barrier blade is currently used in CMG %u\n",cmg);+return-EBUSY;+}++structinit_sync_args{+u64val;+u8bb;+};++staticvoidwrite_init_sync_reg(void*args)+{+structinit_sync_args*sync_args=(structinit_sync_args*)args;++switch(sync_args->bb){+case0:+write_sysreg_s(sync_args->val,FHWB_INIT_SYNC_BB0_EL1);+break;+case1:+write_sysreg_s(sync_args->val,FHWB_INIT_SYNC_BB1_EL1);+break;+case2:+write_sysreg_s(sync_args->val,FHWB_INIT_SYNC_BB2_EL1);+break;+case3:+write_sysreg_s(sync_args->val,FHWB_INIT_SYNC_BB3_EL1);+break;+case4:+write_sysreg_s(sync_args->val,FHWB_INIT_SYNC_BB4_EL1);+break;+case5:+write_sysreg_s(sync_args->val,FHWB_INIT_SYNC_BB5_EL1);+break;+}+}++/* Send IPI to initialize INIT_SYNC register */+staticvoidsetup_bb(structbb_info*bb_info,unsignedlongphys_pemask)+{+structinit_sync_argsargs={0};+intcpu;++/* INIT_SYNC register is shared resource in CMG. Pick one PE to set it up */+cpu=cpumask_any(bb_info->pemask);++args.bb=bb_info->bb;+args.val=FIELD_PREP(FHWB_INIT_SYNC_BB_EL1_MASK_FIELD,phys_pemask);+on_each_cpu_mask(cpumask_of(cpu),write_init_sync_reg,&args,1);++pr_debug("Setup bb. cpu: %d, CMG: %u, BB: %u, bimtap: %lx\n",+cpu,bb_info->cmg,bb_info->bb,_hwinfo.used_bb_bmap[bb_info->cmg]);+}++staticintioc_bb_alloc(structfile*filp,void__user*argp)+{+structhwb_private_data*pdata=(structhwb_private_data*)filp->private_data;+structfujitsu_hwb_ioc_bb_ctlbb_ctl;+structbb_info*bb_info;+unsignedlongphysical_pemask;+unsignedintsize;+intret;++if(copy_from_user(&bb_ctl,(structfujitsu_hwb_ioc_bb_ctl__user*)argp,+sizeof(structfujitsu_hwb_ioc_bb_ctl)))+return-EFAULT;++bb_info=alloc_bb_info();+if(!bb_info)+return-ENOMEM;++/* cpumask size may vary in user and kernel space. Use the smaller one */+size=min(cpumask_size(),bb_ctl.size);+if(copy_from_user(bb_info->pemask,bb_ctl.pemask,size)){+ret=-EFAULT;+gotoput_bb_info;+}++ret=validate_and_convert_pemask(bb_info,&physical_pemask);+if(ret<0)+gotoput_bb_info;++ret=search_free_bb(bb_info->cmg);+if(ret<0)+gotoput_bb_info;+bb_info->bb=ret;++/* Copy back CMG/BB number to be used to user */+bb_ctl.cmg=bb_info->cmg;+bb_ctl.bb=bb_info->bb;+if(copy_to_user((structfujitsu_hwb_ioc_bb_ctl__user*)argp,&bb_ctl,+sizeof(structfujitsu_hwb_ioc_bb_ctl))){+ret=-EFAULT;+clear_bit(bb_ctl.bb,&_hwinfo.used_bb_bmap[bb_ctl.cmg]);+gotoput_bb_info;+}++setup_bb(bb_info,physical_pemask);++spin_lock(&pdata->list_lock);+list_add_tail(&bb_info->node,&pdata->bb_list);+spin_unlock(&pdata->list_lock);++return0;++put_bb_info:+put_bb_info(bb_info);++returnret;+}++staticlongfujitsu_hwb_dev_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg)+{+void__user*argp=(void__user*)arg;+intret;++switch(cmd){+caseFUJITSU_HWB_IOC_BB_ALLOC:+ret=ioc_bb_alloc(filp,argp);+break;+default:+ret=-ENOTTY;+break;+}++returnret;+}+staticintfujitsu_hwb_dev_open(structinode*inode,structfile*filp){structhwb_private_data*pdata;
This adds hardware barrier driver's struct definitions and
module init/exit code. We use miscdeice for barrier driver ioctl
and /dev/fujitsu_hwb will be created upon module load.
Following commits will add each ioctl definition.
Signed-off-by: Misono Tomohiro <redacted>
---
drivers/soc/fujitsu/fujitsu_hwb.c | 313 ++++++++++++++++++++++++++++++
1 file changed, 313 insertions(+)
create mode 100644 drivers/soc/fujitsu/fujitsu_hwb.c
@@ -0,0 +1,313 @@+// SPDX-License-Identifier: GPL-2.0+/*+*Copyright2020FUJITSULIMITED+*+*Thishardwarebarrier(HWB)driverprovidesasetofioctlstorealizesynchronization+*byPEsinthesameComeMemoryGroup(CMG)byusingimplementationdefinedregisters.+*OnA64FX,CMGisthesameasL3cachedomain.+*+*Themainpurposeofthedriverissettingupregisterswhichcannotbeaccessed+*fromEL0.However,afterinitialization,BST_SYNC/LBSY_SYNCregisterswhichisused+*insynchronizationmainlogiccanbeaccessedfromEL0(thereforeitisfast).+*+*Simplifiedbarrieroperationflowofuserapplicationisasfollows:+*(onePE)+*1.CallIOC_BB_ALLOCtosetupINIT_SYNCregisterwhichissharedinaCMG.+*ThisspecifieswhichPEsjoinsynchronization+*(oneachPEjoiningsynchronization)+*2.CallIOC_BW_ASSIGNtosetupASSIGN_SYNCregisterperPE+*3.Barriermainlogic(alllogicrunsinEL0)+*a)Write1toBST_SYNCregister+*b)ReadLBSY_SYNCregister+*c)IfLBSY_SYNCvalueis1,syncisfinished,otherwisegobacktob+*(IfallPEsjoiningsynchronizationwrite1toBST_SYNC,LBSY_SYNCbecomes1)+*4.CallIOC_BW_UNASSIGNtoresetASSIGN_SYNCregister+*(onePE)+*5.CallIOC_BB_FREEtoresetINIT_SYNCregister+*/++#include<asm/cputype.h>+#include<linux/bitfield.h>+#include<linux/bitops.h>+#include<linux/cpu.h>+#include<linux/cpumask.h>+#include<linux/kernel.h>+#include<linux/miscdevice.h>+#include<linux/module.h>+#include<linux/spinlock.h>+#include<linux/slab.h>+#include<linux/wait.h>++#ifdef pr_fmt+#undef pr_fmt+#endif+#define pr_fmt(fmt) "[%s:%s:%d] " fmt, KBUILD_MODNAME, __func__, __LINE__++/* Since miscdevice is used, /dev/fujitsu_hwb will be created when module is loaded */+#define FHWB_DEV_NAME "fujitsu_hwb"++/* Implementation defined registers for barrier shared in CMG */+#define FHWB_INIT_SYNC_BB0_EL1 sys_reg(3, 0, 15, 13, 0)+#define FHWB_INIT_SYNC_BB1_EL1 sys_reg(3, 0, 15, 13, 1)+#define FHWB_INIT_SYNC_BB2_EL1 sys_reg(3, 0, 15, 13, 2)+#define FHWB_INIT_SYNC_BB3_EL1 sys_reg(3, 0, 15, 13, 3)+#define FHWB_INIT_SYNC_BB4_EL1 sys_reg(3, 0, 15, 13, 4)+#define FHWB_INIT_SYNC_BB5_EL1 sys_reg(3, 0, 15, 13, 5)++/* Implementation defined registers for barrier per PE */+#define FHWB_CTRL_EL1 sys_reg(3, 0, 11, 12, 0)+#define FHWB_BST_BIT_EL1 sys_reg(3, 0, 11, 12, 4)+#define FHWB_ASSIGN_SYNC_W0_EL1 sys_reg(3, 0, 15, 15, 0)+#define FHWB_ASSIGN_SYNC_W1_EL1 sys_reg(3, 0, 15, 15, 1)+#define FHWB_ASSIGN_SYNC_W2_EL1 sys_reg(3, 0, 15, 15, 2)+#define FHWB_ASSIGN_SYNC_W3_EL1 sys_reg(3, 0, 15, 15, 3)++/* Field definitions for above registers */+#define FHWB_INIT_SYNC_BB_EL1_MASK_FIELD GENMASK_ULL(44, 32)+#define FHWB_INIT_SYNC_BB_EL1_BST_FIELD GENMASK_ULL(12, 0)+#define FHWB_CTRL_EL1_EL1AE BIT_ULL(63)+#define FHWB_CTRL_EL1_EL0AE BIT_ULL(62)+#define FHWB_BST_BIT_EL1_CMG_FILED GENMASK_ULL(5, 4)+#define FHWB_BST_BIT_EL1_PE_FILED GENMASK_ULL(3, 0)+#define FHWB_ASSIGN_SYNC_W_EL1_VALID BIT_ULL(63)++staticenumcpuhp_state_hp_state;++/*+*EachPEhasitsownCMGandPhysicalPEnumber(determinedbyBST_BIT_EL1register).+*BarrieroperationcanbeperformedbyPEswhichbelongtothesameCMG.+*/+structpe_info{+/* CMG number of this PE */+u8cmg;+/* Physical PE number of this PE */+u8ppe;+};++/* Hardware information of running system */+structhwb_hwinfo{+/* CPU type (part number) */+unsignedinttype;+/* Number of CMG */+u8num_cmg;+/* Number of barrier blade(BB) per CMG */+u8num_bb;+/* Number of barrier window(BW) per PE */+u8num_bw;+/*+*MaximumnumberofPEperCMG.+*DependingonBIOSconfiguration,eachCMGhasuptomax_pe_per_cmgPEs+*andeachPEhasuniquephysicalPEnumberbetween0~(max_pe_per_cmg-1)+*/+u8max_pe_per_cmg;++/* Bitmap for currently allocated BB per CMG */+unsignedlong*used_bb_bmap;+/* Bitmap for currently allocated BW per PE */+unsignedlong*used_bw_bmap;+/* Mapping table of cpuid -> CMG/PE number */+structpe_info*core_map;+};+staticstructhwb_hwinfo_hwinfo;++/* List for barrier blade currently used per FD */+structhwb_private_data{+structlist_headbb_list;+spinlock_tlist_lock;+};++/* Each barrier blade info */+#define BB_FREEING 1+structbb_info{+/* cpumask for PEs which participate synchronization */+cpumask_var_tpemask;+/* cpumask for PEs which currently assigned BW for this BB */+cpumask_var_tassigned_pemask;+/* Added to hwb_private_data::bb_list */+structlist_headnode;+/* For indicating if this bb is currently being freed or not */+unsignedlongflag;+/* For waiting ongoing assign/unassign operation to finish before freeing BB */+wait_queue_head_twq;+/* Track ongoing assign/unassign operation count */+atomic_tongoing_assign_count;+/* CMG number of this blade */+u8cmg;+/* BB number of this blade */+u8bb;+/* Hold assigned window number of each PE corresponding to @assigned_pemask */+u8*bw;+/* Track usage count as IOC_BB_FREE and IOC_BW_[UN]ASSIGN might be run in parallel */+structkrefkref;+};+staticstructkmem_cache*bb_info_cachep;++staticconststructfile_operationsfujitsu_hwb_dev_fops={+.owner=THIS_MODULE,+};++staticstructmiscdevicebar_miscdev={+.fops=&fujitsu_hwb_dev_fops,+.minor=MISC_DYNAMIC_MINOR,+.mode=0666,+.name=FHWB_DEV_NAME,+};++staticvoiddestroy_bb_info_cachep(void)+{+kmem_cache_destroy(bb_info_cachep);+}++staticint__initinit_bb_info_cachep(void)+{+/*+*Sincecpumaskvaluewillbecopiedfromuserspacetothebeginningof+*structbb_info,usekmem_cache_create_usercopytomarkthatregion.+*OtherwiseCONFIG_HARDENED_USERCOPYgivesuser_copy_warn.+*/+bb_info_cachep=kmem_cache_create_usercopy("bb_info_cache",sizeof(structbb_info),+0,SLAB_HWCACHE_ALIGN,0,sizeof(cpumask_var_t),NULL);+if(bb_info_cachep==NULL)+return-ENOMEM;++return0;+}++staticvoidfree_map(void)+{+kfree(_hwinfo.used_bw_bmap);+kfree(_hwinfo.used_bb_bmap);+kfree(_hwinfo.core_map);+}++staticint__initalloc_map(void)+{+_hwinfo.core_map=kcalloc(num_possible_cpus(),sizeof(structpe_info),GFP_KERNEL);+_hwinfo.used_bb_bmap=kcalloc(_hwinfo.num_cmg,sizeof(unsignedlong),GFP_KERNEL);+_hwinfo.used_bw_bmap=kcalloc(num_possible_cpus(),sizeof(unsignedlong),GFP_KERNEL);+if(!_hwinfo.core_map||!_hwinfo.used_bb_bmap||!_hwinfo.used_bw_bmap)+gotofail;++/* 0 is valid number for both CMG/PE. Set all bits to 1 to represents uninitialized state */+memset(_hwinfo.core_map,0xFF,sizeof(structpe_info)*num_possible_cpus());++return0;++fail:+free_map();+return-ENOMEM;+}++/* Get this system's CPU type (part number). If it is not fujitsu CPU, return -1 */+staticint__initget_cpu_type(void)+{+if(read_cpuid_implementor()!=ARM_CPU_IMP_FUJITSU)+return-1;++returnread_cpuid_part_number();+}++staticint__initsetup_hwinfo(void)+{+inttype;++type=get_cpu_type();+if(type<0)+return-ENODEV;++_hwinfo.type=type;+switch(type){+caseFUJITSU_CPU_PART_A64FX:+_hwinfo.num_cmg=4;+_hwinfo.num_bb=6;+_hwinfo.num_bw=4;+_hwinfo.max_pe_per_cmg=13;+break;+default:+return-ENODEV;+}++return0;+}++staticinthwb_cpu_online(unsignedintcpu)+{+u64val;+inti;++/* Setup core_map by reading BST_BIT_EL1 register of each PE */+val=read_sysreg_s(FHWB_BST_BIT_EL1);+_hwinfo.core_map[cpu].cmg=FIELD_GET(FHWB_BST_BIT_EL1_CMG_FILED,val);+_hwinfo.core_map[cpu].ppe=FIELD_GET(FHWB_BST_BIT_EL1_PE_FILED,val);++/* Since these registers' values are UNKNOWN on reset, explicitly clear all */+for(i=0;i<_hwinfo.num_bw;i++)+write_bw_reg(i,0);++write_sysreg_s(0,FHWB_CTRL_EL1);++return0;+}++staticint__inithwb_init(void)+{+intret;++ret=setup_hwinfo();+if(ret<0){+pr_err("Unsupported CPU type\n");+returnret;+}++ret=alloc_map();+if(ret<0)+returnret;++ret=init_bb_info_cachep();+if(ret<0)+gotoout1;++/*+*SetupcpuhpcallbacktoensureeachPE'sresourcewillbeinitialized+*evenifsomePEsareofflineatthispoint+*/+ret=cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,"soc/fujitsu_hwb:online",+hwb_cpu_online,NULL);+if(ret<0){+pr_err("cpuhp setup failed: %d\n",ret);+gotoout2;+}+_hp_state=ret;++ret=misc_register(&bar_miscdev);+if(ret<0){+pr_err("misc_register failed: %d\n",ret);+gotoout3;+}++return0;++out3:+cpuhp_remove_state(_hp_state);+out2:+destroy_bb_info_cachep();+out1:+free_map();++returnret;+}++staticvoid__exithwb_exit(void)+{+misc_deregister(&bar_miscdev);+cpuhp_remove_state(_hp_state);+destroy_bb_info_cachep();+free_map();+}++module_init(hwb_init);+module_exit(hwb_exit);++MODULE_LICENSE("GPL v2");+MODULE_AUTHOR("FUJITSU LIMITED");+MODULE_DESCRIPTION("FUJITSU HPC Hardware Barrier Driver");
--
2.26.2
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
This adds kconfig/Makefile to build fujitsu hardware barrier driver
(fujitsu_hwb.ko when built as module).
Note that this is the first time to add A64FX specific driver,
this also adds A64FX entry in Kconfig.platforms of arm64 Kconfig.
Also add MAINTAINERS entry for ARM/A64FX accordingly.
Signed-off-by: Misono Tomohiro <redacted>
---
MAINTAINERS | 7 +++++++
arch/arm64/Kconfig.platforms | 5 +++++
drivers/soc/Kconfig | 1 +
drivers/soc/Makefile | 1 +
drivers/soc/fujitsu/Kconfig | 24 ++++++++++++++++++++++++
drivers/soc/fujitsu/Makefile | 2 ++
6 files changed, 40 insertions(+)
create mode 100644 drivers/soc/fujitsu/Kconfig
create mode 100644 drivers/soc/fujitsu/Makefile
Upon release, we cleanup remaining resources/registers if necessary.
This happens when user does not call IOC_BB_FREE properly and the
function will do effectively the same operation as IOC_BB_FREE.
Signed-off-by: Misono Tomohiro <redacted>
---
drivers/soc/fujitsu/fujitsu_hwb.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
@@ -796,9 +796,35 @@ static int fujitsu_hwb_dev_open(struct inode *inode, struct file *filp)return0;}+staticintfujitsu_hwb_dev_release(structinode*inode,structfile*filp)+{+structhwb_private_data*pdata=(structhwb_private_data*)filp->private_data;+structbb_info*bb_info,*tmp;++/*+*CleanupBBifIOC_BB_FREEisnotcalledproperly.+*Nolockforpdata->bb_listisneededcausethereisnooneelse+*/+if(!list_empty(&pdata->bb_list)){+pr_warn("free operation is not called properly\n");++list_for_each_entry_safe(bb_info,tmp,&pdata->bb_list,node){+teardown_bb(bb_info);+list_del_init(&bb_info->node);+/* 1 put for alloc_bb_info */+put_bb_info(bb_info);+}+}++kfree(pdata);++return0;+}+staticconststructfile_operationsfujitsu_hwb_dev_fops={.owner=THIS_MODULE,.open=fujitsu_hwb_dev_open,+.release=fujitsu_hwb_dev_release,.unlocked_ioctl=fujitsu_hwb_dev_ioctl,};
--
2.26.2
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
IOC_BW_UNASSIGN resets what IOC_BW_ASSIGN did on each PE.
This ioctl will also be called as many times as the number of PEs joining
synchronization.
Signed-off-by: Misono Tomohiro <redacted>
---
drivers/soc/fujitsu/fujitsu_hwb.c | 93 ++++++++++++++++++++++++++
include/uapi/linux/fujitsu_hpc_ioctl.h | 2 +
2 files changed, 95 insertions(+)
@@ -531,6 +531,96 @@ static int ioc_bw_assign(struct file *filp, void __user *argp)returnret;}+staticintis_bw_unassignable(structbb_info*bb_info,intcpu)+{+u8ppe;++if(!cpumask_test_and_clear_cpu(cpu,bb_info->assigned_pemask)){+pr_err("This pe is not assigned: %u/%u/%d\n",bb_info->cmg,bb_info->bb,cpu);+return-EINVAL;+}++ppe=_hwinfo.core_map[cpu].ppe;+if(!test_bit(bb_info->bw[ppe],&_hwinfo.used_bw_bmap[cpu])){+/* should not happen */+pr_crit("Logic error. This window is not assigned: %u/%u/%d\n",+bb_info->cmg,bb_info->bb,cpu);+return-EINVAL;+}++return0;+}++staticvoidteardown_ctl_reg(structbb_info*bb_info,intcpu)+{+if(_hwinfo.used_bw_bmap[cpu]!=0)+/* Other window on this PE is still in use. Nothing todo */+return;++/*+*ThisisthelastunassignonthisPE.+*ClearallbitstodisallowaccesstoBST_SYNC/LBSY_SYNCfromEL0+*/+write_sysreg_s(0,FHWB_CTRL_EL1);++pr_debug("Teardown ctl reg. cpu: %d\n",cpu);+}++staticvoidteardown_bw(structbb_info*bb_info,intcpu)+{+u8window;+u8ppe;++/* Just clear all bits */+ppe=_hwinfo.core_map[cpu].ppe;+window=bb_info->bw[ppe];+write_bw_reg(window,0);++/* Update bitmap info */+clear_bit(window,&_hwinfo.used_bw_bmap[cpu]);+bb_info->bw[ppe]=-1;++pr_debug("Teardown bw. cpu: %d, window: %u, BB: %u, bw_bmap: %lx, assigned_pemask: %*pbl\n",+cpu,window,bb_info->bb,+_hwinfo.used_bw_bmap[cpu],cpumask_pr_args(bb_info->assigned_pemask));+}++staticintioc_bw_unassign(structfile*filp,void__user*argp)+{+structhwb_private_data*pdata=(structhwb_private_data*)filp->private_data;+structfujitsu_hwb_ioc_bw_ctlbw_ctl;+structbb_info*bb_info;+intcpu;+intret;+u8cmg;++if(!is_bound_only_one_pe())+return-EPERM;++if(copy_from_user(&bw_ctl,(structfujitsu_hwb_ioc_bw_ctl__user*)argp,+sizeof(structfujitsu_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))+returnPTR_ERR(bb_info);++/* See comments in ioc_bw_assign() */+preempt_disable();+ret=is_bw_unassignable(bb_info,cpu);+if(!ret){+teardown_bw(bb_info,cpu);+teardown_ctl_reg(bb_info,cpu);+}+preempt_enable();++put_bb_info(bb_info);++returnret;+}+staticlongfujitsu_hwb_dev_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg){void__user*argp=(void__user*)arg;
@@ -543,6 +633,9 @@ static long fujitsu_hwb_dev_ioctl(struct file *filp, unsigned int cmd, unsignedcaseFUJITSU_HWB_IOC_BW_ASSIGN:ret=ioc_bw_assign(filp,argp);break;+caseFUJITSU_HWB_IOC_BW_UNASSIGN:+ret=ioc_bw_unassign(filp,argp);+break;default:ret=-ENOTTY;break;
This adds sysfs entry per CMG to show running barrier driver status
for debugging user application. The following entries will be created:
/sys/class/misc/fujitsu_hwb
|- hwinfo ... number of CMG/BB/BW/pe_per_cmg on running system
|- CMG0
|- core_map ... cpuid belonging to this CMG
|- used_bb_bmap ... bitmap of currently allocated BB
|- used_bw_bmap ... bitmap of currently allocated BW
|- init_sync_bb0 ... current value of INIT_SYNC register 0
|- init_sync_bb1 ... current value of INIT_SYNC register 1
...
|- CMG1
...
Signed-off-by: Misono Tomohiro <redacted>
---
drivers/soc/fujitsu/fujitsu_hwb.c | 258 ++++++++++++++++++++++++++++++
1 file changed, 258 insertions(+)
@@ -931,6 +932,254 @@ static int hwb_cpu_online(unsigned int cpu)return0;}+staticvoidread_init_sync_reg(void*args)+{+structinit_sync_args*sync_args=(structinit_sync_args*)args;+u64val=0;++switch(sync_args->bb){+case0:+val=read_sysreg_s(FHWB_INIT_SYNC_BB0_EL1);+break;+case1:+val=read_sysreg_s(FHWB_INIT_SYNC_BB1_EL1);+break;+case2:+val=read_sysreg_s(FHWB_INIT_SYNC_BB2_EL1);+break;+case3:+val=read_sysreg_s(FHWB_INIT_SYNC_BB3_EL1);+break;+case4:+val=read_sysreg_s(FHWB_INIT_SYNC_BB4_EL1);+break;+case5:+val=read_sysreg_s(FHWB_INIT_SYNC_BB5_EL1);+break;+}++sync_args->val=val;+}++structhwb_attr{+structkobj_attributeattr;+u8bb;+};+staticstructhwb_attr*battr;++/* kobject for each CMG */+staticstructkobject**cmg_kobj;++/* Get CMG number based on index value of cmg_kobj */+staticintget_cmg_from_kobj(structkobject*kobj)+{+inti;++for(i=0;i<_hwinfo.num_cmg;i++){+if(cmg_kobj[i]==kobj)+returni;+}+/* should not happen */+WARN_ON_ONCE("cmg_kobj not found\n");+return0;+}++staticssize_thwb_init_sync_bb_show(structkobject*kobj,+structkobj_attribute*attr,char*buf)+{+structhwb_attr*battr=container_of(attr,structhwb_attr,attr);+structinit_sync_argsargs={0};+ssize_twritten=0;+intcpu;+intcmg;+u64mask;+u64bst;++/* Find online cpu in target cmg */+cmg=get_cmg_from_kobj(kobj);+for_each_online_cpu(cpu){+if(_hwinfo.core_map[cpu].cmg==cmg)+break;+}+if(cpu>=nr_cpu_ids)+return0;++/* Send IPI to read INIT_SYNC register */+args.bb=battr->bb;+on_each_cpu_mask(cpumask_of(cpu),read_init_sync_reg,&args,1);++mask=FIELD_GET(FHWB_INIT_SYNC_BB_EL1_MASK_FIELD,args.val);+bst=FIELD_GET(FHWB_INIT_SYNC_BB_EL1_BST_FIELD,args.val);++written+=scnprintf(buf,PAGE_SIZE,"%04llx\n",mask);+written+=scnprintf(buf+written,PAGE_SIZE-written,"%04llx\n",bst);++returnwritten;+}++#define BARRIER_ATTR(name) \+staticstructkobj_attributehwb_##name##_attribute=\+__ATTR(name,0444,hwb_##name##_show,NULL)++staticssize_thwb_hwinfo_show(structkobject*kobj,+structkobj_attribute*attr,char*buf)+{+returnscnprintf(buf,PAGE_SIZE,"%d %d %d %d\n",+_hwinfo.num_cmg,_hwinfo.num_bb,+_hwinfo.num_bw,_hwinfo.max_pe_per_cmg);+}+BARRIER_ATTR(hwinfo);++staticssize_thwb_used_bb_bmap_show(structkobject*kobj,+structkobj_attribute*attr,char*buf)+{+intcmg;++cmg=get_cmg_from_kobj(kobj);++returnscnprintf(buf,PAGE_SIZE,"%04lx\n",_hwinfo.used_bb_bmap[cmg]);+}+BARRIER_ATTR(used_bb_bmap);++staticssize_thwb_used_bw_bmap_show(structkobject*kobj,+structkobj_attribute*attr,char*buf)+{+ssize_twritten=0;+intcmg;+intcpu;++cmg=get_cmg_from_kobj(kobj);+for(cpu=0;cpu<num_possible_cpus();cpu++){+if(_hwinfo.core_map[cpu].cmg==cmg)+written+=scnprintf(buf+written,PAGE_SIZE-written,"%d %04lx\n",+cpu,_hwinfo.used_bw_bmap[cpu]);+}++returnwritten;+}+BARRIER_ATTR(used_bw_bmap);++staticssize_thwb_core_map_show(structkobject*kobj,+structkobj_attribute*attr,char*buf)+{+ssize_twritten=0;+intcmg;+intcpu;++cmg=get_cmg_from_kobj(kobj);+for(cpu=0;cpu<num_possible_cpus();cpu++){+if(_hwinfo.core_map[cpu].cmg==cmg)+written+=scnprintf(buf+written,PAGE_SIZE-written,"%d %d\n",+cpu,_hwinfo.core_map[cpu].ppe);+}++returnwritten;+}+BARRIER_ATTR(core_map);++staticstructattribute*hwb_attrs[]={+&hwb_used_bb_bmap_attribute.attr,+&hwb_used_bw_bmap_attribute.attr,+&hwb_core_map_attribute.attr,+NULL,+};++staticconststructattribute_grouphwb_attribute={+.attrs=hwb_attrs,+};++staticvoiddestroy_sysfs(void)+{+intcmg;+intbb;+inti;++sysfs_remove_file(&bar_miscdev.this_device->kobj,&hwb_hwinfo_attribute.attr);++for(cmg=0;cmg<_hwinfo.num_cmg;cmg++){+for(bb=0;bb<_hwinfo.num_bb;bb++){+i=(cmg*_hwinfo.num_bb)+bb;+if(battr[i].attr.attr.name)+sysfs_remove_file(cmg_kobj[cmg],&battr[i].attr.attr);+}+}+kfree(battr);++for(cmg=0;cmg<_hwinfo.num_cmg;cmg++){+if(cmg_kobj[cmg]){+sysfs_remove_group(cmg_kobj[cmg],&hwb_attribute);+kobject_put(cmg_kobj[cmg]);+}+}+kfree(cmg_kobj);+}++/* Create sysfs file under /sys/class/misc/fujitsu_hwb */+#define NAME_LEN 16+staticint__initinit_sysfs(void)+{+charname[NAME_LEN];+intret;+intcmg;+intbb;+inti;++/* Create file to show number of CMG/BB/BW/pe_per_cmg */+ret=sysfs_create_file(&bar_miscdev.this_device->kobj,&hwb_hwinfo_attribute.attr);+if(ret)+returnret;++cmg_kobj=kcalloc(_hwinfo.num_cmg,sizeof(structkobject*),GFP_KERNEL);+battr=kcalloc(_hwinfo.num_cmg*_hwinfo.num_bb,sizeof(structhwb_attr),GFP_KERNEL);+if(!cmg_kobj||!battr){+kfree(cmg_kobj);+kfree(battr);+return-ENOMEM;+}++/* Create folder for each CMG and create core_map/bitmap file */+for(cmg=0;cmg<_hwinfo.num_cmg;cmg++){+scnprintf(name,NAME_LEN,"CMG%d",cmg);+cmg_kobj[cmg]=kobject_create_and_add(name,&bar_miscdev.this_device->kobj);+if(!cmg_kobj[cmg]){+ret=-ENOMEM;+gotofail;+}++ret=sysfs_create_group(cmg_kobj[cmg],&hwb_attribute);+if(ret)+gotofail;+}++/* Create files for INIT_SYNC register */+for(cmg=0;cmg<_hwinfo.num_cmg;cmg++){+for(bb=0;bb<_hwinfo.num_bb;bb++){+i=(cmg*_hwinfo.num_bb)+bb;++scnprintf(name,NAME_LEN,"init_sync_bb%d",bb);+battr[i].bb=bb;+battr[i].attr.attr.name=kstrdup(name,GFP_KERNEL);+if(!battr[i].attr.attr.name){+ret=-ENOMEM;+gotofail;+}+battr[i].attr.attr.mode=0400;/* root only */+battr[i].attr.show=hwb_init_sync_bb_show;++sysfs_attr_init(&battr[i].attr.attr);+ret=sysfs_create_file(cmg_kobj[cmg],&battr[i].attr.attr);+if(ret<0)+gotofail;+}+}++return0;++fail:+destroy_sysfs();+returnret;+}+staticint__inithwb_init(void){intret;
@@ -0,0 +1,313 @@+// SPDX-License-Identifier: GPL-2.0+/*+*Copyright2020FUJITSULIMITED+*+*Thishardwarebarrier(HWB)driverprovidesasetofioctlstorealizesynchronization+*byPEsinthesameComeMemoryGroup(CMG)byusingimplementationdefinedregisters.+*OnA64FX,CMGisthesameasL3cachedomain.+*+*Themainpurposeofthedriverissettingupregisterswhichcannotbeaccessed+*fromEL0.However,afterinitialization,BST_SYNC/LBSY_SYNCregisterswhichisused+*insynchronizationmainlogiccanbeaccessedfromEL0(thereforeitisfast).+*+*Simplifiedbarrieroperationflowofuserapplicationisasfollows:+*(onePE)+*1.CallIOC_BB_ALLOCtosetupINIT_SYNCregisterwhichissharedinaCMG.+*ThisspecifieswhichPEsjoinsynchronization+*(oneachPEjoiningsynchronization)+*2.CallIOC_BW_ASSIGNtosetupASSIGN_SYNCregisterperPE+*3.Barriermainlogic(alllogicrunsinEL0)+*a)Write1toBST_SYNCregister+*b)ReadLBSY_SYNCregister+*c)IfLBSY_SYNCvalueis1,syncisfinished,otherwisegobacktob+*(IfallPEsjoiningsynchronizationwrite1toBST_SYNC,LBSY_SYNCbecomes1)+*4.CallIOC_BW_UNASSIGNtoresetASSIGN_SYNCregister+*(onePE)+*5.CallIOC_BB_FREEtoresetINIT_SYNCregister+*/++#include<asm/cputype.h>+#include<linux/bitfield.h>+#include<linux/bitops.h>+#include<linux/cpu.h>+#include<linux/cpumask.h>+#include<linux/kernel.h>+#include<linux/miscdevice.h>+#include<linux/module.h>+#include<linux/spinlock.h>+#include<linux/slab.h>+#include<linux/wait.h>++#ifdef pr_fmt+#undef pr_fmt+#endif+#define pr_fmt(fmt) "[%s:%s:%d] " fmt, KBUILD_MODNAME, __func__, __LINE__++/* Since miscdevice is used, /dev/fujitsu_hwb will be created when module is loaded */+#define FHWB_DEV_NAME "fujitsu_hwb"++/* Implementation defined registers for barrier shared in CMG */+#define FHWB_INIT_SYNC_BB0_EL1 sys_reg(3, 0, 15, 13, 0)+#define FHWB_INIT_SYNC_BB1_EL1 sys_reg(3, 0, 15, 13, 1)+#define FHWB_INIT_SYNC_BB2_EL1 sys_reg(3, 0, 15, 13, 2)+#define FHWB_INIT_SYNC_BB3_EL1 sys_reg(3, 0, 15, 13, 3)+#define FHWB_INIT_SYNC_BB4_EL1 sys_reg(3, 0, 15, 13, 4)+#define FHWB_INIT_SYNC_BB5_EL1 sys_reg(3, 0, 15, 13, 5)++/* Implementation defined registers for barrier per PE */+#define FHWB_CTRL_EL1 sys_reg(3, 0, 11, 12, 0)+#define FHWB_BST_BIT_EL1 sys_reg(3, 0, 11, 12, 4)+#define FHWB_ASSIGN_SYNC_W0_EL1 sys_reg(3, 0, 15, 15, 0)+#define FHWB_ASSIGN_SYNC_W1_EL1 sys_reg(3, 0, 15, 15, 1)+#define FHWB_ASSIGN_SYNC_W2_EL1 sys_reg(3, 0, 15, 15, 2)+#define FHWB_ASSIGN_SYNC_W3_EL1 sys_reg(3, 0, 15, 15, 3)++/* Field definitions for above registers */+#define FHWB_INIT_SYNC_BB_EL1_MASK_FIELD GENMASK_ULL(44, 32)+#define FHWB_INIT_SYNC_BB_EL1_BST_FIELD GENMASK_ULL(12, 0)+#define FHWB_CTRL_EL1_EL1AE BIT_ULL(63)+#define FHWB_CTRL_EL1_EL0AE BIT_ULL(62)+#define FHWB_BST_BIT_EL1_CMG_FILED GENMASK_ULL(5, 4)+#define FHWB_BST_BIT_EL1_PE_FILED GENMASK_ULL(3, 0)+#define FHWB_ASSIGN_SYNC_W_EL1_VALID BIT_ULL(63)++staticenumcpuhp_state_hp_state;++/*+*EachPEhasitsownCMGandPhysicalPEnumber(determinedbyBST_BIT_EL1register).+*BarrieroperationcanbeperformedbyPEswhichbelongtothesameCMG.+*/+structpe_info{+/* CMG number of this PE */+u8cmg;+/* Physical PE number of this PE */+u8ppe;+};++/* Hardware information of running system */+structhwb_hwinfo{+/* CPU type (part number) */+unsignedinttype;+/* Number of CMG */+u8num_cmg;+/* Number of barrier blade(BB) per CMG */+u8num_bb;+/* Number of barrier window(BW) per PE */+u8num_bw;+/*+*MaximumnumberofPEperCMG.+*DependingonBIOSconfiguration,eachCMGhasuptomax_pe_per_cmgPEs+*andeachPEhasuniquephysicalPEnumberbetween0~(max_pe_per_cmg-1)+*/+u8max_pe_per_cmg;++/* Bitmap for currently allocated BB per CMG */+unsignedlong*used_bb_bmap;+/* Bitmap for currently allocated BW per PE */+unsignedlong*used_bw_bmap;+/* Mapping table of cpuid -> CMG/PE number */+structpe_info*core_map;+};+staticstructhwb_hwinfo_hwinfo;++/* List for barrier blade currently used per FD */+structhwb_private_data{+structlist_headbb_list;+spinlock_tlist_lock;+};++/* Each barrier blade info */+#define BB_FREEING 1+structbb_info{+/* cpumask for PEs which participate synchronization */+cpumask_var_tpemask;+/* cpumask for PEs which currently assigned BW for this BB */+cpumask_var_tassigned_pemask;+/* Added to hwb_private_data::bb_list */+structlist_headnode;+/* For indicating if this bb is currently being freed or not */+unsignedlongflag;+/* For waiting ongoing assign/unassign operation to finish before freeing BB */+wait_queue_head_twq;+/* Track ongoing assign/unassign operation count */+atomic_tongoing_assign_count;+/* CMG number of this blade */
nitpick: Double space currently after CMG that looks inconsistent.
+ u8 cmg;
+ /* BB number of this blade */
+ u8 bb;
+ /* Hold assigned window number of each PE corresponding to @assigned_pemask */
+ u8 *bw;
+ /* Track usage count as IOC_BB_FREE and IOC_BW_[UN]ASSIGN might be run in parallel */
+ struct kref kref;
+};
+static struct kmem_cache *bb_info_cachep;
+
+static const struct file_operations fujitsu_hwb_dev_fops = {
+ .owner = THIS_MODULE,
+};
+
+static struct miscdevice bar_miscdev = {
+ .fops = &fujitsu_hwb_dev_fops,
+ .minor = MISC_DYNAMIC_MINOR,
+ .mode = 0666,
+ .name = FHWB_DEV_NAME,
+};
+
+static void destroy_bb_info_cachep(void)
+{
+ kmem_cache_destroy(bb_info_cachep);
+}
+
+static int __init init_bb_info_cachep(void)
+{
+ /*
+ * Since cpumask value will be copied from userspace to the beginning of
+ * struct bb_info, use kmem_cache_create_usercopy to mark that region.
+ * Otherwise CONFIG_HARDENED_USERCOPY gives user_copy_warn.
+ */
+ bb_info_cachep = kmem_cache_create_usercopy("bb_info_cache", sizeof(struct bb_info),
+ 0, SLAB_HWCACHE_ALIGN, 0, sizeof(cpumask_var_t), NULL);
+ if (bb_info_cachep == NULL)
inconsistent on ! vs == NULL checks. I personally don't care which you use, but better to chose
one style and use if everywhere in a given driver.
For generic sounding function names, it is better to prefix with something driver specific.
perhaps hwb_alloc_map() or similar? Both avoids possible clashes of naming in future and
leads to more readable code as people then know the function is local.
preferred to use sizeof(*_hwinfo.core_map) as saves reviewer checking the types. Note
applies in other places as well.
Whilst it's nice to make these flexible in size, the separate allocations do add overheads.
Given num_possible_cpus is probably constrained, perhaps better to just make these fixed
size and big enough for all plausible usecases?
I'd prefer you check these individually and handle the frees explicitly. Makes for easier reviewing
as we can match each fail against the clean up.
+
+ /* 0 is valid number for both CMG/PE. Set all bits to 1 to represents uninitialized state */
+ memset(_hwinfo.core_map, 0xFF, sizeof(struct pe_info) * num_possible_cpus());
+
+ return 0;
+
+fail:
+ free_map();
+ return -ENOMEM;
+}
+
+/* Get this system's CPU type (part number). If it is not fujitsu CPU, return -1 */
+static int __init get_cpu_type(void)
+{
+ if (read_cpuid_implementor() != ARM_CPU_IMP_FUJITSU)
+ return -1;
Better to return a meaningful error code from here, then pass it on at the caller.
+
+ return read_cpuid_part_number();
+}
+
+static int __init setup_hwinfo(void)
+{
+ int type;
+
+ type = get_cpu_type();
+ if (type < 0)
As above, I'd expect to see return type; here.
+ return -ENODEV;
+
+ _hwinfo.type = type;
+ switch (type) {
+ case FUJITSU_CPU_PART_A64FX:
+ _hwinfo.num_cmg = 4;
+ _hwinfo.num_bb = 6;
+ _hwinfo.num_bw = 4;
+ _hwinfo.max_pe_per_cmg = 13;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int hwb_cpu_online(unsigned int cpu)
+{
+ u64 val;
+ int i;
+
+ /* Setup core_map by reading BST_BIT_EL1 register of each PE */
+ val = read_sysreg_s(FHWB_BST_BIT_EL1);
+ _hwinfo.core_map[cpu].cmg = FIELD_GET(FHWB_BST_BIT_EL1_CMG_FILED, val);
+ _hwinfo.core_map[cpu].ppe = FIELD_GET(FHWB_BST_BIT_EL1_PE_FILED, val);
+
+ /* Since these registers' values are UNKNOWN on reset, explicitly clear all */
+ for (i = 0; i < _hwinfo.num_bw; i++)
+ write_bw_reg(i, 0);
+
+ write_sysreg_s(0, FHWB_CTRL_EL1);
+
+ return 0;
+}
+
+static int __init hwb_init(void)
+{
+ int ret;
+
+ ret = setup_hwinfo();
+ if (ret < 0) {
As it's not obvious from the function name that the only thing it is doing is
checking the cpu type, I'd move this error print into that function call or
rename setup_hwinfo()
+ pr_err("Unsupported CPU type\n");
+ return ret;
+ }
+
+ ret = alloc_map();
+ if (ret < 0)
+ return ret;
+
+ ret = init_bb_info_cachep();
+ if (ret < 0)
+ goto out1;
+
+ /*
+ * Setup cpuhp callback to ensure each PE's resource will be initialized
+ * even if some PEs are offline at this point
+ */
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/fujitsu_hwb:online",
+ hwb_cpu_online, NULL);
+ if (ret < 0) {
+ pr_err("cpuhp setup failed: %d\n", ret);
+ goto out2;
+ }
+ _hp_state = ret;
+
+ ret = misc_register(&bar_miscdev);
+ if (ret < 0) {
+ pr_err("misc_register failed: %d\n", ret);
+ goto out3;
+ }
+
+ return 0;
+
+out3:
+ cpuhp_remove_state(_hp_state);
+out2:
+ destroy_bb_info_cachep();
+out1:
+ free_map();
+
+ return ret;
+}
+
+static void __exit hwb_exit(void)
+{
+ misc_deregister(&bar_miscdev);
+ cpuhp_remove_state(_hp_state);
+ destroy_bb_info_cachep();
+ free_map();
+}
+
+module_init(hwb_init);
+module_exit(hwb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("FUJITSU LIMITED");
+MODULE_DESCRIPTION("FUJITSU HPC Hardware Barrier Driver");
On Fri, Jan 8, 2021 at 11:32 AM Misono Tomohiro
[off-list ref] wrote:
Subject: [RFC PATCH 00/10] Add Fujitsu A64FX soc entry/hardware barrier driver
[RFC]
This is the first time we upstream drivers for our chip and I want to
confirm driver location and patch submission process.
Based on my observation it seems drivers/soc folder is right place to put
this driver, so I added Kconfig entry for arm64 platform config, created
soc/fujitsu folder and updated MAINTAINER entry accordingly (last patch).
Is it right?
This looks good as a start. It may be possible that during review, we
come up with a different location or a different user interface that may
change the code, but if it stays in drivers/soc/fujitsu, then the other
steps are absolutely right.
Also for final submission I think I need to 1) create some public git
tree to push driver code (github or something), 2) make pull request to
SOC team (soc@kernel.org). Is it a correct procedure?
Yes. I would prefer something other than github, e.g. an account
on a fujitsu.com host, on kernel.org, or on git.linaro.org, but github
works if none of the alternatives are easy for you.
When you send a pull request, make sure you sign the tag with
a gpg key, ideally after getting it on the kernel.org keyring [1].
Arnd
[1] https://korg.docs.kernel.org/pgpkeys.html
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Fri, Jan 8, 2021 at 11:32 AM Misono Tomohiro
[off-list ref] wrote:
+ *
+ * This hardware barrier (HWB) driver provides a set of ioctls to realize synchronization
+ * by PEs in the same Come Memory Group (CMG) by using implementation defined registers.
+ * On A64FX, CMG is the same as L3 cache domain.
+ *
+ * The main purpose of the driver is setting up registers which cannot be accessed
+ * from EL0. However, after initialization, BST_SYNC/LBSY_SYNC registers which is used
+ * in synchronization main logic can be accessed from EL0 (therefore it is fast).
+ *
+ * Simplified barrier operation flow of user application is as follows:
+ * (one PE)
+ * 1. Call IOC_BB_ALLOC to setup INIT_SYNC register which is shared in a CMG.
+ * This specifies which PEs join synchronization
+ * (on each PE joining synchronization)
+ * 2. Call IOC_BW_ASSIGN to setup ASSIGN_SYNC register per PE
+ * 3. Barrier main logic (all logic runs in EL0)
+ * a) Write 1 to BST_SYNC register
+ * b) Read LBSY_SYNC register
+ * c) If LBSY_SYNC value is 1, sync is finished, otherwise go back to b
+ * (If all PEs joining synchronization write 1 to BST_SYNC, LBSY_SYNC becomes 1)
+ * 4. Call IOC_BW_UNASSIGN to reset ASSIGN_SYNC register
+ * (one PE)
+ * 5. Call IOC_BB_FREE to reset INIT_SYNC register
+ */
On a very general note, I would like to see some background about how
specific this functionality is to the specific design of A64fx. If there are
other processors with a similar requirement, then it would be best to
define a more abstract user API that can work for any such product.
+static int __init hwb_init(void)
+{
+ int ret;
+
+ ret = setup_hwinfo();
+ if (ret < 0) {
+ pr_err("Unsupported CPU type\n");
+ return ret;
+ }
Loading the module on a different machine should not print a warning:
In general, we want it to be possible to have all hardware specific
drivers built into the kernel, but not print any irritating messages
when they are simply not used on the hardware.
One way to avoid this would be to use a platform_driver() that
only gets loaded when a corresponding hardware device of some
sort is found, or ignored otherwise.
Arnd
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
@@ -0,0 +1,313 @@+// SPDX-License-Identifier: GPL-2.0+/*+*Copyright2020FUJITSULIMITED+*+*Thishardwarebarrier(HWB)driverprovidesasetofioctlsto+realizesynchronization+*byPEsinthesameComeMemoryGroup(CMG)byusingimplementationdefinedregisters.+*OnA64FX,CMGisthesameasL3cachedomain.+*+*Themainpurposeofthedriverissettingupregisterswhich+cannotbeaccessed+*fromEL0.However,afterinitialization,BST_SYNC/LBSY_SYNC+registerswhichisused+*insynchronizationmainlogiccanbeaccessedfromEL0(thereforeitisfast).+*+*Simplifiedbarrieroperationflowofuserapplicationisasfollows:+*(onePE)+*1.CallIOC_BB_ALLOCtosetupINIT_SYNCregisterwhichissharedinaCMG.+*ThisspecifieswhichPEsjoinsynchronization+*(oneachPEjoiningsynchronization)+*2.CallIOC_BW_ASSIGNtosetupASSIGN_SYNCregisterperPE+*3.Barriermainlogic(alllogicrunsinEL0)+*a)Write1toBST_SYNCregister+*b)ReadLBSY_SYNCregister+*c)IfLBSY_SYNCvalueis1,syncisfinished,otherwisegobacktob+*(IfallPEsjoiningsynchronizationwrite1toBST_SYNC,LBSY_SYNCbecomes1)+*4.CallIOC_BW_UNASSIGNtoresetASSIGN_SYNCregister+*(onePE)+*5.CallIOC_BB_FREEtoresetINIT_SYNCregister+*/++#include<asm/cputype.h>+#include<linux/bitfield.h>+#include<linux/bitops.h>+#include<linux/cpu.h>+#include<linux/cpumask.h>+#include<linux/kernel.h>+#include<linux/miscdevice.h>+#include<linux/module.h>+#include<linux/spinlock.h>+#include<linux/slab.h>+#include<linux/wait.h>++#ifdef pr_fmt+#undef pr_fmt+#endif+#define pr_fmt(fmt) "[%s:%s:%d] " fmt, KBUILD_MODNAME, __func__,+__LINE__++/* Since miscdevice is used, /dev/fujitsu_hwb will be created when+moduleisloaded*/#defineFHWB_DEV_NAME"fujitsu_hwb"++/* Implementation defined registers for barrier shared in CMG */+#define FHWB_INIT_SYNC_BB0_EL1 sys_reg(3, 0, 15, 13, 0) #define+FHWB_INIT_SYNC_BB1_EL1sys_reg(3,0,15,13,1)#define+FHWB_INIT_SYNC_BB2_EL1sys_reg(3,0,15,13,2)#define+FHWB_INIT_SYNC_BB3_EL1sys_reg(3,0,15,13,3)#define+FHWB_INIT_SYNC_BB4_EL1sys_reg(3,0,15,13,4)#define+FHWB_INIT_SYNC_BB5_EL1sys_reg(3,0,15,13,5)++/* Implementation defined registers for barrier per PE */+#define FHWB_CTRL_EL1 sys_reg(3, 0, 11, 12, 0)+#define FHWB_BST_BIT_EL1 sys_reg(3, 0, 11, 12, 4)+#define FHWB_ASSIGN_SYNC_W0_EL1 sys_reg(3, 0, 15, 15, 0) #define+FHWB_ASSIGN_SYNC_W1_EL1sys_reg(3,0,15,15,1)#define+FHWB_ASSIGN_SYNC_W2_EL1sys_reg(3,0,15,15,2)#define+FHWB_ASSIGN_SYNC_W3_EL1sys_reg(3,0,15,15,3)++/* Field definitions for above registers */#define+FHWB_INIT_SYNC_BB_EL1_MASK_FIELDGENMASK_ULL(44,32)+#define FHWB_INIT_SYNC_BB_EL1_BST_FIELD GENMASK_ULL(12, 0)+#define FHWB_CTRL_EL1_EL1AE BIT_ULL(63)+#define FHWB_CTRL_EL1_EL0AE BIT_ULL(62)+#define FHWB_BST_BIT_EL1_CMG_FILED GENMASK_ULL(5, 4)+#define FHWB_BST_BIT_EL1_PE_FILED GENMASK_ULL(3, 0)+#define FHWB_ASSIGN_SYNC_W_EL1_VALID BIT_ULL(63)++staticenumcpuhp_state_hp_state;++/*+*EachPEhasitsownCMGandPhysicalPEnumber(determinedbyBST_BIT_EL1register).+*BarrieroperationcanbeperformedbyPEswhichbelongtothesameCMG.+*/+structpe_info{+/* CMG number of this PE */+u8cmg;+/* Physical PE number of this PE */+u8ppe;+};++/* Hardware information of running system */structhwb_hwinfo{+/* CPU type (part number) */+unsignedinttype;+/* Number of CMG */+u8num_cmg;+/* Number of barrier blade(BB) per CMG */+u8num_bb;+/* Number of barrier window(BW) per PE */+u8num_bw;+/*+*MaximumnumberofPEperCMG.+*DependingonBIOSconfiguration,eachCMGhasuptomax_pe_per_cmgPEs+*andeachPEhasuniquephysicalPEnumberbetween0~(max_pe_per_cmg-1)+*/+u8max_pe_per_cmg;++/* Bitmap for currently allocated BB per CMG */+unsignedlong*used_bb_bmap;+/* Bitmap for currently allocated BW per PE */+unsignedlong*used_bw_bmap;+/* Mapping table of cpuid -> CMG/PE number */+structpe_info*core_map;+};+staticstructhwb_hwinfo_hwinfo;++/* List for barrier blade currently used per FD */struct+hwb_private_data{+structlist_headbb_list;+spinlock_tlist_lock;+};++/* Each barrier blade info */+#define BB_FREEING 1+structbb_info{+/* cpumask for PEs which participate synchronization */+cpumask_var_tpemask;+/* cpumask for PEs which currently assigned BW for this BB */+cpumask_var_tassigned_pemask;+/* Added to hwb_private_data::bb_list */+structlist_headnode;+/* For indicating if this bb is currently being freed or not */+unsignedlongflag;+/* For waiting ongoing assign/unassign operation to finish before freeing BB */+wait_queue_head_twq;+/* Track ongoing assign/unassign operation count */+atomic_tongoing_assign_count;+/* CMG number of this blade */
nitpick: Double space currently after CMG that looks inconsistent.
Right. I will fix it.
quoted
+ u8 cmg;
+ /* BB number of this blade */
+ u8 bb;
+ /* Hold assigned window number of each PE corresponding to @assigned_pemask */
+ u8 *bw;
+ /* Track usage count as IOC_BB_FREE and IOC_BW_[UN]ASSIGN might be run in parallel */
+ struct kref kref;
+};
+static struct kmem_cache *bb_info_cachep;
+
+static const struct file_operations fujitsu_hwb_dev_fops = {
+ .owner = THIS_MODULE,
+};
+
+static struct miscdevice bar_miscdev = {
+ .fops = &fujitsu_hwb_dev_fops,
+ .minor = MISC_DYNAMIC_MINOR,
+ .mode = 0666,
+ .name = FHWB_DEV_NAME,
+};
+
+static void destroy_bb_info_cachep(void) {
+ kmem_cache_destroy(bb_info_cachep);
+}
+
+static int __init init_bb_info_cachep(void) {
+ /*
+ * Since cpumask value will be copied from userspace to the beginning of
+ * struct bb_info, use kmem_cache_create_usercopy to mark that region.
+ * Otherwise CONFIG_HARDENED_USERCOPY gives user_copy_warn.
+ */
+ bb_info_cachep = kmem_cache_create_usercopy("bb_info_cache", sizeof(struct bb_info),
+ 0, SLAB_HWCACHE_ALIGN, 0, sizeof(cpumask_var_t), NULL);
+ if (bb_info_cachep == NULL)
inconsistent on ! vs == NULL checks. I personally don't care which you use, but better to chose one style and use if
everywhere in a given driver.
For generic sounding function names, it is better to prefix with something driver specific.
perhaps hwb_alloc_map() or similar? Both avoids possible clashes of naming in future and leads to more readable code
as people then know the function is local.
preferred to use sizeof(*_hwinfo.core_map) as saves reviewer checking the types. Note applies in other places as well.
OK. I will fix it.
Whilst it's nice to make these flexible in size, the separate allocations do add overheads.
Given num_possible_cpus is probably constrained, perhaps better to just make these fixed size and big enough for all
plausible usecases?
Well, it might be possible that the number of CPUs may vary in systems (though currently all system has 1 CPU),
I'd rather keep current notation.
I'd prefer you check these individually and handle the frees explicitly. Makes for easier reviewing as we can match each
fail against the clean up.
OK, I will separate them.
quoted
+
+ /* 0 is valid number for both CMG/PE. Set all bits to 1 to represents uninitialized state */
+ memset(_hwinfo.core_map, 0xFF, sizeof(struct pe_info) *
+num_possible_cpus());
+
+ return 0;
+
+fail:
+ free_map();
+ return -ENOMEM;
+}
+
+/* Get this system's CPU type (part number). If it is not fujitsu
+CPU, return -1 */ static int __init get_cpu_type(void) {
+ if (read_cpuid_implementor() != ARM_CPU_IMP_FUJITSU)
+ return -1;
Better to return a meaningful error code from here, then pass it on at the caller.
OK. I will incorporate this code into hwinfo_setup() and return -ENODEV for error.
quoted
+
+ return read_cpuid_part_number();
+}
+
+static int __init setup_hwinfo(void)
+{
+ int type;
+
+ type = get_cpu_type();
+ if (type < 0)
As above, I'd expect to see return type; here.
quoted
+ return -ENODEV;
+
+ _hwinfo.type = type;
+ switch (type) {
+ case FUJITSU_CPU_PART_A64FX:
+ _hwinfo.num_cmg = 4;
+ _hwinfo.num_bb = 6;
+ _hwinfo.num_bw = 4;
+ _hwinfo.max_pe_per_cmg = 13;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int hwb_cpu_online(unsigned int cpu) {
+ u64 val;
+ int i;
+
+ /* Setup core_map by reading BST_BIT_EL1 register of each PE */
+ val = read_sysreg_s(FHWB_BST_BIT_EL1);
+ _hwinfo.core_map[cpu].cmg = FIELD_GET(FHWB_BST_BIT_EL1_CMG_FILED, val);
+ _hwinfo.core_map[cpu].ppe = FIELD_GET(FHWB_BST_BIT_EL1_PE_FILED,
+val);
+
+ /* Since these registers' values are UNKNOWN on reset, explicitly clear all */
+ for (i = 0; i < _hwinfo.num_bw; i++)
+ write_bw_reg(i, 0);
+
+ write_sysreg_s(0, FHWB_CTRL_EL1);
+
+ return 0;
+}
+
+static int __init hwb_init(void)
+{
+ int ret;
+
+ ret = setup_hwinfo();
+ if (ret < 0) {
As it's not obvious from the function name that the only thing it is doing is checking the cpu type, I'd move this error print
into that function call or rename setup_hwinfo()
I will remove this error print as following arnd's comment in a different thread.
Thanks,
Misono
quoted
+ pr_err("Unsupported CPU type\n");
+ return ret;
+ }
+
+ ret = alloc_map();
+ if (ret < 0)
+ return ret;
+
+ ret = init_bb_info_cachep();
+ if (ret < 0)
+ goto out1;
+
+ /*
+ * Setup cpuhp callback to ensure each PE's resource will be initialized
+ * even if some PEs are offline at this point
+ */
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/fujitsu_hwb:online",
+ hwb_cpu_online, NULL);
+ if (ret < 0) {
+ pr_err("cpuhp setup failed: %d\n", ret);
+ goto out2;
+ }
+ _hp_state = ret;
+
+ ret = misc_register(&bar_miscdev);
+ if (ret < 0) {
+ pr_err("misc_register failed: %d\n", ret);
+ goto out3;
+ }
+
+ return 0;
+
+out3:
+ cpuhp_remove_state(_hp_state);
+out2:
+ destroy_bb_info_cachep();
+out1:
+ free_map();
+
+ return ret;
+}
+
+static void __exit hwb_exit(void)
+{
+ misc_deregister(&bar_miscdev);
+ cpuhp_remove_state(_hp_state);
+ destroy_bb_info_cachep();
+ free_map();
+}
+
+module_init(hwb_init);
+module_exit(hwb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("FUJITSU LIMITED");
+MODULE_DESCRIPTION("FUJITSU HPC Hardware Barrier Driver");
On Fri, Jan 8, 2021 at 11:32 AM Misono Tomohiro
[off-list ref] wrote:
quoted
+ *
+ * This hardware barrier (HWB) driver provides a set of ioctls to realize synchronization
+ * by PEs in the same Come Memory Group (CMG) by using implementation defined registers.
+ * On A64FX, CMG is the same as L3 cache domain.
+ *
+ * The main purpose of the driver is setting up registers which cannot be accessed
+ * from EL0. However, after initialization, BST_SYNC/LBSY_SYNC registers which is used
+ * in synchronization main logic can be accessed from EL0 (therefore it is fast).
+ *
+ * Simplified barrier operation flow of user application is as follows:
+ * (one PE)
+ * 1. Call IOC_BB_ALLOC to setup INIT_SYNC register which is shared in a CMG.
+ * This specifies which PEs join synchronization
+ * (on each PE joining synchronization)
+ * 2. Call IOC_BW_ASSIGN to setup ASSIGN_SYNC register per PE
+ * 3. Barrier main logic (all logic runs in EL0)
+ * a) Write 1 to BST_SYNC register
+ * b) Read LBSY_SYNC register
+ * c) If LBSY_SYNC value is 1, sync is finished, otherwise go back to b
+ * (If all PEs joining synchronization write 1 to BST_SYNC, LBSY_SYNC becomes 1)
+ * 4. Call IOC_BW_UNASSIGN to reset ASSIGN_SYNC register
+ * (one PE)
+ * 5. Call IOC_BB_FREE to reset INIT_SYNC register
+ */
On a very general note, I would like to see some background about how
specific this functionality is to the specific design of A64fx. If there are
other processors with a similar requirement, then it would be best to
define a more abstract user API that can work for any such product.
+static int __init hwb_init(void)
+{
+ int ret;
+
+ ret = setup_hwinfo();
+ if (ret < 0) {
+ pr_err("Unsupported CPU type\n");
+ return ret;
+ }
Loading the module on a different machine should not print a warning:
In general, we want it to be possible to have all hardware specific
drivers built into the kernel, but not print any irritating messages
when they are simply not used on the hardware.
One way to avoid this would be to use a platform_driver() that
only gets loaded when a corresponding hardware device of some
sort is found, or ignored otherwise.
As far as I understand, to use platform_driver() the system needs to provide device tree or
ACPI entry for the driver. Target system uses ACPI but there is no corresponding ACPI HID
for this hardware barrier feature as that is an extension feature to the processor.
So, I thought it is not applicable to use platform_driver().
At least, I will remove this pr_err() If I send updated patch.
Regards,
Tomohiro
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel