Skip to content

Commit

Permalink
s390/cio: introduce driver_override on the css bus
Browse files Browse the repository at this point in the history
Sometimes, we want to control which of the matching drivers
binds to a subchannel device (e.g. for subchannels we want to
handle via vfio-ccw).

For pci devices, a mechanism to do so has been introduced in
782a985 ("PCI: Introduce new device binding path using
pci_dev.driver_override"). It makes sense to introduce the
driver_override attribute for subchannel devices as well, so
that we can easily extend the 'driverctl' tool (which makes
use of the driver_override attribute for pci).

Note that unlike pci we still require a driver override to
match the subchannel type; matching more than one subchannel
type is probably not useful anyway.

Signed-off-by: Cornelia Huck <cohuck@redhat.com>
Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
Reviewed-by: Sebastian Ott <sebott@linux.ibm.com>
Signed-off-by: Sebastian Ott <sebott@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
  • Loading branch information
Cornelia Huck authored and Vasily Gorbik committed Jul 2, 2019
1 parent dbd6655 commit ebc3d17
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 0 deletions.
23 changes: 23 additions & 0 deletions Documentation/ABI/testing/sysfs-bus-css
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,26 @@ Description: Contains the PIM/PAM/POM values, as reported by the
in sync with the values current in the channel subsystem).
Note: This is an I/O-subchannel specific attribute.
Users: s390-tools, HAL

What: /sys/bus/css/devices/.../driver_override
Date: June 2019
Contact: Cornelia Huck <cohuck@redhat.com>
linux-s390@vger.kernel.org
Description: This file allows the driver for a device to be specified. When
specified, only a driver with a name matching the value written
to driver_override will have an opportunity to bind to the
device. The override is specified by writing a string to the
driver_override file (echo vfio-ccw > driver_override) and
may be cleared with an empty string (echo > driver_override).
This returns the device to standard matching rules binding.
Writing to driver_override does not automatically unbind the
device from its current driver or make any attempt to
automatically load the specified driver. If no driver with a
matching name is currently loaded in the kernel, the device
will not bind to any driver. This also allows devices to
opt-out of driver binding using a driver_override name such as
"none". Only a single driver may be specified in the override,
there is no support for parsing delimiters.
Note that unlike the mechanism of the same name for pci, this
file does not allow to override basic matching rules. I.e.,
the driver must still match the subchannel type of the device.
1 change: 1 addition & 0 deletions drivers/s390/cio/cio.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ struct subchannel {
enum sch_todo todo;
struct work_struct todo_work;
struct schib_config config;
char *driver_override; /* Driver name to force a match */
} __attribute__ ((aligned(8)));

DECLARE_PER_CPU_ALIGNED(struct irb, cio_irb);
Expand Down
53 changes: 53 additions & 0 deletions drivers/s390/cio/css.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ static void css_subchannel_release(struct device *dev)

sch->config.intparm = 0;
cio_commit_config(sch);
kfree(sch->driver_override);
kfree(sch->lock);
kfree(sch);
}
Expand Down Expand Up @@ -323,9 +324,57 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,

static DEVICE_ATTR_RO(modalias);

static ssize_t driver_override_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct subchannel *sch = to_subchannel(dev);
char *driver_override, *old, *cp;

/* We need to keep extra room for a newline */
if (count >= (PAGE_SIZE - 1))
return -EINVAL;

driver_override = kstrndup(buf, count, GFP_KERNEL);
if (!driver_override)
return -ENOMEM;

cp = strchr(driver_override, '\n');
if (cp)
*cp = '\0';

device_lock(dev);
old = sch->driver_override;
if (strlen(driver_override)) {
sch->driver_override = driver_override;
} else {
kfree(driver_override);
sch->driver_override = NULL;
}
device_unlock(dev);

kfree(old);

return count;
}

static ssize_t driver_override_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct subchannel *sch = to_subchannel(dev);
ssize_t len;

device_lock(dev);
len = snprintf(buf, PAGE_SIZE, "%s\n", sch->driver_override);
device_unlock(dev);
return len;
}
static DEVICE_ATTR_RW(driver_override);

static struct attribute *subch_attrs[] = {
&dev_attr_type.attr,
&dev_attr_modalias.attr,
&dev_attr_driver_override.attr,
NULL,
};

Expand Down Expand Up @@ -1348,6 +1397,10 @@ static int css_bus_match(struct device *dev, struct device_driver *drv)
struct css_driver *driver = to_cssdriver(drv);
struct css_device_id *id;

/* When driver_override is set, only bind to the matching driver */
if (sch->driver_override && strcmp(sch->driver_override, drv->name))
return 0;

for (id = driver->subchannel_type; id->match_flags; id++) {
if (sch->st == id->type)
return 1;
Expand Down

0 comments on commit ebc3d17

Please sign in to comment.