s390 fixes for 7.3-rc5

- Fix several bugs in PCI error recovery SCLP reporting: don't report
   success on skipped recovery, report errors when no pdev is associated,
   add missing device lock, and fix struct pci_dev reference leak in
   zpci_report_status()
 
 - Fix several bugs in CIO code: fix use of invalid SCHIB data, guard PMCW
   field accesses, check device number valid bit in PMWC before accessing
   other fields, and fix NULL pointer dereference in
   ccw_device_get_util_str()
 
 - Fix virtual vs physical address confusion in channel measurement
   facility code on kernels with CONFIG_RANDOMIZE_IDENTITY_BASE=y
 
 - Fix couple of bugs in s390dbf: fix copy of failed static debug areas,
   skip view registration on failure, and reject NULL pointer in
   debug_dump()
 
 - Fix sriov_numvfs attribute name in zPCI documentation
 
 - Fix typos in comments
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEECMNfWEw3SLnmiLkZIg7DeRspbsIFAmq2V1AACgkQIg7DeRsp
 bsJ7zg/7BrnPs0WyKv9NZvVUudYbz3nUIAleJo6HcDK2SUDr+EX3W4U7OjsXL6Fv
 H76hx5AqF6ddtUOJ2uevCAb3PKm0eA7vmK9YiERAclrBA+tWGW0xBwCqqyc84k38
 CPxOBr87Ae1jIIcfDxfBWujtKTFi/4BpSvKvKgg3mnd2egny4UnHnABx5ZcClEee
 p6aVjCKycQFHwNLMoJA1BxvGSpcXdiKXbm6VRJ4gCaBnVuzkLUv1ZMp/Pi1fWVbV
 E1AEztZ/03Tfry51q7wQLv6wD5lR3Z/Py2vZ4YIbelQwVMAMI+rFtgd5oFfo9qkF
 gcFJ+faE9m0E/nsoaaVCaa2Ja4+hV/vZ5ik0bq1ljEqEvInaa2XTuxNAljId351Y
 JrLvCdaip0FkMNMqEym0iydMAlpc491GAhzNrT3yvq6t03VVpxprPPA6VIwHg+zk
 UBMP+QdxtnaV8AAUzDD9E255d88N4DECBICUgf1XjFm/I7xLJFWyUvbzcqNn9VnY
 EGVBOeQmoByhCL9lN0+nzEM1sgT+hQB/C3WmLqiuLxrxjnc8dVr8rVHcPXFgARPw
 Ht3W9jxdTdl4VBwckOWmbvG4L6spXFLdEzI51cP46L1RKWhFjLl/6RoY8fUHf4Vz
 YnHutQevSoPI2D6G4v8K1FBz9wclxCojBPbXCmevfbL9B4ChPoQ=
 =r3uP
 -----END PGP SIGNATURE-----

Merge tag 's390-7.3-4' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 fixes from Heiko Carstens:

 - Fix several bugs in PCI error recovery SCLP reporting: don't report
   success on skipped recovery, report errors when no pdev is
   associated, add missing device lock, and fix struct pci_dev reference
   leak in zpci_report_status()

 - Fix several bugs in CIO code: fix use of invalid SCHIB data, guard
   PMCW field accesses, check device number valid bit in PMWC before
   accessing other fields, and fix NULL pointer dereference in
   ccw_device_get_util_str()

 - Fix virtual vs physical address confusion in channel measurement
   facility code on kernels with CONFIG_RANDOMIZE_IDENTITY_BASE=y

 - Fix couple of bugs in s390dbf: fix copy of failed static debug areas,
   skip view registration on failure, and reject NULL pointer in
   debug_dump()

 - Fix sriov_numvfs attribute name in zPCI documentation

 - Fix typos in comments

* tag 's390-7.3-4' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
  s390/cio: Fix NULL pointer dereference in ccw_device_get_util_str()
  s390/debug: Fix NULL pointer dereference in debug_info_copy()
  s390/debug: Do not register views for failed static debug areas
  s390/debug: Reject NULL debug info in debug_dump()
  s390/cmf: Fix virtual vs physical address confusion
  s390/pci: Don't report recovery success on skipped recovery
  s390/pci: Report SCLP status on error events when no pdev is associated
  s390/pci: Fix missing device lock in zpci_report_status()
  s390/pci: Fix leak of struct pci_dev reference in zpci_report_status()
  s390/cio: Guard PMCW field accesses with dnv check
  s390/cio: Check pmcw.dnv before pmcw.ena in I/O entry points
  s390/cio: Fix cio_update_schib() to not cache invalid schib
  s390/pci/docs: Fix sriov_numvfs attribute name
  s390: Fix typos in comments
