Skip to content

Commit 4533803

Browse files
Thomas RichterVasily Gorbik
authored andcommitted
s390/cpumf: Allow multiple processes to access /dev/hwc
Commit a029a4e ("s390/cpumf: Allow concurrent access for CPU Measurement Counter Facility") added CPU Measurement counter facility access to multiple consumers. It allows concurrent access to the CPU Measurement counter facility via several perf_event_open() system call invocations and via ioctl() system call of device /dev/hwc. However the access via device /dev/hwc was exclusive, only one process was able to open this device. The patch removes this restriction. Now multiple invocations of lshwc can execute in parallel. They can access different CPUs and counter sets or CPUs and counter set can overlap. Signed-off-by: Thomas Richter <[email protected]> Suggested-by: Heiko Carstens <[email protected]> Reviewed-by: Sumanth Korikkar <[email protected]> Acked-by: Heiko Carstens <[email protected]> Signed-off-by: Vasily Gorbik <[email protected]>
1 parent ff7a1ee commit 4533803

File tree

1 file changed

+150
-78
lines changed

1 file changed

+150
-78
lines changed

arch/s390/kernel/perf_cpum_cf.c

Lines changed: 150 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -773,22 +773,46 @@ static int __init cpumf_pmu_init(void)
773773
* counter set via normal file operations.
774774
*/
775775

776-
static atomic_t cfset_opencnt = ATOMIC_INIT(0); /* Excl. access */
776+
static atomic_t cfset_opencnt = ATOMIC_INIT(0); /* Access count */
777777
static DEFINE_MUTEX(cfset_ctrset_mutex);/* Synchronize access to hardware */
778778
struct cfset_call_on_cpu_parm { /* Parm struct for smp_call_on_cpu */
779779
unsigned int sets; /* Counter set bit mask */
780780
atomic_t cpus_ack; /* # CPUs successfully executed func */
781781
};
782782

783-
static struct cfset_request { /* CPUs and counter set bit mask */
783+
static struct cfset_session { /* CPUs and counter set bit mask */
784+
struct list_head head; /* Head of list of active processes */
785+
} cfset_session = {
786+
.head = LIST_HEAD_INIT(cfset_session.head)
787+
};
788+
789+
struct cfset_request { /* CPUs and counter set bit mask */
784790
unsigned long ctrset; /* Bit mask of counter set to read */
785791
cpumask_t mask; /* CPU mask to read from */
786-
} cfset_request;
792+
struct list_head node; /* Chain to cfset_session.head */
793+
};
794+
795+
static void cfset_session_init(void)
796+
{
797+
INIT_LIST_HEAD(&cfset_session.head);
798+
}
799+
800+
/* Remove current request from global bookkeeping. Maintain a counter set bit
801+
* mask on a per CPU basis.
802+
* Done in process context under mutex protection.
803+
*/
804+
static void cfset_session_del(struct cfset_request *p)
805+
{
806+
list_del(&p->node);
807+
}
787808

788-
static void cfset_ctrset_clear(void)
809+
/* Add current request to global bookkeeping. Maintain a counter set bit mask
810+
* on a per CPU basis.
811+
* Done in process context under mutex protection.
812+
*/
813+
static void cfset_session_add(struct cfset_request *p)
789814
{
790-
cpumask_clear(&cfset_request.mask);
791-
cfset_request.ctrset = 0;
815+
list_add(&p->node, &cfset_session.head);
792816
}
793817

794818
/* The /dev/hwctr device access uses PMU_F_IN_USE to mark the device access
@@ -827,15 +851,23 @@ static void cfset_ioctl_off(void *parm)
827851
struct cfset_call_on_cpu_parm *p = parm;
828852
int rc;
829853

830-
cpuhw->dev_state = 0;
854+
/* Check if any counter set used by /dev/hwc */
831855
for (rc = CPUMF_CTR_SET_BASIC; rc < CPUMF_CTR_SET_MAX; ++rc)
832-
if ((p->sets & cpumf_ctr_ctl[rc]))
833-
atomic_dec(&cpuhw->ctr_set[rc]);
834-
rc = lcctl(cpuhw->state); /* Keep perf_event_open counter sets */
856+
if ((p->sets & cpumf_ctr_ctl[rc])) {
857+
if (!atomic_dec_return(&cpuhw->ctr_set[rc])) {
858+
ctr_set_disable(&cpuhw->dev_state,
859+
cpumf_ctr_ctl[rc]);
860+
ctr_set_stop(&cpuhw->dev_state,
861+
cpumf_ctr_ctl[rc]);
862+
}
863+
}
864+
/* Keep perf_event_open counter sets */
865+
rc = lcctl(cpuhw->dev_state | cpuhw->state);
835866
if (rc)
836867
pr_err("Counter set stop %#llx of /dev/%s failed rc=%i\n",
837868
cpuhw->state, S390_HWCTR_DEVICE, rc);
838-
cpuhw->flags &= ~PMU_F_IN_USE;
869+
if (!cpuhw->dev_state)
870+
cpuhw->flags &= ~PMU_F_IN_USE;
839871
debug_sprintf_event(cf_dbg, 4, "%s rc %d state %#llx dev_state %#llx\n",
840872
__func__, rc, cpuhw->state, cpuhw->dev_state);
841873
}
@@ -870,66 +902,76 @@ static void cfset_release_cpu(void *p)
870902

871903
debug_sprintf_event(cf_dbg, 4, "%s state %#llx dev_state %#llx\n",
872904
__func__, cpuhw->state, cpuhw->dev_state);
905+
cpuhw->dev_state = 0;
873906
rc = lcctl(cpuhw->state); /* Keep perf_event_open counter sets */
874907
if (rc)
875908
pr_err("Counter set release %#llx of /dev/%s failed rc=%i\n",
876909
cpuhw->state, S390_HWCTR_DEVICE, rc);
877-
cpuhw->dev_state = 0;
910+
}
911+
912+
/* This modifies the process CPU mask to adopt it to the currently online
913+
* CPUs. Offline CPUs can not be addresses. This call terminates the access
914+
* and is usually followed by close() or a new iotcl(..., START, ...) which
915+
* creates a new request structure.
916+
*/
917+
static void cfset_all_stop(struct cfset_request *req)
918+
{
919+
struct cfset_call_on_cpu_parm p = {
920+
.sets = req->ctrset,
921+
};
922+
923+
cpumask_and(&req->mask, &req->mask, cpu_online_mask);
924+
on_each_cpu_mask(&req->mask, cfset_ioctl_off, &p, 1);
878925
}
879926

880927
/* Release function is also called when application gets terminated without
881928
* doing a proper ioctl(..., S390_HWCTR_STOP, ...) command.
882929
*/
883930
static int cfset_release(struct inode *inode, struct file *file)
884931
{
885-
on_each_cpu(cfset_release_cpu, NULL, 1);
932+
mutex_lock(&cfset_ctrset_mutex);
933+
/* Open followed by close/exit has no private_data */
934+
if (file->private_data) {
935+
cfset_all_stop(file->private_data);
936+
cfset_session_del(file->private_data);
937+
kfree(file->private_data);
938+
file->private_data = NULL;
939+
}
940+
if (!atomic_dec_return(&cfset_opencnt))
941+
on_each_cpu(cfset_release_cpu, NULL, 1);
942+
mutex_unlock(&cfset_ctrset_mutex);
943+
886944
hw_perf_event_destroy(NULL);
887-
cfset_ctrset_clear();
888-
atomic_set(&cfset_opencnt, 0);
889945
return 0;
890946
}
891947

892948
static int cfset_open(struct inode *inode, struct file *file)
893949
{
894950
if (!capable(CAP_SYS_ADMIN))
895951
return -EPERM;
896-
/* Only one user space program can open /dev/hwctr */
897-
if (atomic_xchg(&cfset_opencnt, 1))
898-
return -EBUSY;
952+
mutex_lock(&cfset_ctrset_mutex);
953+
if (atomic_inc_return(&cfset_opencnt) == 1)
954+
cfset_session_init();
955+
mutex_unlock(&cfset_ctrset_mutex);
899956

900957
cpumf_hw_inuse();
901958
file->private_data = NULL;
902959
/* nonseekable_open() never fails */
903960
return nonseekable_open(inode, file);
904961
}
905962

906-
static int cfset_all_stop(void)
963+
static int cfset_all_start(struct cfset_request *req)
907964
{
908965
struct cfset_call_on_cpu_parm p = {
909-
.sets = cfset_request.ctrset,
910-
};
911-
cpumask_var_t mask;
912-
913-
if (!alloc_cpumask_var(&mask, GFP_KERNEL))
914-
return -ENOMEM;
915-
cpumask_and(mask, &cfset_request.mask, cpu_online_mask);
916-
on_each_cpu_mask(mask, cfset_ioctl_off, &p, 1);
917-
free_cpumask_var(mask);
918-
return 0;
919-
}
920-
921-
static int cfset_all_start(void)
922-
{
923-
struct cfset_call_on_cpu_parm p = {
924-
.sets = cfset_request.ctrset,
966+
.sets = req->ctrset,
925967
.cpus_ack = ATOMIC_INIT(0),
926968
};
927969
cpumask_var_t mask;
928970
int rc = 0;
929971

930972
if (!alloc_cpumask_var(&mask, GFP_KERNEL))
931973
return -ENOMEM;
932-
cpumask_and(mask, &cfset_request.mask, cpu_online_mask);
974+
cpumask_and(mask, &req->mask, cpu_online_mask);
933975
on_each_cpu_mask(mask, cfset_ioctl_on, &p, 1);
934976
if (atomic_read(&p.cpus_ack) != cpumask_weight(mask)) {
935977
on_each_cpu_mask(mask, cfset_ioctl_off, &p, 1);
@@ -1045,7 +1087,7 @@ static void cfset_cpu_read(void *parm)
10451087
cpuhw->sets, cpuhw->used);
10461088
}
10471089

