s390 fixes for 7.1-rc5

- Fix PAI NNPA mismatch between counting and recording, where
   sampling reports twice the value
 
 - Fix loss of PAI counter increments during recording on systems
   with many CPUs under heavy load, while counting is not affected
 
 - On some supported machines, CHSC cannot access memory outside
   the DMA zone, causing CHSC command failures. Restore GFP_DMA
   flag when allocating memory for CHSC control blocks
 
 - Align the numbering scheme for higher-level topology structures
   like socket, book, drawer with other hardware identifiers e.g.
   in sysfs, procfs and tools like lscpu
 -----BEGIN PGP SIGNATURE-----
 
 iI0EABYKADUWIQQrtrZiYVkVzKQcYivNdxKlNrRb8AUCag8nxxccYWdvcmRlZXZA
 bGludXguaWJtLmNvbQAKCRDNdxKlNrRb8OulAQC/lQlKztFCEUad/yCXhfZnJbwz
 zqUrq/5+JyKm8w1kPgEAq1cYMpzoUdcQ1D8N889SLe6o5aUPHO1fBu3VC6V0cg0=
 =OS/x
 -----END PGP SIGNATURE-----

Merge tag 's390-7.1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 fixes from Alexander Gordeev:

 - Fix PAI NNPA mismatch between counting and recording, where sampling
   reports twice the value

 - Fix loss of PAI counter increments during recording on systems with
   many CPUs under heavy load, while counting is not affected

 - On some supported machines, CHSC cannot access memory outside the DMA
   zone, causing CHSC command failures. Restore GFP_DMA flag when
   allocating memory for CHSC control blocks

 - Align the numbering scheme for higher-level topology structures like
   socket, book, drawer with other hardware identifiers e.g. in sysfs,
   procfs and tools like lscpu

* tag 's390-7.1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
  s390/topology: Use zero-based numbering for containing entities
  s390/cio: Restore GFP_DMA for CHSC allocation
  s390/pai: Fix missing PAI counter increments under heavy load
  s390/pai: Disable duplicate read of kernel PAI counter value
This commit is contained in:
Linus Torvalds 2026-05-22 06:40:31 -07:00
commit cbadb98b7c
5 changed files with 41 additions and 26 deletions

View File

@ -186,6 +186,13 @@ static u64 pai_getctr(unsigned long *page, int nr, unsigned long offset)
return page[nr];
}
static void pai_setctr(unsigned long *page, int nr, unsigned long offset, u64 v)
{
if (offset)
nr += offset / sizeof(*page);
page[nr] = v;
}
/* Read the counter values. Return value from location in CMP. For base
* event xxx_ALL sum up all events. Returns counter value.
*/
@ -551,6 +558,8 @@ static void paicrypt_del(struct perf_event *event, int flags)
/* Create raw data and save it in buffer. Calculate the delta for each
* counter between this invocation and the last invocation.
* Returns number of bytes copied.
* After reading from PAI counter page, save the read value to the old
* page to calculate PAI counter deltas.
* Saves only entries with positive counter difference of the form
* 2 bytes: Number of counter
* 8 bytes: Value of counter
@ -562,16 +571,22 @@ static size_t pai_copy(struct pai_userdata *userdata, unsigned long *page,
int i, outidx = 0;
for (i = 1; i <= pp->num_avail; i++) {
u64 val = 0, val_old = 0;
u64 val = 0, val_old = 0, val_k = 0, val_old_k = 0;
if (!exclude_kernel) {
val += pai_getctr(page, i, pp->kernel_offset);
val_old += pai_getctr(page_old, i, pp->kernel_offset);
val_k = pai_getctr(page, i, pp->kernel_offset);
val_old_k = pai_getctr(page_old, i, pp->kernel_offset);
if (val_k != val_old_k)
pai_setctr(page_old, i, pp->kernel_offset, val_k);
}
if (!exclude_user) {
val += pai_getctr(page, i, 0);
val_old += pai_getctr(page_old, i, 0);
val = pai_getctr(page, i, 0);
val_old = pai_getctr(page_old, i, 0);
if (val != val_old)
pai_setctr(page_old, i, 0, val);
}
val += val_k;
val_old += val_old_k;
if (val >= val_old)
val -= val_old;
else
@ -602,8 +617,6 @@ static size_t pai_copy(struct pai_userdata *userdata, unsigned long *page,
static int pai_push_sample(size_t rawsize, struct pai_map *cpump,
struct perf_event *event)
{
int idx = PAI_PMU_IDX(event);
struct pai_pmu *pp = &pai_pmu[idx];
struct perf_sample_data data;
struct perf_raw_record raw;
struct pt_regs regs;
@ -634,8 +647,6 @@ static int pai_push_sample(size_t rawsize, struct pai_map *cpump,
overflow = perf_event_overflow(event, &data, &regs);
perf_event_update_userpage(event);
/* Save crypto counter lowcore page after reading event data. */
memcpy((void *)PAI_SAVE_AREA(event), cpump->area, pp->area_size);
return overflow;
}
@ -651,7 +662,7 @@ static void pai_have_sample(struct perf_event *event, struct pai_map *cpump)
rawsize = pai_copy(cpump->save, cpump->area, pp,
(unsigned long *)PAI_SAVE_AREA(event),
event->attr.exclude_user,
event->attr.exclude_kernel);
!pp->kernel_offset ? true : event->attr.exclude_kernel);
if (rawsize) /* No incremented counters */
pai_push_sample(rawsize, cpump, event);
}

