Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8251b4c4 authored by Stephen Rothwell's avatar Stephen Rothwell Committed by Paul Mackerras
Browse files

[POWERPC] iSeries: Move viodasd probing



This way we only have entries in the device tree for disks that actually
exist.  A slight complication is that disks may be attached to LPARs
at runtime.

Signed-off-by: default avatarStephen Rothwell <sfr@canb.auug.org.au>
Acked-by: default avatarJens Axboe <jens.axboe@oracle.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 7465ce0d
Loading
Loading
Loading
Loading
+0 −6
Original line number Diff line number Diff line
@@ -72,7 +72,6 @@ static char __initdata device_type_cpu[] = "cpu";
static char __initdata device_type_memory[] = "memory";
static char __initdata device_type_serial[] = "serial";
static char __initdata device_type_network[] = "network";
static char __initdata device_type_block[] = "block";
static char __initdata device_type_pci[] = "pci";
static char __initdata device_type_vdevice[] = "vdevice";
static char __initdata device_type_vscsi[] = "vscsi";
@@ -374,11 +373,6 @@ static void __init dt_vdevices(struct iseries_flat_dt *dt)

		dt_end_node(dt);
	}
	reg += HVMAXARCHITECTEDVIRTUALLANS;

	for (i = 0; i < HVMAXARCHITECTEDVIRTUALDISKS; i++)
		dt_do_vdevice(dt, "viodasd", reg, i, device_type_block,
				"IBM,iSeries-viodasd", 1);

	dt_end_node(dt);
}
+210 −91
Original line number Diff line number Diff line
@@ -25,8 +25,10 @@
#include <linux/gfp.h>
#include <linux/completion.h>
#include <linux/proc_fs.h>
#include <linux/module.h>

#include <asm/firmware.h>
#include <asm/vio.h>
#include <asm/iseries/vio.h>
#include <asm/iseries/iommu.h>
#include <asm/iseries/hv_types.h>
@@ -57,7 +59,7 @@ struct vio_resource {
	char	model[3];
};