1048-
static int cfset_all_read(unsigned long arg)
1090+
static int cfset_all_read(unsigned long arg, struct cfset_request *req)
10491091
{
10501092
struct cfset_call_on_cpu_parm p;
10511093
cpumask_var_t mask;
@@ -1054,46 +1096,53 @@ static int cfset_all_read(unsigned long arg)
10541096
if (!alloc_cpumask_var(&mask, GFP_KERNEL))
10551097
return -ENOMEM;
10561098

1057-
p.sets = cfset_request.ctrset;
1058-
cpumask_and(mask, &cfset_request.mask, cpu_online_mask);
1099+
p.sets = req->ctrset;
1100+
cpumask_and(mask, &req->mask, cpu_online_mask);
10591101
on_each_cpu_mask(mask, cfset_cpu_read, &p, 1);
10601102
rc = cfset_all_copy(arg, mask);
10611103
free_cpumask_var(mask);
10621104
return rc;
10631105
}
10641106

1065-
static long cfset_ioctl_read(unsigned long arg)
1107+
static long cfset_ioctl_read(unsigned long arg, struct cfset_request *req)
10661108
{
10671109
struct s390_ctrset_read read;
1068-
int ret = 0;
1110+
int ret = -ENODATA;
10691111

1070-
if (copy_from_user(&read, (char __user *)arg, sizeof(read)))
1071-
return -EFAULT;
1072-
ret = cfset_all_read(arg);
1112+
if (req && req->ctrset) {
1113+
if (copy_from_user(&read, (char __user *)arg, sizeof(read)))
1114+
return -EFAULT;
1115+
ret = cfset_all_read(arg, req);
1116+
}
10731117
return ret;
10741118
}
10751119

