sunvdc: prevent sunvdc panic when mpgroup disk added to guest domain

Using mpgroup to define multiple paths for a virtual disk causes multiple
virtual-device-port ports to be created for that virtual device.
Each virtual-device-port port then gets a vdisk created for it by the Linux
sunvdc driver. As mpgroup is not supported by the Linux sunvdc driver it
cannot handle multiple ports for a single vdisk, leading to a kernel panic
at startup.

This fix prevents more than one vdisk per virtual-device-port being created
until full virtual disk multipathing (mpgroup) support is implemented.

Signed-off-by: Jim Quigley <Jim.Quigley@oracle.com>
Reviewed-by: Liam Merwick <liam.merwick@oracle.com>
Reviewed-by: Shannon Nelson <shannon.nelson@oracle.com>
Reviewed-by: Alexandre Chartre <alexandre.chartre@oracle.com>
Reviewed-by: Aaron Young <aaron.young@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Jim Quigley 2017-07-21 09:20:15 -04:00 committed by David S. Miller
parent fdaccf74fe
commit 3ee70591d6

View File

@ -875,6 +875,56 @@ static void print_version(void)
printk(KERN_INFO "%s", version);
}
struct vdc_check_port_data {
int dev_no;
char *type;
};
static int vdc_device_probed(struct device *dev, void *arg)
{
struct vio_dev *vdev = to_vio_dev(dev);
struct vdc_check_port_data *port_data;
port_data = (struct vdc_check_port_data *)arg;
if ((vdev->dev_no == port_data->dev_no) &&
(!(strcmp((char *)&vdev->type, port_data->type))) &&
dev_get_drvdata(dev)) {
/* This device has already been configured
* by vdc_port_probe()
*/
return 1;
} else {
return 0;
}
}
/* Determine whether the VIO device is part of an mpgroup
* by locating all the virtual-device-port nodes associated
* with the parent virtual-device node for the VIO device
* and checking whether any of these nodes are vdc-ports
* which have already been configured.
*
* Returns true if this device is part of an mpgroup and has
* already been probed.
*/
static bool vdc_port_mpgroup_check(struct vio_dev *vdev)
{
struct vdc_check_port_data port_data;
struct device *dev;
port_data.dev_no = vdev->dev_no;
port_data.type = (char *)&vdev->type;
dev = device_find_child(vdev->dev.parent, &port_data,
vdc_device_probed);
if (dev)
return true;
return false;
}
static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
{
struct mdesc_handle *hp;
@ -893,6 +943,14 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
goto err_out_release_mdesc;
}
/* Check if this device is part of an mpgroup */
if (vdc_port_mpgroup_check(vdev)) {
printk(KERN_WARNING
"VIO: Ignoring extra vdisk port %s",
dev_name(&vdev->dev));
goto err_out_release_mdesc;
}
port = kzalloc(sizeof(*port), GFP_KERNEL);
err = -ENOMEM;
if (!port) {
@ -943,6 +1001,9 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
if (err)
goto err_out_free_tx_ring;
/* Note that the device driver_data is used to determine
* whether the port has been probed.
*/
dev_set_drvdata(&vdev->dev, port);
mdesc_release(hp);