s390/ap: Support driver_override for AP queue devices

Add a new sysfs attribute driver_override the AP queue's
directory. Writing in a string overrides the default driver
determination and the drivers are matched against this string
instead. This overrules the driver binding determined by the
apmask/aqmask bitmask fields.

According to the common understanding of how the driver_override
behavior shall work, there is no further checking done. Neither about
the string which is given as override driver nor if this device is
currently in use by an mdev device. Another patch may limit this
behavior to refuse a mixed usage of the driver_override and
apmask/aqmask feature.

As there exists some tooling for this kind of driver_override
(see package driverctl) the AP bus behavior for re-binding
should be compatible to this. The steps for a driver_override are:
 1) unbind the current driver from the device. For example
    echo "17.0005" > /sys/devices/ap/card17/17.0005/driver/unbind
 2) set the new driver for this device in the sysfs
    driver_override attribute. For example
    echo "vfio_ap" > /sys//devices/ap/card17/17.0005/driver_override
 3) trigger a bus reprobe of this device. For example
    echo "17.0005" > /sys/bus/ap/drivers_probe
With the driverctl package this is more comfortable and
the settings get persisted:
  driverctl -b ap set-override 17.0005 vfio_ap
and unset with
  driverctl -b ap unset-override 17.0005

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
This commit is contained in:
Harald Freudenberger 2025-11-19 16:27:36 +01:00 committed by Heiko Carstens
parent 6917f434fd
commit d38a87d7c0
3 changed files with 110 additions and 27 deletions

View File

@ -853,20 +853,38 @@ static int __ap_revise_reserved(struct device *dev, void *dummy)
int rc, card, queue, devres, drvres; int rc, card, queue, devres, drvres;
if (is_queue_dev(dev)) { if (is_queue_dev(dev)) {
card = AP_QID_CARD(to_ap_queue(dev)->qid); struct ap_driver *ap_drv = to_ap_drv(dev->driver);
queue = AP_QID_QUEUE(to_ap_queue(dev)->qid); struct ap_queue *aq = to_ap_queue(dev);
mutex_lock(&ap_perms_mutex); struct ap_device *ap_dev = &aq->ap_dev;
devres = test_bit_inv(card, ap_perms.apm) &&
test_bit_inv(queue, ap_perms.aqm); card = AP_QID_CARD(aq->qid);
mutex_unlock(&ap_perms_mutex); queue = AP_QID_QUEUE(aq->qid);
drvres = to_ap_drv(dev->driver)->flags
& AP_DRIVER_FLAG_DEFAULT; if (ap_dev->driver_override) {
if (!!devres != !!drvres) { if (strcmp(ap_dev->driver_override,
pr_debug("reprobing queue=%02x.%04x\n", card, queue); ap_drv->driver.name)) {
rc = device_reprobe(dev); pr_debug("reprobing queue=%02x.%04x\n", card, queue);
if (rc) rc = device_reprobe(dev);
AP_DBF_WARN("%s reprobing queue=%02x.%04x failed\n", if (rc) {
__func__, card, queue); AP_DBF_WARN("%s reprobing queue=%02x.%04x failed\n",
__func__, card, queue);
}
}
} else {
mutex_lock(&ap_perms_mutex);
devres = test_bit_inv(card, ap_perms.apm) &&
test_bit_inv(queue, ap_perms.aqm);
mutex_unlock(&ap_perms_mutex);
drvres = to_ap_drv(dev->driver)->flags
& AP_DRIVER_FLAG_DEFAULT;
if (!!devres != !!drvres) {
pr_debug("reprobing queue=%02x.%04x\n", card, queue);
rc = device_reprobe(dev);
if (rc) {
AP_DBF_WARN("%s reprobing queue=%02x.%04x failed\n",
__func__, card, queue);
}
}
} }
} }
@ -891,15 +909,30 @@ static void ap_bus_revise_bindings(void)
*/ */
int ap_owned_by_def_drv(int card, int queue) int ap_owned_by_def_drv(int card, int queue)
{ {
struct ap_queue *aq;
int rc = 0; int rc = 0;
if (card < 0 || card >= AP_DEVICES || queue < 0 || queue >= AP_DOMAINS) if (card < 0 || card >= AP_DEVICES || queue < 0 || queue >= AP_DOMAINS)
return -EINVAL; return -EINVAL;
aq = ap_get_qdev(AP_MKQID(card, queue));
if (aq) {
const struct device_driver *drv = aq->ap_dev.device.driver;
const struct ap_driver *ap_drv = to_ap_drv(drv);
bool override = !!aq->ap_dev.driver_override;
if (override && drv && ap_drv->flags & AP_DRIVER_FLAG_DEFAULT)
rc = 1;
put_device(&aq->ap_dev.device);
if (override)
goto out;
}
if (test_bit_inv(card, ap_perms.apm) && if (test_bit_inv(card, ap_perms.apm) &&
test_bit_inv(queue, ap_perms.aqm)) test_bit_inv(queue, ap_perms.aqm))
rc = 1; rc = 1;
out:
return rc; return rc;
} }
EXPORT_SYMBOL(ap_owned_by_def_drv); EXPORT_SYMBOL(ap_owned_by_def_drv);
@ -922,12 +955,10 @@ int ap_apqn_in_matrix_owned_by_def_drv(unsigned long *apm,
int card, queue, rc = 0; int card, queue, rc = 0;
for (card = 0; !rc && card < AP_DEVICES; card++) for (card = 0; !rc && card < AP_DEVICES; card++)
if (test_bit_inv(card, apm) && if (test_bit_inv(card, apm))
test_bit_inv(card, ap_perms.apm))
for (queue = 0; !rc && queue < AP_DOMAINS; queue++) for (queue = 0; !rc && queue < AP_DOMAINS; queue++)
if (test_bit_inv(queue, aqm) && if (test_bit_inv(queue, aqm))
test_bit_inv(queue, ap_perms.aqm)) rc = ap_owned_by_def_drv(card, queue);
rc = 1;
return rc; return rc;
} }
@ -951,13 +982,19 @@ static int ap_device_probe(struct device *dev)
*/ */
card = AP_QID_CARD(to_ap_queue(dev)->qid); card = AP_QID_CARD(to_ap_queue(dev)->qid);
queue = AP_QID_QUEUE(to_ap_queue(dev)->qid); queue = AP_QID_QUEUE(to_ap_queue(dev)->qid);
mutex_lock(&ap_perms_mutex); if (ap_dev->driver_override) {
devres = test_bit_inv(card, ap_perms.apm) && if (strcmp(ap_dev->driver_override,
test_bit_inv(queue, ap_perms.aqm); ap_drv->driver.name))
mutex_unlock(&ap_perms_mutex); goto out;
drvres = ap_drv->flags & AP_DRIVER_FLAG_DEFAULT; } else {
if (!!devres != !!drvres) mutex_lock(&ap_perms_mutex);
goto out; devres = test_bit_inv(card, ap_perms.apm) &&
test_bit_inv(queue, ap_perms.aqm);
mutex_unlock(&ap_perms_mutex);
drvres = ap_drv->flags & AP_DRIVER_FLAG_DEFAULT;
if (!!devres != !!drvres)
goto out;
}
} }
/* /*
@ -983,8 +1020,17 @@ static int ap_device_probe(struct device *dev)
} }
out: out:
if (rc) if (rc) {
put_device(dev); put_device(dev);
} else {
if (is_queue_dev(dev)) {
pr_debug("queue=%02x.%04x new driver=%s\n",
card, queue, ap_drv->driver.name);
} else {
pr_debug("card=%02x new driver=%s\n",
to_ap_card(dev)->id, ap_drv->driver.name);
}
}
return rc; return rc;
} }

View File

@ -166,6 +166,7 @@ void ap_driver_unregister(struct ap_driver *);
struct ap_device { struct ap_device {
struct device device; struct device device;
int device_type; /* AP device type. */ int device_type; /* AP device type. */
const char *driver_override;
}; };
#define to_ap_dev(x) container_of((x), struct ap_device, device) #define to_ap_dev(x) container_of((x), struct ap_device, device)

