Commit ebc3d179 authored by Cornelia Huck's avatar Cornelia Huck Committed by Vasily Gorbik
Browse files

s390/cio: introduce driver_override on the css bus



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
782a985d ("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: default avatarCornelia Huck <cohuck@redhat.com>
Reviewed-by: default avatarHalil Pasic <pasic@linux.ibm.com>
Reviewed-by: default avatarSebastian Ott <sebott@linux.ibm.com>
Signed-off-by: default avatarSebastian Ott <sebott@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent dbd66558
Loading
Loading
Loading
Loading
+23 −0
Original line number Original line Diff line number Diff line
@@ -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).
		in sync with the values current in the channel subsystem).
		Note: This is an I/O-subchannel specific attribute.
		Note: This is an I/O-subchannel specific attribute.
Users:		s390-tools, HAL
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 −0
Original line number Original line Diff line number Diff line
@@ -113,6 +113,7 @@ struct subchannel {
	enum sch_todo todo;
	enum sch_todo todo;
	struct work_struct todo_work;
	struct work_struct todo_work;
	struct schib_config config;
	struct schib_config config;
	char *driver_override; /* Driver name to force a match */
} __attribute__ ((aligned(8)));
} __attribute__ ((aligned(8)));


DECLARE_PER_CPU_ALIGNED(struct irb, cio_irb);
DECLARE_PER_CPU_ALIGNED(struct irb, cio_irb);
+53 −0
Original line number Original line Diff line number Diff line
@@ -167,6 +167,7 @@ static void css_subchannel_release(struct device *dev)


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


static DEVICE_ATTR_RO(modalias);
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[] = {
static struct attribute *subch_attrs[] = {
	&dev_attr_type.attr,
	&dev_attr_type.attr,
	&dev_attr_modalias.attr,
	&dev_attr_modalias.attr,
	&dev_attr_driver_override.attr,
	NULL,
	NULL,
};
};


@@ -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_driver *driver = to_cssdriver(drv);
	struct css_device_id *id;
	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++) {
	for (id = driver->subchannel_type; id->match_flags; id++) {
		if (sch->st == id->type)
		if (sch->st == id->type)
			return 1;
			return 1;