static struct property * __init new_property(const char *name, int length,
static struct property *new_property(const char *name, int length,
		const void *value)
{
	struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length,
@@ -78,7 +80,7 @@ static void __init free_property(struct property *np)
	kfree(np);
}

static struct device_node * __init new_node(const char *path,
static struct device_node *new_node(const char *path,
		struct device_node *parent)
{
	struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL);
@@ -97,7 +99,7 @@ static struct device_node * __init new_node(const char *path,
	return np;
}

static void __init free_node(struct device_node *np)
static void free_node(struct device_node *np)
{
	struct property *next;
	struct property *prop;
@@ -113,7 +115,7 @@ static void __init free_node(struct device_node *np)
	kfree(np);
}

static int __init add_string_property(struct device_node *np, const char *name,
static int add_string_property(struct device_node *np, const char *name,
		const char *value)
{
	struct property *nprop = new_property(name, strlen(value) + 1, value);
@@ -124,7 +126,7 @@ static int __init add_string_property(struct device_node *np, const char *name,
	return 1;
}

static int __init add_raw_property(struct device_node *np, const char *name,
static int add_raw_property(struct device_node *np, const char *name,
		int length, const void *value)
{
	struct property *nprop = new_property(name, length, value);
@@ -135,6 +137,201 @@ static int __init add_raw_property(struct device_node *np, const char *name,
	return 1;
}

static struct device_node *do_device_node(struct device_node *parent,
		const char *name, u32 reg, u32 unit, const char *type,
		const char *compat, struct vio_resource *res)
{
	struct device_node *np;
	char path[32];

	snprintf(path, sizeof(path), "/vdevice/%s@%08x", name, reg);
	np = new_node(path, parent);
	if (!np)
		return NULL;
	if (!add_string_property(np, "name", name) ||
		!add_string_property(np, "device_type", type) ||
		!add_string_property(np, "compatible", compat) ||
		!add_raw_property(np, "reg", sizeof(reg), &reg) ||
		!add_raw_property(np, "linux,unit_address",
			sizeof(unit), &unit)) {
		goto node_free;
	}
	if (res) {
		if (!add_raw_property(np, "linux,vio_rsrcname",
				sizeof(res->rsrcname), res->rsrcname) ||
			!add_raw_property(np, "linux,vio_type",
				sizeof(res->type), res->type) ||
			!add_raw_property(np, "linux,vio_model",
				sizeof(res->model), res->model))
			goto node_free;
	}
	np->name = of_get_property(np, "name", NULL);
	np->type = of_get_property(np, "device_type", NULL);
	of_attach_node(np);
#ifdef CONFIG_PROC_DEVICETREE
	if (parent->pde) {
		struct proc_dir_entry *ent;

		ent = proc_mkdir(strrchr(np->full_name, '/') + 1, parent->pde);
		if (ent)
			proc_device_tree_add_node(np, ent);
	}
#endif
	return np;

 node_free:
	free_node(np);
	return NULL;
}

/*
 * This is here so that we can dynamically add viodasd
 * devices without exposing all the above infrastructure.
 */
struct vio_dev *vio_create_viodasd(u32 unit)
{
	struct device_node *vio_root;
	struct device_node *np;
	struct vio_dev *vdev = NULL;

	vio_root = of_find_node_by_path("/vdevice");
	if (!vio_root)
		return NULL;
	np = do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
			"block", "IBM,iSeries-viodasd", NULL);
	of_node_put(vio_root);
	if (np) {
		vdev = vio_register_device_node(np);
		if (!vdev)
			free_node(np);
	}
	return vdev;
}
EXPORT_SYMBOL_GPL(vio_create_viodasd);

static void __init handle_block_event(struct HvLpEvent *event)
{
	struct vioblocklpevent *bevent = (struct vioblocklpevent *)event;
	struct vio_waitevent *pwe;

	if (event == NULL)
		/* Notification that a partition went away! */
		return;
	/* First, we should NEVER get an int here...only acks */
	if (hvlpevent_is_int(event)) {
		printk(KERN_WARNING "handle_viod_request: "
		       "Yikes! got an int in viodasd event handler!\n");
		if (hvlpevent_need_ack(event)) {
			event->xRc = HvLpEvent_Rc_InvalidSubtype;
			HvCallEvent_ackLpEvent(event);
		}
		return;
	}

	switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
	case vioblockopen:
		/*
		 * Handle a response to an open request.  We get all the
		 * disk information in the response, so update it.  The
		 * correlation token contains a pointer to a waitevent
		 * structure that has a completion in it.  update the
		 * return code in the waitevent structure and post the
		 * completion to wake up the guy who sent the request
		 */
		pwe = (struct vio_waitevent *)event->xCorrelationToken;
		pwe->rc = event->xRc;
		pwe->sub_result = bevent->sub_result;
		complete(&pwe->com);
		break;
	case vioblockclose:
		break;
	default:
		printk(KERN_WARNING "handle_viod_request: unexpected subtype!");
		if (hvlpevent_need_ack(event)) {
			event->xRc = HvLpEvent_Rc_InvalidSubtype;
			HvCallEvent_ackLpEvent(event);
		}
	}
}

static void __init probe_disk(struct device_node *vio_root, u32 unit)
{
	HvLpEvent_Rc hvrc;
	struct vio_waitevent we;
	u16 flags = 0;

retry:
	init_completion(&we.com);

	/* Send the open event to OS/400 */
	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
			HvLpEvent_Type_VirtualIo,
			viomajorsubtype_blockio | vioblockopen,
			HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
			viopath_sourceinst(viopath_hostLp),
			viopath_targetinst(viopath_hostLp),
			(u64)(unsigned long)&we, VIOVERSION << 16,
			((u64)unit << 48) | ((u64)flags<< 32),
			0, 0, 0);
	if (hvrc != 0) {
		printk(KERN_WARNING "probe_disk: bad rc on HV open %d\n",
			(int)hvrc);
		return;
	}

	wait_for_completion(&we.com);

	if (we.rc != 0) {
		if (flags != 0)
			return;
		/* try again with read only flag set */
		flags = vioblockflags_ro;
		goto retry;
	}

	/* Send the close event to OS/400.  We DON'T expect a response */
	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
			HvLpEvent_Type_VirtualIo,
			viomajorsubtype_blockio | vioblockclose,
			HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
			viopath_sourceinst(viopath_hostLp),
			viopath_targetinst(viopath_hostLp),
			0, VIOVERSION << 16,
			((u64)unit << 48) | ((u64)flags << 32),
			0, 0, 0);
	if (hvrc != 0) {
		printk(KERN_WARNING "probe_disk: "
		       "bad rc sending event to OS/400 %d\n", (int)hvrc);
		return;
	}

	do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
			"block", "IBM,iSeries-viodasd", NULL);
}

static void __init get_viodasd_info(struct device_node *vio_root)
{
	int rc;
	u32 unit;

	rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, 2);
	if (rc) {
		printk(KERN_WARNING "get_viodasd_info: "
		       "error opening path to host partition %d\n",
		       viopath_hostLp);
		return;
	}

	/* Initialize our request handler */
	vio_setHandler(viomajorsubtype_blockio, handle_block_event);

	for (unit = 0; unit < HVMAXARCHITECTEDVIRTUALDISKS; unit++)
		probe_disk(vio_root, unit);

	vio_clearHandler(viomajorsubtype_blockio);
	viopath_close(viopath_hostLp, viomajorsubtype_blockio, 2);
}

static void __init handle_cd_event(struct HvLpEvent *event)
{
	struct viocdlpevent *bevent;
@@ -233,48 +430,8 @@ static void __init get_viocd_info(struct device_node *vio_root)

	for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) &&
			unitinfo[unit].rsrcname[0]; unit++) {
		struct device_node *np;
		char name[64];
		u32 reg = FIRST_VIOCD + unit;

		snprintf(name, sizeof(name), "/vdevice/viocd@%08x", reg);
		np = new_node(name, vio_root);
		if (!np)
			goto hv_free;
		if (!add_string_property(np, "name", "viocd") ||
			!add_string_property(np, "device_type", "block") ||
			!add_string_property(np, "compatible",
				"IBM,iSeries-viocd") ||
			!add_raw_property(np, "reg", sizeof(reg), &reg) ||
			!add_raw_property(np, "linux,unit_address",
				sizeof(unit), &unit) ||
			!add_raw_property(np, "linux,vio_rsrcname",
				sizeof(unitinfo[unit].rsrcname),
				unitinfo[unit].rsrcname) ||
			!add_raw_property(np, "linux,vio_type",
				sizeof(unitinfo[unit].type),
				unitinfo[unit].type) ||
			!add_raw_property(np, "linux,vio_model",
				sizeof(unitinfo[unit].model),
				unitinfo[unit].model))
			goto node_free;
		np->name = of_get_property(np, "name", NULL);
		np->type = of_get_property(np, "device_type", NULL);
		of_attach_node(np);
#ifdef CONFIG_PROC_DEVICETREE
		if (vio_root->pde) {
			struct proc_dir_entry *ent;

			ent = proc_mkdir(strrchr(np->full_name, '/') + 1,
					vio_root->pde);
			if (ent)
				proc_device_tree_add_node(np, ent);
		}
#endif
		continue;

 node_free:
		free_node(np);
		if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit,
				"block", "IBM,iSeries-viocd", &unitinfo[unit]))
			break;
	}

