s390 fixes for 6.19-rc2
- clear 'Search boot program' flag when 'bootprog' sysfs file is written to override a value set from Hardware Management Console - fix cyclic dead-lock in zpci_zdev_put() and zpci_scan_devices() functions when triggering PCI device recovery using sysfs - annotate the expected lock context imbalance in zpci_release_device() function to fix a sparse complaint - fix the logic to fallback to the return address register value in the topmost frame when stack tracing uses a back chain -----BEGIN PGP SIGNATURE----- iI0EABYKADUWIQQrtrZiYVkVzKQcYivNdxKlNrRb8AUCaUFNXRccYWdvcmRlZXZA bGludXguaWJtLmNvbQAKCRDNdxKlNrRb8JpYAQC4mM0ZoTUp+c6rVjgPsDLbhFMm HX6PGcTCxwAirSQSqwD+LrC216fNcG5gsfRU4NdMzVxAs11DnRcnbjAx8/3tawg= =Q3D2 -----END PGP SIGNATURE----- Merge tag 's390-6.19-3' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux Pull s390 fixes from Alexander Gordeev: - clear 'Search boot program' flag when 'bootprog' sysfs file is written to override a value set from Hardware Management Console - fix cyclic dead-lock in zpci_zdev_put() and zpci_scan_devices() functions when triggering PCI device recovery using sysfs - annotate the expected lock context imbalance in zpci_release_device() function to fix a sparse complaint - fix the logic to fallback to the return address register value in the topmost frame when stack tracing uses a back chain * tag 's390-6.19-3' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: s390/stacktrace: Do not fallback to RA register s390/pci: Annotate lock context imbalance in zpci_release_device() s390/pci: Fix cyclic dead-lock in zpci_zdev_put() and zpci_scan_devices() s390/ipl: Clear SBP flag when bootprog is setmaster
commit
64e68f8a95
|
|
@ -748,6 +748,7 @@ ForEachMacros:
|
|||
- 'ynl_attr_for_each_nested'
|
||||
- 'ynl_attr_for_each_payload'
|
||||
- 'zorro_for_each_dev'
|
||||
- 'zpci_bus_for_each'
|
||||
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ struct ipl_pl_hdr {
|
|||
#define IPL_PL_FLAG_IPLPS 0x80
|
||||
#define IPL_PL_FLAG_SIPL 0x40
|
||||
#define IPL_PL_FLAG_IPLSR 0x20
|
||||
#define IPL_PL_FLAG_SBP 0x10
|
||||
|
||||
/* IPL Parameter Block header */
|
||||
struct ipl_pb_hdr {
|
||||
|
|
|
|||
|
|
@ -262,6 +262,24 @@ static struct kobj_attribute sys_##_prefix##_##_name##_attr = \
|
|||
sys_##_prefix##_##_name##_show, \
|
||||
sys_##_prefix##_##_name##_store)
|
||||
|
||||
#define DEFINE_IPL_ATTR_BOOTPROG_RW(_prefix, _name, _fmt_out, _fmt_in, _hdr, _value) \
|
||||
IPL_ATTR_SHOW_FN(_prefix, _name, _fmt_out, (unsigned long long) _value) \
|
||||
static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \
|
||||
struct kobj_attribute *attr, \
|
||||
const char *buf, size_t len) \
|
||||
{ \
|
||||
unsigned long long value; \
|
||||
if (sscanf(buf, _fmt_in, &value) != 1) \
|
||||
return -EINVAL; \
|
||||
(_value) = value; \
|
||||
(_hdr).flags &= ~IPL_PL_FLAG_SBP; \
|
||||
return len; \
|
||||
} \
|
||||
static struct kobj_attribute sys_##_prefix##_##_name##_attr = \
|
||||
__ATTR(_name, 0644, \
|
||||
sys_##_prefix##_##_name##_show, \
|
||||
sys_##_prefix##_##_name##_store)
|
||||
|
||||
#define DEFINE_IPL_ATTR_STR_RW(_prefix, _name, _fmt_out, _fmt_in, _value)\
|
||||
IPL_ATTR_SHOW_FN(_prefix, _name, _fmt_out, _value) \
|
||||
static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \
|
||||
|
|
@ -818,12 +836,13 @@ DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%llx\n",
|
|||
reipl_block_fcp->fcp.wwpn);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%llx\n",
|
||||
reipl_block_fcp->fcp.lun);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_fcp->fcp.bootprog);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, br_lba, "%lld\n", "%lld\n",
|
||||
reipl_block_fcp->fcp.br_lba);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
|
||||
reipl_block_fcp->fcp.devno);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(reipl_fcp, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_fcp->hdr,
|
||||
reipl_block_fcp->fcp.bootprog);
|
||||
|
||||
static void reipl_get_ascii_loadparm(char *loadparm,
|
||||
struct ipl_parameter_block *ibp)
|
||||
|
|
@ -942,10 +961,11 @@ DEFINE_IPL_ATTR_RW(reipl_nvme, fid, "0x%08llx\n", "%llx\n",
|
|||
reipl_block_nvme->nvme.fid);
|
||||
DEFINE_IPL_ATTR_RW(reipl_nvme, nsid, "0x%08llx\n", "%llx\n",
|
||||
reipl_block_nvme->nvme.nsid);
|
||||
DEFINE_IPL_ATTR_RW(reipl_nvme, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_nvme->nvme.bootprog);
|
||||
DEFINE_IPL_ATTR_RW(reipl_nvme, br_lba, "%lld\n", "%lld\n",
|
||||
reipl_block_nvme->nvme.br_lba);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(reipl_nvme, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_nvme->hdr,
|
||||
reipl_block_nvme->nvme.bootprog);
|
||||
|
||||
static struct attribute *reipl_nvme_attrs[] = {
|
||||
&sys_reipl_nvme_fid_attr.attr,
|
||||
|
|
@ -1038,8 +1058,9 @@ static const struct bin_attribute *const reipl_eckd_bin_attrs[] = {
|
|||
};
|
||||
|
||||
DEFINE_IPL_CCW_ATTR_RW(reipl_eckd, device, reipl_block_eckd->eckd);
|
||||
DEFINE_IPL_ATTR_RW(reipl_eckd, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_eckd->eckd.bootprog);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(reipl_eckd, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_eckd->hdr,
|
||||
reipl_block_eckd->eckd.bootprog);
|
||||
|
||||
static struct attribute *reipl_eckd_attrs[] = {
|
||||
&sys_reipl_eckd_device_attr.attr,
|
||||
|
|
@ -1567,12 +1588,13 @@ DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%llx\n",
|
|||
dump_block_fcp->fcp.wwpn);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%llx\n",
|
||||
dump_block_fcp->fcp.lun);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
|
||||
dump_block_fcp->fcp.bootprog);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n",
|
||||
dump_block_fcp->fcp.br_lba);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
|
||||
dump_block_fcp->fcp.devno);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
|
||||
dump_block_fcp->hdr,
|
||||
dump_block_fcp->fcp.bootprog);
|
||||
|
||||
DEFINE_IPL_ATTR_SCP_DATA_RW(dump_fcp, dump_block_fcp->hdr,
|
||||
dump_block_fcp->fcp,
|
||||
|
|
@ -1604,10 +1626,11 @@ DEFINE_IPL_ATTR_RW(dump_nvme, fid, "0x%08llx\n", "%llx\n",
|
|||
dump_block_nvme->nvme.fid);
|
||||
DEFINE_IPL_ATTR_RW(dump_nvme, nsid, "0x%08llx\n", "%llx\n",
|
||||
dump_block_nvme->nvme.nsid);
|
||||
DEFINE_IPL_ATTR_RW(dump_nvme, bootprog, "%lld\n", "%llx\n",
|
||||
dump_block_nvme->nvme.bootprog);
|
||||
DEFINE_IPL_ATTR_RW(dump_nvme, br_lba, "%lld\n", "%llx\n",
|
||||
dump_block_nvme->nvme.br_lba);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(dump_nvme, bootprog, "%lld\n", "%llx\n",
|
||||
dump_block_nvme->hdr,
|
||||
dump_block_nvme->nvme.bootprog);
|
||||
|
||||
DEFINE_IPL_ATTR_SCP_DATA_RW(dump_nvme, dump_block_nvme->hdr,
|
||||
dump_block_nvme->nvme,
|
||||
|
|
@ -1635,8 +1658,9 @@ static const struct attribute_group dump_nvme_attr_group = {
|
|||
|
||||
/* ECKD dump device attributes */
|
||||
DEFINE_IPL_CCW_ATTR_RW(dump_eckd, device, dump_block_eckd->eckd);
|
||||
DEFINE_IPL_ATTR_RW(dump_eckd, bootprog, "%lld\n", "%llx\n",
|
||||
dump_block_eckd->eckd.bootprog);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(dump_eckd, bootprog, "%lld\n", "%llx\n",
|
||||
dump_block_eckd->hdr,
|
||||
dump_block_eckd->eckd.bootprog);
|
||||
|
||||
IPL_ATTR_BR_CHR_SHOW_FN(dump, dump_block_eckd->eckd);
|
||||
IPL_ATTR_BR_CHR_STORE_FN(dump, dump_block_eckd->eckd);
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo
|
|||
struct stack_frame_vdso_wrapper __user *sf_vdso;
|
||||
struct stack_frame_user __user *sf;
|
||||
unsigned long ip, sp;
|
||||
bool first = true;
|
||||
|
||||
if (!current->mm)
|
||||
return;
|
||||
|
|
@ -133,24 +132,11 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo
|
|||
if (__get_user(ip, &sf->gprs[8]))
|
||||
break;
|
||||
}
|
||||
/* Sanity check: ABI requires SP to be 8 byte aligned. */
|
||||
if (sp & 0x7)
|
||||
/* Validate SP and RA (ABI requires SP to be 8 byte aligned). */
|
||||
if (sp & 0x7 || ip_invalid(ip))
|
||||
break;
|
||||
if (ip_invalid(ip)) {
|
||||
/*
|
||||
* If the instruction address is invalid, and this
|
||||
* is the first stack frame, assume r14 has not
|
||||
* been written to the stack yet. Otherwise exit.
|
||||
*/
|
||||
if (!first)
|
||||
break;
|
||||
ip = regs->gprs[14];
|
||||
if (ip_invalid(ip))
|
||||
break;
|
||||
}
|
||||
if (!store_ip(consume_entry, cookie, entry, perf, ip))
|
||||
break;
|
||||
first = false;
|
||||
}
|
||||
pagefault_enable();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -961,6 +961,7 @@ void zpci_device_reserved(struct zpci_dev *zdev)
|
|||
}
|
||||
|
||||
void zpci_release_device(struct kref *kref)
|
||||
__releases(&zpci_list_lock)
|
||||
{
|
||||
struct zpci_dev *zdev = container_of(kref, struct zpci_dev, kref);
|
||||
|
||||
|
|
@ -1148,6 +1149,7 @@ static void zpci_add_devices(struct list_head *scan_list)
|
|||
|
||||
int zpci_scan_devices(void)
|
||||
{
|
||||
struct zpci_bus *zbus;
|
||||
LIST_HEAD(scan_list);
|
||||
int rc;
|
||||
|
||||
|
|
@ -1156,7 +1158,10 @@ int zpci_scan_devices(void)
|
|||
return rc;
|
||||
|
||||
zpci_add_devices(&scan_list);
|
||||
zpci_bus_scan_busses();
|
||||
zpci_bus_for_each(zbus) {
|
||||
zpci_bus_scan_bus(zbus);
|
||||
cond_resched();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -153,23 +153,6 @@ int zpci_bus_scan_bus(struct zpci_bus *zbus)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* zpci_bus_scan_busses - Scan all registered busses
|
||||
*
|
||||
* Scan all available zbusses
|
||||
*
|
||||
*/
|
||||
void zpci_bus_scan_busses(void)
|
||||
{
|
||||
struct zpci_bus *zbus = NULL;
|
||||
|
||||
mutex_lock(&zbus_list_lock);
|
||||
list_for_each_entry(zbus, &zbus_list, bus_next) {
|
||||
zpci_bus_scan_bus(zbus);
|
||||
cond_resched();
|
||||
}
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
}
|
||||
|
||||
static bool zpci_bus_is_multifunction_root(struct zpci_dev *zdev)
|
||||
{
|
||||
return !s390_pci_no_rid && zdev->rid_available &&
|
||||
|
|
@ -222,10 +205,29 @@ out_free_domain:
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void zpci_bus_release(struct kref *kref)
|
||||
/**
|
||||
* zpci_bus_release - Un-initialize resources associated with the zbus and
|
||||
* free memory
|
||||
* @kref: refcount * that is part of struct zpci_bus
|
||||
*
|
||||
* MUST be called with `zbus_list_lock` held, but the lock is released during
|
||||
* run of the function.
|
||||
*/
|
||||
static inline void zpci_bus_release(struct kref *kref)
|
||||
__releases(&zbus_list_lock)
|
||||
{
|
||||
struct zpci_bus *zbus = container_of(kref, struct zpci_bus, kref);
|
||||
|
||||
lockdep_assert_held(&zbus_list_lock);
|
||||
|
||||
list_del(&zbus->bus_next);
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
|
||||
/*
|
||||
* At this point no-one should see this object, or be able to get a new
|
||||
* reference to it.
|
||||
*/
|
||||
|
||||
if (zbus->bus) {
|
||||
pci_lock_rescan_remove();
|
||||
pci_stop_root_bus(zbus->bus);
|
||||
|
|
@ -237,16 +239,19 @@ static void zpci_bus_release(struct kref *kref)
|
|||
pci_unlock_rescan_remove();
|
||||
}
|
||||
|
||||
mutex_lock(&zbus_list_lock);
|
||||
list_del(&zbus->bus_next);
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
zpci_remove_parent_msi_domain(zbus);
|
||||
kfree(zbus);
|
||||
}
|
||||
|
||||
static void zpci_bus_put(struct zpci_bus *zbus)
|
||||
static inline void __zpci_bus_get(struct zpci_bus *zbus)
|
||||
{
|
||||
kref_put(&zbus->kref, zpci_bus_release);
|
||||
lockdep_assert_held(&zbus_list_lock);
|
||||
kref_get(&zbus->kref);
|
||||
}
|
||||
|
||||
static inline void zpci_bus_put(struct zpci_bus *zbus)
|
||||
{
|
||||
kref_put_mutex(&zbus->kref, zpci_bus_release, &zbus_list_lock);
|
||||
}
|
||||
|
||||
static struct zpci_bus *zpci_bus_get(int topo, bool topo_is_tid)
|
||||
|
|
@ -258,7 +263,7 @@ static struct zpci_bus *zpci_bus_get(int topo, bool topo_is_tid)
|
|||
if (!zbus->multifunction)
|
||||
continue;
|
||||
if (topo_is_tid == zbus->topo_is_tid && topo == zbus->topo) {
|
||||
kref_get(&zbus->kref);
|
||||
__zpci_bus_get(zbus);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
|
@ -268,6 +273,44 @@ out_unlock:
|
|||
return zbus;
|
||||
}
|
||||
|
||||
/**
|
||||
* zpci_bus_get_next - get the next zbus object from given position in the list
|
||||
* @pos: current position/cursor in the global zbus list
|
||||
*
|
||||
* Acquires and releases references as the cursor iterates (might also free/
|
||||
* release the cursor). Is tolerant of concurrent operations on the list.
|
||||
*
|
||||
* To begin the iteration, set *@pos to %NULL before calling the function.
|
||||
*
|
||||
* *@pos is set to %NULL in cases where either the list is empty, or *@pos is
|
||||
* the last element in the list.
|
||||
*
|
||||
* Context: Process context. May sleep.
|
||||
*/
|
||||
void zpci_bus_get_next(struct zpci_bus **pos)
|
||||
{
|
||||
struct zpci_bus *curp = *pos, *next = NULL;
|
||||
|
||||
mutex_lock(&zbus_list_lock);
|
||||
if (curp)
|
||||
next = list_next_entry(curp, bus_next);
|
||||
else
|
||||
next = list_first_entry(&zbus_list, typeof(*curp), bus_next);
|
||||
|
||||
if (list_entry_is_head(next, &zbus_list, bus_next))
|
||||
next = NULL;
|
||||
|
||||
if (next)
|
||||
__zpci_bus_get(next);
|
||||
|
||||
*pos = next;
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
|
||||
/* zpci_bus_put() might drop refcount to 0 and locks zbus_list_lock */
|
||||
if (curp)
|
||||
zpci_bus_put(curp);
|
||||
}
|
||||
|
||||
static struct zpci_bus *zpci_bus_alloc(int topo, bool topo_is_tid)
|
||||
{
|
||||
struct zpci_bus *zbus;
|
||||
|
|
@ -279,9 +322,6 @@ static struct zpci_bus *zpci_bus_alloc(int topo, bool topo_is_tid)
|
|||
zbus->topo = topo;
|
||||
zbus->topo_is_tid = topo_is_tid;
|
||||
INIT_LIST_HEAD(&zbus->bus_next);
|
||||
mutex_lock(&zbus_list_lock);
|
||||
list_add_tail(&zbus->bus_next, &zbus_list);
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
|
||||
kref_init(&zbus->kref);
|
||||
INIT_LIST_HEAD(&zbus->resources);
|
||||
|
|
@ -291,6 +331,10 @@ static struct zpci_bus *zpci_bus_alloc(int topo, bool topo_is_tid)
|
|||
zbus->bus_resource.flags = IORESOURCE_BUS;
|
||||
pci_add_resource(&zbus->resources, &zbus->bus_resource);
|
||||
|
||||
mutex_lock(&zbus_list_lock);
|
||||
list_add_tail(&zbus->bus_next, &zbus_list);
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
|
||||
return zbus;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,20 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops);
|
|||
void zpci_bus_device_unregister(struct zpci_dev *zdev);
|
||||
|
||||
int zpci_bus_scan_bus(struct zpci_bus *zbus);
|
||||
void zpci_bus_scan_busses(void);
|
||||
void zpci_bus_get_next(struct zpci_bus **pos);
|
||||
|
||||
/**
|
||||
* zpci_bus_for_each - iterate over all the registered zbus objects
|
||||
* @pos: a struct zpci_bus * as cursor
|
||||
*
|
||||
* Acquires and releases references as the cursor iterates over the registered
|
||||
* objects. Is tolerant against concurrent removals of objects.
|
||||
*
|
||||
* Context: Process context. May sleep.
|
||||
*/
|
||||
#define zpci_bus_for_each(pos) \
|
||||
for ((pos) = NULL, zpci_bus_get_next(&(pos)); (pos) != NULL; \
|
||||
zpci_bus_get_next(&(pos)))
|
||||
|
||||
int zpci_bus_scan_device(struct zpci_dev *zdev);
|
||||
void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error);
|
||||
|
|
|
|||
Loading…
Reference in New Issue