diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2007-10-11 06:59:54 +0200 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-10-11 12:40:48 +0200 |
commit | 8251b4c481bca72568e9c1042ea11189838e5f6d (patch) | |
tree | 41991f93aec12592885ac33a5312dcf4024ebe5f | |
parent | [POWERPC] iSeries: Move detection of virtual tapes (diff) | |
download | linux-8251b4c481bca72568e9c1042ea11189838e5f6d.tar.xz linux-8251b4c481bca72568e9c1042ea11189838e5f6d.zip |
[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: Stephen Rothwell <sfr@canb.auug.org.au>
Acked-by: Jens Axboe <jens.axboe@oracle.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r-- | arch/powerpc/platforms/iseries/dt.c | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/vio.c | 301 | ||||
-rw-r--r-- | drivers/block/viodasd.c | 77 | ||||
-rw-r--r-- | include/asm-powerpc/iseries/vio.h | 47 |
4 files changed, 282 insertions, 149 deletions
diff --git a/arch/powerpc/platforms/iseries/dt.c b/arch/powerpc/platforms/iseries/dt.c index 2e4ad6b34506..4543c4bc3a56 100644 --- a/arch/powerpc/platforms/iseries/dt.c +++ b/arch/powerpc/platforms/iseries/dt.c @@ -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); } diff --git a/arch/powerpc/platforms/iseries/vio.c b/arch/powerpc/platforms/iseries/vio.c index a4cc990a26a0..910b00b4703e 100644 --- a/arch/powerpc/platforms/iseries/vio.c +++ b/arch/powerpc/platforms/iseries/vio.c @@ -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), ®) || + !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,49 +430,9 @@ 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), ®) || - !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); - break; + if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit, + "block", "IBM,iSeries-viocd", &unitinfo[unit])) + break; } hv_free: @@ -350,49 +507,10 @@ 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), ®) || - !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); - break; + if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit, + unit, "byte", "IBM,iSeries-viotape", + &unitinfo[unit])) + break; } hv_free: @@ -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); diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c index af3969a9c963..e824b672e05a 100644 --- a/drivers/block/viodasd.c +++ b/drivers/block/viodasd.c @@ -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 @@ retry: 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 @@ retry: 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 @@ retry: "cannot allocate disk structure for disk %d\n", dev_no); blk_cleanup_queue(q); - return; + return 0; } d->disk = g; @@ -538,6 +511,7 @@ retry: /* 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; } diff --git a/include/asm-powerpc/iseries/vio.h b/include/asm-powerpc/iseries/vio.h index 2555dfd6fac6..f9ac0d00b951 100644 --- a/include/asm-powerpc/iseries/vio.h +++ b/include/asm-powerpc/iseries/vio.h @@ -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; |