View File

@ -192,17 +192,21 @@ static void tl_to_masks(struct sysinfo_15_1_x *info)
end = (union topology_entry *)((unsigned long)info + info->length);
while (tle < end) {
switch (tle->nl) {
/*
* Adjust drawer_id, book_id, and socked_id so they match the
* numbering scheme of e.g. the hardware management console.
*/
case 3:
drawer = drawer->next;
drawer->id = tle->container.id;
drawer->id = tle->container.id - 1;
break;
case 2:
book = book->next;
book->id = tle->container.id;
book->id = tle->container.id - 1;
break;
case 1:
socket = socket->next;
socket->id = tle->container.id;
socket->id = tle->container.id - 1;
break;
case 0:
add_cpus_to_mask(&tle->cpu, drawer, book, socket);

View File

@ -1142,8 +1142,8 @@ int __init chsc_init(void)
{
int ret;
sei_page = (void *)get_zeroed_page(GFP_KERNEL);
chsc_page = (void *)get_zeroed_page(GFP_KERNEL);
sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sei_page || !chsc_page) {
ret = -ENOMEM;
goto out_err;

View File

@ -292,7 +292,7 @@ static int chsc_ioctl_start(void __user *user_area)
if (!css_general_characteristics.dynio)
/* It makes no sense to try. */
return -EOPNOTSUPP;
chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
if (!chsc_area)
return -ENOMEM;
request = kzalloc_obj(*request);
@ -340,7 +340,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area)
ret = -ENOMEM;
goto out_unlock;
}
on_close_chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
if (!on_close_chsc_area) {
ret = -ENOMEM;
goto out_free_request;
@ -392,7 +392,7 @@ static int chsc_ioctl_start_sync(void __user *user_area)
struct chsc_sync_area *chsc_area;
int ret, ccode;
chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!chsc_area)
return -ENOMEM;
if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
@ -438,7 +438,7 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *scpcd_area;
scpcd_area = (void *)get_zeroed_page(GFP_KERNEL);
scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scpcd_area)
return -ENOMEM;
cd = kzalloc_obj(*cd);
@ -500,7 +500,7 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *scucd_area;
scucd_area = (void *)get_zeroed_page(GFP_KERNEL);
scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scucd_area)
return -ENOMEM;
cd = kzalloc_obj(*cd);
@ -563,7 +563,7 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *sscud_area;
sscud_area = (void *)get_zeroed_page(GFP_KERNEL);
sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sscud_area)
return -ENOMEM;
cud = kzalloc_obj(*cud);
@ -625,7 +625,7 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *sci_area;
sci_area = (void *)get_zeroed_page(GFP_KERNEL);
sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sci_area)
return -ENOMEM;
ci = kzalloc_obj(*ci);
@ -696,7 +696,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
u32 res;
} __attribute__ ((packed)) *cssids_parm;
sccl_area = (void *)get_zeroed_page(GFP_KERNEL);
sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sccl_area)
return -ENOMEM;
ccl = kzalloc_obj(*ccl);
@ -756,7 +756,7 @@ static int chsc_ioctl_chpd(void __user *user_chpd)
int ret;
chpd = kzalloc_obj(*chpd);
scpd_area = (void *)get_zeroed_page(GFP_KERNEL);
scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scpd_area || !chpd) {
ret = -ENOMEM;
goto out_free;
@ -796,7 +796,7 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
u8 data[PAGE_SIZE - 36];
} __attribute__ ((packed)) *sdcal_area;
sdcal_area = (void *)get_zeroed_page(GFP_KERNEL);
sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sdcal_area)
return -ENOMEM;
dcal = kzalloc_obj(*dcal);

View File

@ -229,7 +229,7 @@ int scm_update_information(void)
size_t num;
int ret;
scm_info = (void *)__get_free_page(GFP_KERNEL);
scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
if (!scm_info)
return -ENOMEM;