This commit is contained in:
Linus Torvalds 2026-09-25 09:39:33 -07:00
commit 547463efb9
18 changed files with 102 additions and 41 deletions

View File

@ -67,7 +67,7 @@ Entries specific to zPCI functions and entries that hold zPCI information.
A physical function that currently supports a virtual function cannot be
powered off until all virtual functions are removed with:
echo 0 > /sys/bus/pci/devices/DDDD:BB:dd.f/sriov_numvf
echo 0 > /sys/bus/pci/devices/DDDD:BB:dd.f/sriov_numvfs
* /sys/bus/pci/devices/DDDD:BB:dd.f/:

View File

@ -460,7 +460,11 @@ static int VNAME(var, active_entries)[EARLY_AREAS] __initdata
#define __REGISTER_STATIC_DEBUG_INFO(var, name, pages, areas, view) \
static int __init VNAME(var, reg)(void) \
{ \
debug_register_static(&var, (pages), (areas)); \
int rc; \
\
rc = debug_register_static(&var, (pages), (areas)); \
if (rc) \
return rc; \
debug_register_view(&var, (view)); \
return 0; \
} \
@ -493,7 +497,7 @@ static debug_info_t __refdata var = \
static debug_info_t __used __section(".s390dbf_info") *VNAME(var, info) = &var; \
__REGISTER_STATIC_DEBUG_INFO(var, name, pages, nr_areas, view)
void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas);
int debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas);
#endif /* MODULE */

View File

@ -434,7 +434,8 @@ static debug_info_t *debug_info_copy(debug_info_t *in, int mode)
debug_info_free(rc);
} while (1);
if (mode == NO_AREAS)
/* debug_register_static() failure leaves areas NULL, bounds intact */
if (mode == NO_AREAS || !in->areas)
goto out;
for (i = 0; i < in->nr_areas; i++) {
@ -823,6 +824,9 @@ ssize_t debug_dump(debug_info_t *id, struct debug_view *view,
file_private_info_t *p_info;
size_t size, offset = 0;
if (!id)
return -EINVAL;
/* Need space for '\0' byte */
if (buf_size < 1)
return 0;
@ -950,8 +954,12 @@ EXPORT_SYMBOL(debug_register);
*
* Note: This function is called automatically via an initcall generated by
* DEFINE_STATIC_DEBUG_INFO.
*
* Return:
* - 0 on success
* - negative error code on failure
*/
void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
int debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
{
unsigned long flags;
debug_info_t *copy;
@ -959,7 +967,7 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
if (!initialized) {
pr_err("Tried to register debug feature %s too early\n",
id->name);
return;
return -EINVAL;
}
debug_get_param(id->name, &id->level, &pages_per_area, false);
@ -975,7 +983,7 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
id->active_entries = NULL;
raw_spin_unlock_irqrestore(&id->lock, flags);
return;
return -ENOMEM;
}
/* Replace static trace area with dynamic copy. */
@ -993,6 +1001,8 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)
mutex_lock(&debug_mutex);
_debug_register(id);
mutex_unlock(&debug_mutex);
return 0;
}
/* Remove debugfs entries. */

View File