@@ -350,48 +507,9 @@ static void __init get_viotape_info(struct device_node *vio_root)

	for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) &&
			unitinfo[unit].rsrcname[0]; unit++) {
		struct device_node *np;
		char name[64];
		u32 reg = FIRST_VIOTAPE + unit;

		snprintf(name, sizeof(name), "/vdevice/viotape@%08x", reg);
		np = new_node(name, vio_root);
		if (!np)
			goto hv_free;
		if (!add_string_property(np, "name", "viotape") ||
			!add_string_property(np, "device_type", "byte") ||
			!add_string_property(np, "compatible",
				"IBM,iSeries-viotape") ||
			!add_raw_property(np, "reg", sizeof(reg), &reg) ||
			!add_raw_property(np, "linux,unit_address",
				sizeof(unit), &unit) ||
			!add_raw_property(np, "linux,vio_rsrcname",
				sizeof(unitinfo[unit].rsrcname),
				unitinfo[unit].rsrcname) ||
			!add_raw_property(np, "linux,vio_type",
				sizeof(unitinfo[unit].type),
				unitinfo[unit].type) ||
			!add_raw_property(np, "linux,vio_model",
				sizeof(unitinfo[unit].model),
				unitinfo[unit].model))
			goto node_free;
		np->name = of_get_property(np, "name", NULL);
		np->type = of_get_property(np, "device_type", NULL);
		of_attach_node(np);
#ifdef CONFIG_PROC_DEVICETREE
		if (vio_root->pde) {
			struct proc_dir_entry *ent;

			ent = proc_mkdir(strrchr(np->full_name, '/') + 1,
					vio_root->pde);
			if (ent)
				proc_device_tree_add_node(np, ent);
		}
#endif
		continue;

 node_free:
		free_node(np);
		if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit,
				unit, "byte", "IBM,iSeries-viotape",
				&unitinfo[unit]))
			break;
	}

@@ -422,6 +540,7 @@ static int __init iseries_vio_init(void)
			goto put_node;
	}

	get_viodasd_info(vio_root);
	get_viocd_info(vio_root);
	get_viotape_info(vio_root);