1076-
static long cfset_ioctl_stop(void)
1120+
static long cfset_ioctl_stop(struct file *file)
10771121
{
1078-
int ret = ENXIO;
1079-
1080-
if (cfset_request.ctrset) {
1081-
ret = cfset_all_stop();
1082-
cfset_ctrset_clear();
1122+
struct cfset_request *req = file->private_data;
1123+
int ret = -ENXIO;
1124+
1125+
if (req) {
1126+
cfset_all_stop(req);
1127+
cfset_session_del(req);
1128+
kfree(req);
1129+
file->private_data = NULL;
1130+
ret = 0;
10831131
}
10841132
return ret;
10851133
}
10861134

1087-
static long cfset_ioctl_start(unsigned long arg)
1135+
static long cfset_ioctl_start(unsigned long arg, struct file *file)
10881136
{
10891137
struct s390_ctrset_start __user *ustart;
10901138
struct s390_ctrset_start start;
1139+
struct cfset_request *preq;
10911140
void __user *umask;
10921141
unsigned int len;
10931142
int ret = 0;
10941143
size_t need;
10951144

1096-
if (cfset_request.ctrset)
1145+
if (file->private_data)
10971146
return -EBUSY;
10981147
ustart = (struct s390_ctrset_start __user *)arg;
10991148
if (copy_from_user(&start, ustart, sizeof(start)))
@@ -1108,25 +1157,36 @@ static long cfset_ioctl_start(unsigned long arg)
11081157
return -EINVAL; /* Invalid counter set */
11091158
if (!start.counter_sets)
11101159
return -EINVAL; /* No counter set at all? */
1111-
cpumask_clear(&cfset_request.mask);
1160+
1161+
preq = kzalloc(sizeof(*preq), GFP_KERNEL);
1162+
if (!preq)
1163+
return -ENOMEM;
1164+
cpumask_clear(&preq->mask);
11121165
len = min_t(u64, start.cpumask_len, cpumask_size());
11131166
umask = (void __user *)start.cpumask;
1114-
if (copy_from_user(&cfset_request.mask, umask, len))
1167+
if (copy_from_user(&preq->mask, umask, len)) {
1168+
kfree(preq);
11151169
return -EFAULT;
1116-
if (cpumask_empty(&cfset_request.mask))
1170+
}
1171+
if (cpumask_empty(&preq->mask)) {
1172+
kfree(preq);
11171173
return -EINVAL;
1174+
}
11181175
need = cfset_needspace(start.counter_sets);
1119-
if (put_user(need, &ustart->data_bytes))
1120-
ret = -EFAULT;
1121-
if (ret)
1122-
goto out;
1123-
cfset_request.ctrset = start.counter_sets;
1124-
ret = cfset_all_start();
1125-
out:
1126-
if (ret)
1127-
cfset_ctrset_clear();
1128-
debug_sprintf_event(cf_dbg, 4, "%s sets %#lx need %ld ret %d\n",
1129-
__func__, cfset_request.ctrset, need, ret);
1176+
if (put_user(need, &ustart->data_bytes)) {
1177+
kfree(preq);
1178+
return -EFAULT;
1179+
}
1180+
preq->ctrset = start.counter_sets;
1181+
ret = cfset_all_start(preq);
1182+
if (!ret) {
1183+
cfset_session_add(preq);
1184+
file->private_data = preq;
1185+
debug_sprintf_event(cf_dbg, 4, "%s set %#lx need %ld ret %d\n",
1186+
__func__, preq->ctrset, need, ret);
1187+
} else {
1188+
kfree(preq);
1189+
}
11301190
return ret;
11311191
}
11321192

@@ -1136,7 +1196,7 @@ static long cfset_ioctl_start(unsigned long arg)
11361196
* counter set keeps running until explicitly stopped. Returns the number
11371197
* of bytes needed to store the counter values. If another S390_HWCTR_START
11381198
* ioctl subcommand is called without a previous S390_HWCTR_STOP stop
1139-
* command, -EBUSY is returned.
1199+
* command on the same file descriptor, -EBUSY is returned.
11401200
* S390_HWCTR_READ: Read the counter set values from specified CPU list given
11411201
* with the S390_HWCTR_START command.
11421202
* S390_HWCTR_STOP: Stops the counter sets on the CPU list given with the
@@ -1150,13 +1210,13 @@ static long cfset_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
11501210
mutex_lock(&cfset_ctrset_mutex);
11511211
switch (cmd) {
11521212
case S390_HWCTR_START:
1153-
ret = cfset_ioctl_start(arg);
1213+
ret = cfset_ioctl_start(arg, file);
11541214
break;
11551215
case S390_HWCTR_STOP:
1156-
ret = cfset_ioctl_stop();
1216+
ret = cfset_ioctl_stop(file);
11571217
break;
11581218
case S390_HWCTR_READ:
1159-
ret = cfset_ioctl_read(arg);
1219+
ret = cfset_ioctl_read(arg, file->private_data);
11601220
break;
11611221
default:
11621222
ret = -ENOTTY;
@@ -1182,29 +1242,41 @@ static struct miscdevice cfset_dev = {
11821242
.fops = &cfset_fops,
11831243
};
11841244

1245+
/* Hotplug add of a CPU. Scan through all active processes and add
1246+
* that CPU to the list of CPUs supplied with ioctl(..., START, ...).
1247+
*/
11851248
int cfset_online_cpu(unsigned int cpu)
11861249
{
11871250
struct cfset_call_on_cpu_parm p;
1251+
struct cfset_request *rp;
11881252

11891253
mutex_lock(&cfset_ctrset_mutex);
1190-
if (cfset_request.ctrset) {
1191-
p.sets = cfset_request.ctrset;
1192-
cfset_ioctl_on(&p);
1193-
cpumask_set_cpu(cpu, &cfset_request.mask);
1254+
if (!list_empty(&cfset_session.head)) {
1255+
list_for_each_entry(rp, &cfset_session.head, node) {
1256+
p.sets = rp->ctrset;
1257+
cfset_ioctl_on(&p);
1258+
cpumask_set_cpu(cpu, &rp->mask);
1259+
}
11941260
}
11951261
mutex_unlock(&cfset_ctrset_mutex);
11961262
return 0;
11971263
}
11981264

1265+
/* Hotplug remove of a CPU. Scan through all active processes and clear
1266+
* that CPU from the list of CPUs supplied with ioctl(..., START, ...).
1267+
*/
11991268
int cfset_offline_cpu(unsigned int cpu)
12001269
{
12011270
struct cfset_call_on_cpu_parm p;
1271+
struct cfset_request *rp;
12021272

12031273
mutex_lock(&cfset_ctrset_mutex);
1204-
if (cfset_request.ctrset) {
1205-
p.sets = cfset_request.ctrset;
1206-
cfset_ioctl_off(&p);
1207-
cpumask_clear_cpu(cpu, &cfset_request.mask);
1274+
if (!list_empty(&cfset_session.head)) {
1275+
list_for_each_entry(rp, &cfset_session.head, node) {
1276+
p.sets = rp->ctrset;
1277+
cfset_ioctl_off(&p);
1278+
cpumask_clear_cpu(cpu, &rp->mask);
1279+
}
12081280
}
12091281
mutex_unlock(&cfset_ctrset_mutex);
12101282
return 0;

0 commit comments

Comments
 (0)