@ -226,6 +226,7 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev,
device_lock(&pdev->dev);
if (pdev->error_state == pci_channel_io_perm_failure) {
ers_res = PCI_ERS_RESULT_DISCONNECT;
status_str = "skipped (permanent failure)";
goto out_unlock;
}
pdev->error_state = pci_channel_io_frozen;
@ -297,8 +298,8 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev,
driver->err_handler->resume(pdev);
pci_uevent_ers(pdev, PCI_ERS_RESULT_RECOVERED);
out_unlock:
zpci_report_status(zdev, pdev, "recovery", status_str);
device_unlock(&pdev->dev);
zpci_report_status(zdev, "recovery", status_str);
return ers_res;
}
@ -361,8 +362,10 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf)
__zpci_event_print_error(pdev, ccdf);
if (!pdev)
if (!pdev) {
zpci_report_status(zdev, NULL, "error event", "no pdev bound");
goto no_pdev;
}
switch (ccdf->pec) {
case 0x002a: /* Error event concerns FMB */

View File

@ -87,9 +87,23 @@ static struct debug_view debug_log_view = {
NULL
};
static ssize_t zpci_report_pdev(struct pci_dev *pdev, char *buf, size_t size)
{
struct pci_driver *driver;
const char *start = buf;
char *end = buf + size;
device_lock_assert(&pdev->dev);
buf += scnprintf(buf, end - buf, "state: %s\n", zpci_state_str(pdev->error_state));
driver = to_pci_driver(pdev->dev.driver);
buf += scnprintf(buf, end - buf, "driver: %s\n", (driver) ? driver->name : "n/a");
return buf - start;
}
/**
* zpci_report_status - Report the status of operations on a PCI device
* @zdev: The PCI device for which to report status
* @zdev: The zPCI device for which to report status
* @pdev: The PCI device associated with the zdev if any, NULL otherwise
* @operation: A string representing the operation reported
* @status: A string representing the status of the operation
*
@ -103,15 +117,14 @@ static struct debug_view debug_log_view = {
*
* Return: 0 on success an error code < 0 otherwise.
*/
int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char *status)
int zpci_report_status(struct zpci_dev *zdev, struct pci_dev *pdev,
const char *operation, const char *status)
{
struct zpci_report_error *report;
struct pci_driver *driver = NULL;
struct pci_dev *pdev = NULL;
char *buf, *end;
int ret;
if (!zdev || !zdev->zbus)
if (!zdev)
return -ENODEV;
/* Protected virtualization hosts get nothing from us */
@ -121,18 +134,13 @@ int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char
report = (void *)get_zeroed_page(GFP_KERNEL);
if (!report)
return -ENOMEM;
if (zdev->zbus->bus)
pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn);
if (pdev)
driver = to_pci_driver(pdev->dev.driver);
buf = report->data.log_data;
end = report->data.log_data + ZPCI_REPORT_DATA_SIZE;
buf += scnprintf(buf, end - buf, "report: %s\n", operation);
buf += scnprintf(buf, end - buf, "status: %s\n", status);
buf += scnprintf(buf, end - buf, "state: %s\n",
(pdev) ? zpci_state_str(pdev->error_state) : "n/a");
buf += scnprintf(buf, end - buf, "driver: %s\n", (driver) ? driver->name : "n/a");
if (pdev)
buf += zpci_report_pdev(pdev, buf, end - buf);
ret = debug_dump(pci_debug_msg_id, &debug_log_view, buf, end - buf, true);
if (ret < 0)
pr_err("Reading PCI debug messages failed with code %d\n", ret);

View File

@ -8,9 +8,11 @@
*/
#ifndef __S390_PCI_REPORT_H
#define __S390_PCI_REPORT_H
#include <linux/pci.h>
struct zpci_dev;
int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char *status);
int zpci_report_status(struct zpci_dev *zdev, struct pci_dev *pdev,
const char *operation, const char *status);
#endif /* __S390_PCI_REPORT_H */

View File