+25 −52
Original line number Diff line number Diff line
@@ -74,53 +74,9 @@ enum {
static DEFINE_SPINLOCK(viodasd_spinlock);

#define VIOMAXREQ		16
#define VIOMAXBLOCKDMA		12

#define DEVICE_NO(cell)	((struct viodasd_device *)(cell) - &viodasd_devices[0])

struct open_data {
	u64	disk_size;
	u16	max_disk;
	u16	cylinders;
	u16	tracks;
	u16	sectors;
	u16	bytes_per_sector;
};

struct rw_data {
	u64	offset;
	struct {
		u32	token;
		u32	reserved;
		u64	len;
	} dma_info[VIOMAXBLOCKDMA];
};

struct vioblocklpevent {
	struct HvLpEvent	event;
	u32			reserved;
	u16			version;
	u16			sub_result;
	u16			disk;
	u16			flags;
	union {
		struct open_data	open_data;
		struct rw_data		rw_data;
		u64			changed;
	} u;
};

#define vioblockflags_ro   0x0001

enum vioblocksubtype {
	vioblockopen = 0x0001,
	vioblockclose = 0x0002,
	vioblockread = 0x0003,
	vioblockwrite = 0x0004,
	vioblockflush = 0x0005,
	vioblockcheck = 0x0007
};

struct viodasd_waitevent {
	struct completion	com;
	int			rc;
@@ -429,7 +385,7 @@ static void do_viodasd_request(struct request_queue *q)
 * Probe a single disk and fill in the viodasd_device structure
 * for it.
 */
static void probe_disk(struct viodasd_device *d)
static int probe_disk(struct viodasd_device *d)
{
	HvLpEvent_Rc hvrc;
	struct viodasd_waitevent we;
@@ -453,14 +409,14 @@ static void probe_disk(struct viodasd_device *d)
			0, 0, 0);
	if (hvrc != 0) {
		printk(VIOD_KERN_WARNING "bad rc on HV open %d\n", (int)hvrc);
		return;
		return 0;
	}

	wait_for_completion(&we.com);

	if (we.rc != 0) {
		if (flags != 0)
			return;
			return 0;
		/* try again with read only flag set */
		flags = vioblockflags_ro;
		goto retry;
@@ -490,15 +446,32 @@ static void probe_disk(struct viodasd_device *d)
	if (hvrc != 0) {
		printk(VIOD_KERN_WARNING
		       "bad rc sending event to OS/400 %d\n", (int)hvrc);
		return;
		return 0;
	}

	if (d->dev == NULL) {
		/* this is when we reprobe for new disks */
		if (vio_create_viodasd(dev_no) == NULL) {
			printk(VIOD_KERN_WARNING
				"cannot allocate virtual device for disk %d\n",
				dev_no);
			return 0;
		}
		/*
		 * The vio_create_viodasd will have recursed into this
		 * routine with d->dev set to the new vio device and
		 * will finish the setup of the disk below.
		 */
		return 1;
	}

	/* create the request queue for the disk */
	spin_lock_init(&d->q_lock);
	q = blk_init_queue(do_viodasd_request, &d->q_lock);
	if (q == NULL) {
		printk(VIOD_KERN_WARNING "cannot allocate queue for disk %d\n",
				dev_no);
		return;
		return 0;
	}
	g = alloc_disk(1 << PARTITION_SHIFT);
	if (g == NULL) {
@@ -506,7 +479,7 @@ static void probe_disk(struct viodasd_device *d)
				"cannot allocate disk structure for disk %d\n",
				dev_no);
		blk_cleanup_queue(q);
		return;
		return 0;
	}

	d->disk = g;
@@ -538,6 +511,7 @@ static void probe_disk(struct viodasd_device *d)

	/* register us in the global list */
	add_disk(g);
	return 1;
}

/* returns the total number of scatterlist elements converted */
@@ -718,8 +692,7 @@ static int viodasd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
	struct viodasd_device *d = &viodasd_devices[vdev->unit_address];

	d->dev = &vdev->dev;
	probe_disk(d);
	if (d->disk == NULL)
	if (!probe_disk(d))
		return -ENODEV;
	return 0;
}
+47 −0
Original line number Diff line number Diff line
@@ -51,6 +51,51 @@
 */
#define VIO_MAX_SUBTYPES 8

#define VIOMAXBLOCKDMA	12

struct open_data {
	u64	disk_size;
	u16	max_disk;
	u16	cylinders;
	u16	tracks;
	u16	sectors;
	u16	bytes_per_sector;
};

struct rw_data {
	u64	offset;
	struct {
		u32	token;
		u32	reserved;
		u64	len;
	} dma_info[VIOMAXBLOCKDMA];
};

struct vioblocklpevent {
	struct HvLpEvent	event;
	u32			reserved;
	u16			version;
	u16			sub_result;
	u16			disk;
	u16			flags;
	union {
		struct open_data	open_data;
		struct rw_data		rw_data;
		u64			changed;
	} u;
};

#define vioblockflags_ro   0x0001

enum vioblocksubtype {
	vioblockopen = 0x0001,
	vioblockclose = 0x0002,
	vioblockread = 0x0003,
	vioblockwrite = 0x0004,
	vioblockflush = 0x0005,
	vioblockcheck = 0x0007
};

struct viocdlpevent {
	struct HvLpEvent	event;
	u32			reserved;
@@ -133,6 +178,8 @@ extern void vio_set_hostlp(void);
extern void *vio_get_event_buffer(int subtype);
extern void vio_free_event_buffer(int subtype, void *buffer);

extern struct vio_dev *vio_create_viodasd(u32 unit);

extern HvLpIndex viopath_hostLp;
extern HvLpIndex viopath_ourLp;