View File

@ -731,6 +731,41 @@ static ssize_t ap_functions_show(struct device *dev,
static DEVICE_ATTR_RO(ap_functions); static DEVICE_ATTR_RO(ap_functions);
static ssize_t driver_override_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ap_queue *aq = to_ap_queue(dev);
struct ap_device *ap_dev = &aq->ap_dev;
int rc;
device_lock(dev);
if (ap_dev->driver_override)
rc = sysfs_emit(buf, "%s\n", ap_dev->driver_override);
else
rc = sysfs_emit(buf, "\n");
device_unlock(dev);
return rc;
}
static ssize_t driver_override_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ap_queue *aq = to_ap_queue(dev);
struct ap_device *ap_dev = &aq->ap_dev;
int rc;
rc = driver_set_override(dev, &ap_dev->driver_override, buf, count);
if (rc)
return rc;
return count;
}
static DEVICE_ATTR_RW(driver_override);
#ifdef CONFIG_AP_DEBUG #ifdef CONFIG_AP_DEBUG
static ssize_t states_show(struct device *dev, static ssize_t states_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
@ -843,6 +878,7 @@ static struct attribute *ap_queue_dev_attrs[] = {
&dev_attr_config.attr, &dev_attr_config.attr,
&dev_attr_chkstop.attr, &dev_attr_chkstop.attr,
&dev_attr_ap_functions.attr, &dev_attr_ap_functions.attr,
&dev_attr_driver_override.attr,
#ifdef CONFIG_AP_DEBUG #ifdef CONFIG_AP_DEBUG
&dev_attr_states.attr, &dev_attr_states.attr,
&dev_attr_last_err_rc.attr, &dev_attr_last_err_rc.attr,