@ -103,7 +103,7 @@ dasd_3990_erp_int_req(struct dasd_ccw_req * erp)
/* first time set initial retry counter and erp_function */
/* and retry once without blocking queue */
/* (this enables easier enqueing of the cqr) */
/* (this enables easier enqueuing of the cqr) */
if (erp->function != dasd_3990_erp_int_req) {
erp->retries = 256;
@ -302,7 +302,7 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
/* first time set initial retry counter and erp_function */
/* and retry once without waiting for state change pending */
/* interrupt (this enables easier enqueing of the cqr) */
/* interrupt (this enables easier enqueuing of the cqr) */
if (erp->function != dasd_3990_erp_action_4) {
DBF_DEV_EVENT(DBF_INFO, device, "%s",
@ -1078,7 +1078,7 @@ dasd_3990_erp_bus_out(struct dasd_ccw_req * erp)
/* first time set initial retry counter and erp_function */
/* and retry once without blocking queue */
/* (this enables easier enqueing of the cqr) */
/* (this enables easier enqueuing of the cqr) */
if (erp->function != dasd_3990_erp_bus_out) {
erp->retries = 256;
erp->function = dasd_3990_erp_bus_out;

View File

@ -2060,7 +2060,7 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav,
}
/*
* Valide storage server of current device.
* Valid storage server of current device.
*/
static int dasd_eckd_validate_server(struct dasd_device *device,
unsigned long flags)
@ -5672,7 +5672,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_raw(struct dasd_device *startdev,
char *dst;
/*
* raw track access needs to be mutiple of 64k and on 64k boundary
* raw track access needs to be multiple of 64k and on 64k boundary
* For read requests we can fix an incorrect alignment by padding
* the request with dummy pages.
*/

View File

@ -420,7 +420,7 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */
char flags0;
char flags1;
short w; /* Width of usable area */
short h; /* Heigth of usavle area */
short h; /* Height of usavle area */
char units; /* 0x00:in; 0x01:mm */
int xr;
int yr;

View File

@ -444,7 +444,7 @@ static int vmlogrdr_receive_data(struct vmlogrdr_priv_t *priv)
spin_unlock_bh(&priv->priv_lock);
/* An rc of 5 indicates that the record was bigger than
* the buffer, which is OK for us. A 9 indicates that the
* record was purged befor we could receive it.
* record was purged before we could receive it.
*/
if (rc == 5)
rc = 0;

View File

@ -78,6 +78,9 @@ u8 chp_get_sch_opm(struct subchannel *sch)
int opm;
int i;
if (!sch->schib.pmcw.dnv)
return 0;
opm = 0;
chp_id_init(&chpid);
for (i = 0; i < 8; i++) {

View File

@ -453,7 +453,8 @@ EXPORT_SYMBOL_GPL(cio_commit_config);
/**
* cio_update_schib - Perform stsch and update schib if subchannel is valid.
* @sch: subchannel on which to perform stsch
* Return zero on success, -ENODEV otherwise.
* Return zero on success, -ENODEV if the subchannel is not operational,
* -EACCES if the subchannel has no valid device.
*/
int cio_update_schib(struct subchannel *sch)
{
@ -462,10 +463,12 @@ int cio_update_schib(struct subchannel *sch)
if (stsch(sch->schid, &schib))
return -ENODEV;
memcpy(&sch->schib, &schib, sizeof(schib));
if (!css_sch_is_valid(&schib))
if (!css_sch_is_valid(&schib)) {
memset(&sch->schib, 0, sizeof(sch->schib));
return -EACCES;
}
memcpy(&sch->schib, &schib, sizeof(schib));
return 0;
}

View File

@ -7,6 +7,7 @@
#include <linux/mod_devicetable.h>
#include <asm/chpid.h>
#include <asm/cio.h>
#include <asm/dma-types.h>
#include <asm/fcx.h>
#include <asm/schid.h>
#include <asm/tpi.h>
@ -49,7 +50,7 @@ struct pmcw {
/* Target SCHIB configuration. */
struct schib_config {
u64 mba;
dma64_t mba;
u32 intparm;
u16 mbi;
u32 isc:3;
@ -66,7 +67,7 @@ struct schib_config {
struct schib {
struct pmcw pmcw; /* path management control word */
union scsw scsw; /* subchannel status word */
__u64 mba; /* measurement block address */
dma64_t mba; /* measurement block address */
__u8 mda[4]; /* model dependent area */
} __attribute__ ((packed,aligned(4)));

View File

@ -183,7 +183,7 @@ static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
sch->config.mbfc = mbfc;
/* address can be either a block address or a block index */
if (mbfc)
sch->config.mba = address;
sch->config.mba = address ? virt_to_dma64((void *)address) : 0;
else
sch->config.mbi = address;

View File

@ -922,7 +922,7 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev,
if (!sch_is_pseudo_sch(old_sch)) {
spin_lock_irq(&old_sch->lock);
old_enabled = old_sch->schib.pmcw.ena;
old_enabled = old_sch->schib.pmcw.dnv && old_sch->schib.pmcw.ena;
rc = 0;
if (old_enabled)
rc = cio_disable_subchannel(old_sch);
@ -941,7 +941,7 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev,
CIO_MSG_EVENT(0, "device_move(0.%x.%04x,0.%x.%04x)=%d\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, sch->schid.ssid,
sch->schib.pmcw.dev, rc);
sch->schid.sch_no, rc);
if (old_enabled) {
/* Try to re-enable the old subchannel. */
spin_lock_irq(&old_sch->lock);
@ -1207,7 +1207,7 @@ static void io_subchannel_quiesce(struct subchannel *sch)
cdev = sch_get_cdev(sch);
if (cio_is_console(sch->schid))
goto out_unlock;
if (!sch->schib.pmcw.ena)
if (!sch->schib.pmcw.dnv || !sch->schib.pmcw.ena)
goto out_unlock;
ret = cio_disable_subchannel(sch);
if (ret != -EBUSY)
@ -1254,7 +1254,8 @@ static int recovery_check(struct device *dev, void *data)
switch (cdev->private->state) {
case DEV_STATE_ONLINE:
sch = to_subchannel(cdev->dev.parent);
if ((sch->schib.pmcw.pam & sch->opm) == sch->vpm)
if (sch->schib.pmcw.dnv &&
(sch->schib.pmcw.pam & sch->opm) == sch->vpm)
break;
fallthrough;
case DEV_STATE_DISCONNECTED:

View File

@ -170,6 +170,9 @@ __recover_lost_chpids(struct subchannel *sch, int old_lpm)
int mask, i;
struct chp_id chpid;
if (!sch->schib.pmcw.dnv)
return;
chp_id_init(&chpid);
for (i = 0; i<8; i++) {
mask = 0x80 >> i;

View File

@ -142,6 +142,8 @@ int ccw_device_clear(struct ccw_device *cdev, unsigned long intparm)
if (!cdev || !cdev->dev.parent)
return -ENODEV;
sch = to_subchannel(cdev->dev.parent);
if (!sch->schib.pmcw.dnv)
return -ENODEV;
if (!sch->schib.pmcw.ena)
return -EINVAL;
if (cdev->private->state == DEV_STATE_NOT_OPER)
@ -198,6 +200,8 @@ int ccw_device_start_timeout_key(struct ccw_device *cdev, struct ccw1 *cpa,
if (!cdev || !cdev->dev.parent)
return -ENODEV;
sch = to_subchannel(cdev->dev.parent);
if (!sch->schib.pmcw.dnv)
return -ENODEV;
if (!sch->schib.pmcw.ena)
return -EINVAL;
if (cdev->private->state == DEV_STATE_NOT_OPER)
@ -379,6 +383,8 @@ int ccw_device_halt(struct ccw_device *cdev, unsigned long intparm)
if (!cdev || !cdev->dev.parent)
return -ENODEV;
sch = to_subchannel(cdev->dev.parent);
if (!sch->schib.pmcw.dnv)
return -ENODEV;
if (!sch->schib.pmcw.ena)
return -EINVAL;
if (cdev->private->state == DEV_STATE_NOT_OPER)
@ -413,6 +419,8 @@ int ccw_device_resume(struct ccw_device *cdev)
if (!cdev || !cdev->dev.parent)
return -ENODEV;
sch = to_subchannel(cdev->dev.parent);
if (!sch->schib.pmcw.dnv)
return -ENODEV;
if (!sch->schib.pmcw.ena)
return -EINVAL;
if (cdev->private->state == DEV_STATE_NOT_OPER)
@ -482,6 +490,8 @@ struct channel_path_desc_fmt0 *ccw_device_get_chp_desc(struct ccw_device *cdev,
struct chp_id chpid;
sch = to_subchannel(cdev->dev.parent);
if (!sch->schib.pmcw.dnv)
return NULL;
chp_id_init(&chpid);
chpid.id = sch->schib.pmcw.chpid[chp_idx];
return chp_get_chp_desc(chpid);
@ -502,9 +512,13 @@ u8 *ccw_device_get_util_str(struct ccw_device *cdev, int chp_idx)
struct chp_id chpid;
u8 *util_str;
if (!sch->schib.pmcw.dnv)
return NULL;
chp_id_init(&chpid);
chpid.id = sch->schib.pmcw.chpid[chp_idx];
chp = chpid_to_chp(chpid);
if (!chp)
return NULL;
util_str = kmalloc(sizeof(chp->desc_fmt3.util_str), GFP_KERNEL);
if (!util_str)
@ -548,6 +562,8 @@ int ccw_device_tm_start_timeout_key(struct ccw_device *cdev, struct tcw *tcw,
int rc;
sch = to_subchannel(cdev->dev.parent);
if (!sch->schib.pmcw.dnv)
return -ENODEV;
if (!sch->schib.pmcw.ena)
return -EINVAL;
if (cdev->private->state == DEV_STATE_VERIFY) {
@ -652,6 +668,9 @@ int ccw_device_get_mdc(struct ccw_device *cdev, u8 mask)
struct chp_id chpid;
int mdc = 0, i;
if (!sch->schib.pmcw.dnv)
return 0;
/* Adjust requested path mask to excluded varied off paths. */
if (mask)
mask &= sch->lpm;
@ -694,6 +713,8 @@ int ccw_device_tm_intrg(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
if (!sch->schib.pmcw.dnv)
return -ENODEV;
if (!sch->schib.pmcw.ena)
return -EINVAL;
if (cdev->private->state != DEV_STATE_ONLINE)
@ -786,6 +807,8 @@ int ccw_device_get_chpid(struct ccw_device *cdev, int chp_idx, u8 *chpid)
if ((chp_idx < 0) || (chp_idx > 7))
return -EINVAL;
if (!sch->schib.pmcw.dnv)
return -ENODEV;
mask = 0x80 >> chp_idx;
if (!(sch->schib.pmcw.pim & mask))
return -ENODEV;

View File

@ -399,7 +399,7 @@ static void fsm_close(struct vfio_ccw_private *private,
spin_lock_irq(&sch->lock);
if (!sch->schib.pmcw.ena)
if (!sch->schib.pmcw.dnv || !sch->schib.pmcw.ena)
goto err_unlock;
ret = cio_disable_subchannel(sch);