mirror of https://github.com/torvalds/linux.git
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:
parent
6917f434fd
commit
d38a87d7c0
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue