Commit 03cd7b46 authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: common i/o layer.

 - Remove initialization of device.name.
 - Don't do put_device after failed get_device in get_ccwdev_by_busid.
 - Fix read_dev_chars and read_conf_data.
 - Call interrupt function of ccw device if path verification has been started.
 - Replace atomic_return_add by atomic_add_return in qdio.
 - Use wait_event instead of homegrown wait loop.
 - Fix reestablish queue problem.
 - Add ungroup attribute to ccw_group devices and add links from each
   ccw device of a group to the group device.
 - Use BUS_ID_SIZE instead of DEVICE_ID_SIZE.
 - Delay path verification if a basic sense is required.
 - Move qdio shutdown code from qdio_free to qdio_shutdown.
parent 7d853ae3
/* /*
* drivers/s390/cio/ccwgroup.c * drivers/s390/cio/ccwgroup.c
* bus driver for ccwgroup * bus driver for ccwgroup
* $Revision: 1.7 $ * $Revision: 1.15 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
* Author(s): Arnd Bergmann (arndb@de.ibm.com) * Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cohuck@de.ibm.com)
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -14,6 +15,7 @@ ...@@ -14,6 +15,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/dcache.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <asm/ccwdev.h> #include <asm/ccwdev.h>
...@@ -56,6 +58,45 @@ static struct bus_type ccwgroup_bus_type = { ...@@ -56,6 +58,45 @@ static struct bus_type ccwgroup_bus_type = {
.hotplug = ccwgroup_hotplug, .hotplug = ccwgroup_hotplug,
}; };
static inline void
__ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
{
int i;
char str[8];
for (i = 0; i < gdev->count; i++) {
sprintf(str, "cdev%d", i);
sysfs_remove_link(&gdev->dev.kobj, str);
/* Hack: Make sure we act on still valid subdirs. */
if (atomic_read(&gdev->cdev[i]->dev.kobj.dentry->d_count))
sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
"group_device");
}
}
/*
* Provide an 'ungroup' attribute so the user can remove group devices no
* longer needed or accidentially created. Saves memory :)
*/
static ssize_t
ccwgroup_ungroup_store(struct device *dev, const char *buf, size_t count)
{
struct ccwgroup_device *gdev;
gdev = to_ccwgroupdev(dev);
if (gdev->state != CCWGROUP_OFFLINE)
return -EINVAL;
__ccwgroup_remove_symlinks(gdev);
device_unregister(dev);
return count;
}
static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);
static void static void
ccwgroup_release (struct device *dev) ccwgroup_release (struct device *dev)
{ {
...@@ -69,6 +110,40 @@ ccwgroup_release (struct device *dev) ...@@ -69,6 +110,40 @@ ccwgroup_release (struct device *dev)
kfree(gdev); kfree(gdev);
} }
static inline int
__ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
{
char str[8];
int i, rc;
for (i = 0; i < gdev->count; i++) {
rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj, &gdev->dev.kobj,
"group_device");
if (rc) {
for (--i; i >= 0; i--)
sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
"group_device");
return rc;
}
}
for (i = 0; i < gdev->count; i++) {
sprintf(str, "cdev%d", i);
rc = sysfs_create_link(&gdev->dev.kobj, &gdev->cdev[i]->dev.kobj,
str);
if (rc) {
for (--i; i >= 0; i--) {
sprintf(str, "cdev%d", i);
sysfs_remove_link(&gdev->dev.kobj, str);
}
for (i = 0; i < gdev->count; i++)
sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
"group_device");
return rc;
}
}
return 0;
}
/* /*
* try to add a new ccwgroup device for one driver * try to add a new ccwgroup device for one driver
* argc and argv[] are a list of bus_id's of devices * argc and argv[] are a list of bus_id's of devices
...@@ -82,6 +157,7 @@ ccwgroup_create(struct device *root, ...@@ -82,6 +157,7 @@ ccwgroup_create(struct device *root,
{ {
struct ccwgroup_device *gdev; struct ccwgroup_device *gdev;
int i; int i;
int rc;
if (argc > 256) /* disallow dumb users */ if (argc > 256) /* disallow dumb users */
return -EINVAL; return -EINVAL;
...@@ -90,6 +166,8 @@ ccwgroup_create(struct device *root, ...@@ -90,6 +166,8 @@ ccwgroup_create(struct device *root,
if (!gdev) if (!gdev)
return -ENOMEM; return -ENOMEM;
memset(gdev, 0, sizeof(*gdev) + argc*sizeof(gdev->cdev[0]));
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]); gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]);
...@@ -97,8 +175,10 @@ ccwgroup_create(struct device *root, ...@@ -97,8 +175,10 @@ ccwgroup_create(struct device *root,
* order to be grouped */ * order to be grouped */
if (!gdev->cdev[i] if (!gdev->cdev[i]
|| gdev->cdev[i]->id.driver_info != || gdev->cdev[i]->id.driver_info !=
gdev->cdev[0]->id.driver_info) gdev->cdev[0]->id.driver_info) {
rc = -EINVAL;
goto error; goto error;
}
} }
*gdev = (struct ccwgroup_device) { *gdev = (struct ccwgroup_device) {
...@@ -114,9 +194,24 @@ ccwgroup_create(struct device *root, ...@@ -114,9 +194,24 @@ ccwgroup_create(struct device *root,
snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s", snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s",
gdev->cdev[0]->dev.bus_id); gdev->cdev[0]->dev.bus_id);
/* TODO: make symlinks for sysfs */ rc = device_register(&gdev->dev);
return device_register(&gdev->dev);
if (rc)
goto error;
rc = device_create_file(&gdev->dev, &dev_attr_ungroup);
if (rc) {
device_unregister(&gdev->dev);
goto error;
}
rc = __ccwgroup_create_symlinks(gdev);
if (!rc)
return 0;
device_remove_file(&gdev->dev, &dev_attr_ungroup);
device_unregister(&gdev->dev);
error: error:
for (i = 0; i < argc; i++) for (i = 0; i < argc; i++)
if (gdev->cdev[i]) if (gdev->cdev[i])
...@@ -124,7 +219,7 @@ ccwgroup_create(struct device *root, ...@@ -124,7 +219,7 @@ ccwgroup_create(struct device *root,
kfree(gdev); kfree(gdev);
return -EINVAL; return rc;
} }
static int __init static int __init
...@@ -213,7 +308,7 @@ ccwgroup_online_show (struct device *dev, char *buf) ...@@ -213,7 +308,7 @@ ccwgroup_online_show (struct device *dev, char *buf)
online = (to_ccwgroupdev(dev)->state == CCWGROUP_ONLINE); online = (to_ccwgroupdev(dev)->state == CCWGROUP_ONLINE);
return sprintf(buf, online ? "yes\n" : "no\n"); return sprintf(buf, online ? "1\n" : "0\n");
} }
static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store); static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);
...@@ -286,9 +381,59 @@ ccwgroup_probe_ccwdev(struct ccw_device *cdev) ...@@ -286,9 +381,59 @@ ccwgroup_probe_ccwdev(struct ccw_device *cdev)
return 0; return 0;
} }
static inline struct ccwgroup_device *
__ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev)
{
struct ccwgroup_device *gdev;
struct list_head *entry;
struct device *dev;
int i, found;
/*
* Find groupdevice cdev belongs to.
* Unfortunately, we can't use bus_for_each_dev() because of the
* semaphore (and return value of fn() is int).
*/
if (!get_bus(&ccwgroup_bus_type))
return NULL;
gdev = NULL;
down_read(&ccwgroup_bus_type.subsys.rwsem);
list_for_each(entry, &ccwgroup_bus_type.devices.list) {
dev = get_device(container_of(entry, struct device, bus_list));
found = 0;
if (!dev)
continue;
gdev = to_ccwgroupdev(dev);
for (i = 0; i < gdev->count && (!found); i++) {
if (gdev->cdev[i] == cdev)
found = 1;
}
if (found)
break;
put_device(dev);
gdev = NULL;
}
up_read(&ccwgroup_bus_type.subsys.rwsem);
put_bus(&ccwgroup_bus_type);
return gdev;
}
int int
ccwgroup_remove_ccwdev(struct ccw_device *cdev) ccwgroup_remove_ccwdev(struct ccw_device *cdev)
{ {
struct ccwgroup_device *gdev;
/* If one of its devices is gone, the whole group is done for. */
gdev = __ccwgroup_get_gdev_by_cdev(cdev);
if (gdev) {
ccwgroup_set_offline(gdev);
__ccwgroup_remove_symlinks(gdev);
device_unregister(&gdev->dev);
put_device(&gdev->dev);
}
return 0; return 0;
} }
......
/* /*
* drivers/s390/cio/chsc.c * drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call * S/390 common I/O routines -- channel subsystem call
* $Revision: 1.74 $ * $Revision: 1.77 $
* *
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -865,9 +865,7 @@ new_channel_path(int chpid, int status) ...@@ -865,9 +865,7 @@ new_channel_path(int chpid, int status)
chp->state = status; chp->state = status;
chp->dev.parent = &css_bus_device; chp->dev.parent = &css_bus_device;
snprintf(chp->dev.name, DEVICE_NAME_SIZE, snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid);
"channel path %x", chpid);
snprintf(chp->dev.bus_id, DEVICE_ID_SIZE, "chp%x", chpid);
/* make it known to the system */ /* make it known to the system */
ret = device_register(&chp->dev); ret = device_register(&chp->dev);
......
/* /*
* drivers/s390/cio/cio.c * drivers/s390/cio/cio.c
* S/390 common I/O routines -- low level i/o calls * S/390 common I/O routines -- low level i/o calls
* $Revision: 1.100 $ * $Revision: 1.105 $
* *
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -277,11 +277,6 @@ cio_halt(struct subchannel *sch) ...@@ -277,11 +277,6 @@ cio_halt(struct subchannel *sch)
if (!sch) if (!sch)
return -ENODEV; return -ENODEV;
/*
* we ignore the halt_io() request if ending_status was received but
* a SENSE operation is waiting for completion.
*/
sprintf (dbf_txt, "haltIO%x", sch->irq); sprintf (dbf_txt, "haltIO%x", sch->irq);
CIO_TRACE_EVENT (2, dbf_txt); CIO_TRACE_EVENT (2, dbf_txt);
...@@ -316,10 +311,6 @@ cio_clear(struct subchannel *sch) ...@@ -316,10 +311,6 @@ cio_clear(struct subchannel *sch)
if (!sch) if (!sch)
return -ENODEV; return -ENODEV;
/*
* we ignore the clear_io() request if ending_status was received but
* a SENSE operation is waiting for completion.
*/
sprintf (dbf_txt, "clearIO%x", sch->irq); sprintf (dbf_txt, "clearIO%x", sch->irq);
CIO_TRACE_EVENT (2, dbf_txt); CIO_TRACE_EVENT (2, dbf_txt);
...@@ -380,7 +371,7 @@ cio_cancel (struct subchannel *sch) ...@@ -380,7 +371,7 @@ cio_cancel (struct subchannel *sch)
} }
/* /*
* Function: cio_cancel * Function: cio_modify
* Issues a "Modify Subchannel" on the specified subchannel * Issues a "Modify Subchannel" on the specified subchannel
*/ */
static int static int
...@@ -469,11 +460,6 @@ cio_disable_subchannel (struct subchannel *sch) ...@@ -469,11 +460,6 @@ cio_disable_subchannel (struct subchannel *sch)
sprintf (dbf_txt, "dissch%x", sch->irq); sprintf (dbf_txt, "dissch%x", sch->irq);
CIO_TRACE_EVENT (2, dbf_txt); CIO_TRACE_EVENT (2, dbf_txt);
/*
* If device isn't operational we have to perform delayed
* disabling when the next interrupt occurs - unless the
* irq is re-requested prior to the interrupt to occur.
*/
ccode = stsch (sch->irq, &sch->schib); ccode = stsch (sch->irq, &sch->schib);
if (ccode == 3) /* Not operational. */ if (ccode == 3) /* Not operational. */
return -ENODEV; return -ENODEV;
......
/* /*
* drivers/s390/cio/css.c * drivers/s390/cio/css.c
* driver for channel subsystem * driver for channel subsystem
* $Revision: 1.43 $ * $Revision: 1.49 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -27,7 +27,6 @@ unsigned int highest_subchannel; ...@@ -27,7 +27,6 @@ unsigned int highest_subchannel;
int css_init_done = 0; int css_init_done = 0;
struct device css_bus_device = { struct device css_bus_device = {
.name = "Channel Subsystem 0",
.bus_id = "css0", .bus_id = "css0",
}; };
...@@ -79,17 +78,6 @@ css_free_subchannel(int irq) ...@@ -79,17 +78,6 @@ css_free_subchannel(int irq)
static int static int
css_register_subchannel(struct subchannel *sch) css_register_subchannel(struct subchannel *sch)
{ {
static const char *subchannel_types[] = {
"I/O Subchannel",
"CHSC Subchannel",
"Message Subchannel",
"ADM Subchannel",
"undefined subchannel type 4",
"undefined subchannel type 5",
"undefined subchannel type 6",
"undefined subchannel type 7",
"undefined subchannel type 8",
};
int ret; int ret;
/* Initialize the subchannel structure */ /* Initialize the subchannel structure */
...@@ -97,8 +85,7 @@ css_register_subchannel(struct subchannel *sch) ...@@ -97,8 +85,7 @@ css_register_subchannel(struct subchannel *sch)
sch->dev.bus = &css_bus_type; sch->dev.bus = &css_bus_type;
/* Set a name for the subchannel */ /* Set a name for the subchannel */
strlcpy (sch->dev.name, subchannel_types[sch->st], DEVICE_NAME_SIZE); snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.0.%04x", sch->irq);
snprintf (sch->dev.bus_id, DEVICE_ID_SIZE, "0:%04x", sch->irq);
/* make it known to the system */ /* make it known to the system */
ret = device_register(&sch->dev); ret = device_register(&sch->dev);
......
...@@ -78,6 +78,7 @@ struct ccw_device_private { ...@@ -78,6 +78,7 @@ struct ccw_device_private {
unsigned int pgid_single:1; /* use single path for Set PGID */ unsigned int pgid_single:1; /* use single path for Set PGID */
unsigned int esid:1; /* Ext. SenseID supported by HW */ unsigned int esid:1; /* Ext. SenseID supported by HW */
unsigned int dosense:1; /* delayed SENSE required */ unsigned int dosense:1; /* delayed SENSE required */
unsigned int doverify:1; /* delayed path verification */
} __attribute__((packed)) flags; } __attribute__((packed)) flags;
unsigned long intparm; /* user interruption parameter */ unsigned long intparm; /* user interruption parameter */
struct qdio_irq *qdio_data; struct qdio_irq *qdio_data;
......
/* /*
* drivers/s390/cio/device.c * drivers/s390/cio/device.c
* bus driver for ccw devices * bus driver for ccw devices
* $Revision: 1.60 $ * $Revision: 1.70 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -229,7 +229,7 @@ online_show (struct device *dev, char *buf) ...@@ -229,7 +229,7 @@ online_show (struct device *dev, char *buf)
{ {
struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device *cdev = to_ccwdev(dev);
return sprintf(buf, cdev->online ? "yes\n" : "no\n"); return sprintf(buf, cdev->online ? "1\n" : "0\n");
} }
void void
...@@ -537,8 +537,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) ...@@ -537,8 +537,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
init_timer(&cdev->private->timer); init_timer(&cdev->private->timer);
/* Set an initial name for the device. */ /* Set an initial name for the device. */
snprintf (cdev->dev.name, DEVICE_NAME_SIZE,"ccw device"); snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x",
snprintf (cdev->dev.bus_id, DEVICE_ID_SIZE, "0:%04x",
sch->schib.pmcw.dev); sch->schib.pmcw.dev);
/* Increase counter of devices currently in recognition. */ /* Increase counter of devices currently in recognition. */
...@@ -679,6 +678,7 @@ ccw_device_probe_console(void) ...@@ -679,6 +678,7 @@ ccw_device_probe_console(void)
console_cdev_in_use = 0; console_cdev_in_use = 0;
return ERR_PTR(ret); return ERR_PTR(ret);
} }
console_cdev.online = 1;
return &console_cdev; return &console_cdev;
} }
#endif #endif
...@@ -702,10 +702,12 @@ get_ccwdev_by_busid(struct ccw_driver *cdrv, const char *bus_id) ...@@ -702,10 +702,12 @@ get_ccwdev_by_busid(struct ccw_driver *cdrv, const char *bus_id)
list_for_each_entry(d, &drv->devices, driver_list) { list_for_each_entry(d, &drv->devices, driver_list) {
dev = get_device(d); dev = get_device(d);
if (dev && !strncmp(bus_id, dev->bus_id, DEVICE_ID_SIZE)) if (dev && !strncmp(bus_id, dev->bus_id, BUS_ID_SIZE))
break; break;
else else if (dev) {
put_device(dev); put_device(dev);
dev = NULL;
}
} }
up_read(&drv->bus->subsys.rwsem); up_read(&drv->bus->subsys.rwsem);
put_driver(drv); put_driver(drv);
......
...@@ -15,8 +15,6 @@ enum dev_state { ...@@ -15,8 +15,6 @@ enum dev_state {
DEV_STATE_DISBAND_PGID, DEV_STATE_DISBAND_PGID,
DEV_STATE_BOXED, DEV_STATE_BOXED,
/* states to wait for i/o completion before doing something */ /* states to wait for i/o completion before doing something */
DEV_STATE_ONLINE_VERIFY,
DEV_STATE_W4SENSE_VERIFY,
DEV_STATE_CLEAR_VERIFY, DEV_STATE_CLEAR_VERIFY,
DEV_STATE_TIMEOUT_KILL, DEV_STATE_TIMEOUT_KILL,
/* last element! */ /* last element! */
...@@ -95,7 +93,7 @@ void ccw_device_disband_start(struct ccw_device *); ...@@ -95,7 +93,7 @@ void ccw_device_disband_start(struct ccw_device *);
void ccw_device_disband_irq(struct ccw_device *, enum dev_event); void ccw_device_disband_irq(struct ccw_device *, enum dev_event);
void ccw_device_disband_done(struct ccw_device *, int); void ccw_device_disband_done(struct ccw_device *, int);
void ccw_device_call_handler(struct ccw_device *); int ccw_device_call_handler(struct ccw_device *);
void ccw_device_add_stlck(void *); void ccw_device_add_stlck(void *);
int ccw_device_stlck(struct ccw_device *); int ccw_device_stlck(struct ccw_device *);
......
...@@ -269,6 +269,7 @@ ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -269,6 +269,7 @@ ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event)
void void
ccw_device_verify_done(struct ccw_device *cdev, int err) ccw_device_verify_done(struct ccw_device *cdev, int err)
{ {
cdev->private->flags.doverify = 0;
switch (err) { switch (err) {
case 0: case 0:
ccw_device_done(cdev, DEV_STATE_ONLINE); ccw_device_done(cdev, DEV_STATE_ONLINE);
...@@ -419,13 +420,21 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -419,13 +420,21 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
{ {
struct subchannel *sch; struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent); if (!cdev->private->options.pgroup)
return;
if (cdev->private->state == DEV_STATE_W4SENSE) { if (cdev->private->state == DEV_STATE_W4SENSE) {
cdev->private->state = DEV_STATE_W4SENSE_VERIFY; cdev->private->flags.doverify = 1;
return; return;
} }
if (sch->schib.scsw.actl != 0) { sch = to_subchannel(cdev->dev.parent);
cdev->private->state = DEV_STATE_ONLINE_VERIFY; if (sch->schib.scsw.actl != 0 ||
(cdev->private->irb.scsw.stctl & SCSW_STCTL_STATUS_PEND)) {
/*
* No final status yet or final status not yet delivered
* to the device driver. Can't do path verfication now,
* delay until final status was delivered.
*/
cdev->private->flags.doverify = 1;
return; return;
} }
/* Device is idle, we can do the path verification. */ /* Device is idle, we can do the path verification. */
...@@ -453,19 +462,14 @@ ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -453,19 +462,14 @@ ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
ccw_device_accumulate_irb(cdev, irb); ccw_device_accumulate_irb(cdev, irb);
if (cdev->private->flags.dosense) { if (cdev->private->flags.dosense) {
if (ccw_device_do_sense(cdev, irb) == 0) { if (ccw_device_do_sense(cdev, irb) == 0) {
/* Check if we have to trigger path verification. */ cdev->private->state = DEV_STATE_W4SENSE;
if (irb->esw.esw0.erw.pvrf)
cdev->private->state = DEV_STATE_W4SENSE_VERIFY;
else
cdev->private->state = DEV_STATE_W4SENSE;
} }
return; return;
} }
if (irb->esw.esw0.erw.pvrf) /* Call the handler. */
/* Try to start path verification. */ if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify)
/* Start delayed path verification. */
ccw_device_online_verify(cdev, 0); ccw_device_online_verify(cdev, 0);
/* No basic sense required, call the handler. */
ccw_device_call_handler(cdev);
} }
/* /*
...@@ -485,32 +489,6 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -485,32 +489,6 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
ERR_PTR(-ETIMEDOUT)); ERR_PTR(-ETIMEDOUT));
} }
static void
ccw_device_irq_verify(struct ccw_device *cdev, enum dev_event dev_event)
{
struct irb *irb;
irb = (struct irb *) __LC_IRB;
/* Check for unsolicited interrupt. */
if (irb->scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
if (cdev->handler)
cdev->handler (cdev, 0, irb);
return;
}
/* Accumulate status and find out if a basic sense is needed. */
ccw_device_accumulate_irb(cdev, irb);
if (cdev->private->flags.dosense) {
if (ccw_device_do_sense(cdev, irb) == 0)
cdev->private->state = DEV_STATE_W4SENSE_VERIFY;
return;
}
/* Try to start delayed device verification. */
ccw_device_online_verify(cdev, 0);
/* No basic sense required, call the handler. */
ccw_device_call_handler(cdev);
}
/* /*
* Got an interrupt for a basic sense. * Got an interrupt for a basic sense.
*/ */
...@@ -530,46 +508,15 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -530,46 +508,15 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
/* Add basic sense info to irb. */ /* Add basic sense info to irb. */
ccw_device_accumulate_basic_sense(cdev, irb); ccw_device_accumulate_basic_sense(cdev, irb);
if (cdev->private->flags.dosense) { if (cdev->private->flags.dosense) {
/* Check if we have to trigger path verification. */
if (irb->esw.esw0.erw.pvrf)
cdev->private->state = DEV_STATE_W4SENSE_VERIFY;
/* Another basic sense is needed. */ /* Another basic sense is needed. */
ccw_device_do_sense(cdev, irb); ccw_device_do_sense(cdev, irb);
return; return;
} }
cdev->private->state = DEV_STATE_ONLINE; cdev->private->state = DEV_STATE_ONLINE;
if (irb->esw.esw0.erw.pvrf)
/* Try to start path verification. */
ccw_device_online_verify(cdev, 0);
/* Call the handler. */ /* Call the handler. */
ccw_device_call_handler(cdev); if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify)
} /* Start delayed path verification. */
ccw_device_online_verify(cdev, 0);
static void
ccw_device_w4sense_verify(struct ccw_device *cdev, enum dev_event dev_event)
{
struct irb *irb;
irb = (struct irb *) __LC_IRB;
/* Check for unsolicited interrupt. */
if (irb->scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
if (cdev->handler)
cdev->handler (cdev, 0, irb);
return;
}
/* Add basic sense info to irb. */
ccw_device_accumulate_basic_sense(cdev, irb);
if (cdev->private->flags.dosense) {
/* Another basic sense is needed. */
ccw_device_do_sense(cdev, irb);
return;
}
cdev->private->state = DEV_STATE_ONLINE_VERIFY;
/* Start delayed device verification. */
ccw_device_online_verify(cdev, 0);
/* Call the handler. */
ccw_device_call_handler(cdev);
} }
static void static void
...@@ -718,18 +665,6 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { ...@@ -718,18 +665,6 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_VERIFY] ccw_device_nop, [DEV_EVENT_VERIFY] ccw_device_nop,
}, },
/* states to wait for i/o completion before doing something */ /* states to wait for i/o completion before doing something */
[DEV_STATE_ONLINE_VERIFY] {
[DEV_EVENT_NOTOPER] ccw_device_online_notoper,
[DEV_EVENT_INTERRUPT] ccw_device_irq_verify,
[DEV_EVENT_TIMEOUT] ccw_device_nop,
[DEV_EVENT_VERIFY] ccw_device_nop,
},
[DEV_STATE_W4SENSE_VERIFY] {
[DEV_EVENT_NOTOPER] ccw_device_online_notoper,
[DEV_EVENT_INTERRUPT] ccw_device_w4sense_verify,
[DEV_EVENT_TIMEOUT] ccw_device_nop,
[DEV_EVENT_VERIFY] ccw_device_nop,
},
[DEV_STATE_CLEAR_VERIFY] { [DEV_STATE_CLEAR_VERIFY] {
[DEV_EVENT_NOTOPER] ccw_device_online_notoper, [DEV_EVENT_NOTOPER] ccw_device_online_notoper,
[DEV_EVENT_INTERRUPT] ccw_device_clear_verify, [DEV_EVENT_INTERRUPT] ccw_device_clear_verify,
......
...@@ -342,3 +342,4 @@ ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -342,3 +342,4 @@ ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event)
} }
} }
EXPORT_SYMBOL(diag210);
...@@ -50,9 +50,7 @@ ccw_device_clear(struct ccw_device *cdev, unsigned long intparm) ...@@ -50,9 +50,7 @@ ccw_device_clear(struct ccw_device *cdev, unsigned long intparm)
if (!cdev) if (!cdev)
return -ENODEV; return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE && if (cdev->private->state != DEV_STATE_ONLINE &&
cdev->private->state != DEV_STATE_W4SENSE && cdev->private->state != DEV_STATE_W4SENSE)
cdev->private->state != DEV_STATE_ONLINE_VERIFY &&
cdev->private->state != DEV_STATE_W4SENSE_VERIFY)
return -EINVAL; return -EINVAL;
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
if (!sch) if (!sch)
...@@ -76,7 +74,8 @@ ccw_device_start(struct ccw_device *cdev, struct ccw1 *cpa, ...@@ -76,7 +74,8 @@ ccw_device_start(struct ccw_device *cdev, struct ccw1 *cpa,
if (!sch) if (!sch)
return -ENODEV; return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE || if (cdev->private->state != DEV_STATE_ONLINE ||
sch->schib.scsw.actl != 0) sch->schib.scsw.actl != 0 ||
cdev->private->flags.doverify)
return -EBUSY; return -EBUSY;
ret = cio_set_options (sch, flags); ret = cio_set_options (sch, flags);
if (ret) if (ret)
...@@ -113,9 +112,7 @@ ccw_device_halt(struct ccw_device *cdev, unsigned long intparm) ...@@ -113,9 +112,7 @@ ccw_device_halt(struct ccw_device *cdev, unsigned long intparm)
if (!cdev) if (!cdev)
return -ENODEV; return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE && if (cdev->private->state != DEV_STATE_ONLINE &&
cdev->private->state != DEV_STATE_W4SENSE && cdev->private->state != DEV_STATE_W4SENSE)
cdev->private->state != DEV_STATE_ONLINE_VERIFY &&
cdev->private->state != DEV_STATE_W4SENSE_VERIFY)
return -EINVAL; return -EINVAL;
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
if (!sch) if (!sch)
...@@ -145,7 +142,7 @@ ccw_device_resume(struct ccw_device *cdev) ...@@ -145,7 +142,7 @@ ccw_device_resume(struct ccw_device *cdev)
/* /*
* Pass interrupt to device driver. * Pass interrupt to device driver.
*/ */
void int
ccw_device_call_handler(struct ccw_device *cdev) ccw_device_call_handler(struct ccw_device *cdev)
{ {
struct subchannel *sch; struct subchannel *sch;
...@@ -167,7 +164,7 @@ ccw_device_call_handler(struct ccw_device *cdev) ...@@ -167,7 +164,7 @@ ccw_device_call_handler(struct ccw_device *cdev)
!(stctl & SCSW_STCTL_INTER_STATUS) && !(stctl & SCSW_STCTL_INTER_STATUS) &&
!(cdev->private->options.fast && !(cdev->private->options.fast &&
(stctl & SCSW_STCTL_PRIM_STATUS))) (stctl & SCSW_STCTL_PRIM_STATUS)))
return; return 0;
/* /*
* Now we are ready to call the device driver interrupt handler. * Now we are ready to call the device driver interrupt handler.
...@@ -180,6 +177,8 @@ ccw_device_call_handler(struct ccw_device *cdev) ...@@ -180,6 +177,8 @@ ccw_device_call_handler(struct ccw_device *cdev)
* Clear the old and now useless interrupt response block. * Clear the old and now useless interrupt response block.
*/ */
memset(&cdev->private->irb, 0, sizeof(struct irb)); memset(&cdev->private->irb, 0, sizeof(struct irb));
return 1;
} }
/* /*
...@@ -190,6 +189,8 @@ ccw_device_get_ciw(struct ccw_device *cdev, __u32 ct) ...@@ -190,6 +189,8 @@ ccw_device_get_ciw(struct ccw_device *cdev, __u32 ct)
{ {
int ciw_cnt; int ciw_cnt;
if (cdev->private->flags.esid == 0)
return NULL;
for (ciw_cnt = 0; ciw_cnt < MAX_CIWS; ciw_cnt++) for (ciw_cnt = 0; ciw_cnt < MAX_CIWS; ciw_cnt++)
if (cdev->private->senseid.ciw[ciw_cnt].ct == ct) if (cdev->private->senseid.ciw[ciw_cnt].ct == ct)
return cdev->private->senseid.ciw + ciw_cnt; return cdev->private->senseid.ciw + ciw_cnt;
...@@ -211,25 +212,89 @@ ccw_device_get_path_mask(struct ccw_device *cdev) ...@@ -211,25 +212,89 @@ ccw_device_get_path_mask(struct ccw_device *cdev)
static void static void
ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb) ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb)
{ {
if (!ip)
/* unsolicited interrupt */
return;
/* Abuse intparm for error reporting. */
if (IS_ERR(irb))
cdev->private->intparm = -EIO;
else if ((irb->scsw.dstat !=
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
(irb->scsw.cstat != 0)) {
/*
* We didn't get channel end / device end. Check if path
* verification has been started; we can retry after it has
* finished. We also retry unit checks except for command reject
* or intervention required.
*/
if (cdev->private->flags.doverify ||
cdev->private->state == DEV_STATE_VERIFY)
cdev->private->intparm = -EAGAIN;
if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
!(irb->ecw[0] &
(SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ)))
cdev->private->intparm = -EAGAIN;
else
cdev->private->intparm = -EIO;
} else
cdev->private->intparm = 0;
wake_up(&cdev->private->wait_q);
}
static inline int
__ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic,
unsigned long flags)
{
int ret;
struct subchannel *sch; struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
if (!IS_ERR(irb)) do {
memcpy(&sch->schib.scsw, &irb->scsw, sizeof(struct scsw)); ret = cio_start (sch, ccw, magic, 0);
wake_up(&cdev->private->wait_q); if ((ret == -EBUSY) || (ret == -EACCES)) {
/* Try again later. */
schedule_timeout(1);
continue;
}
if (ret != 0)
/* Non-retryable error. */
break;
/* Wait for end of request. */
cdev->private->intparm = magic;
spin_unlock_irqrestore(&sch->lock, flags);
wait_event(cdev->private->wait_q,
(cdev->private->intparm == -EIO) ||
(cdev->private->intparm == -EAGAIN) ||
(cdev->private->intparm == 0));
spin_lock_irqsave(&sch->lock, flags);
/* Check at least for channel end / device end */
if (cdev->private->intparm == -EIO) {
/* Non-retryable error. */
ret = -EIO;
break;
}
if (cdev->private->intparm == 0)
/* Success. */
break;
/* Try again later. */
schedule_timeout(1);
} while (1);
return ret;
} }
/* /**
* This routine returns the characteristics for the device * read_dev_chars() - read device characteristics
* specified. Some old devices might not provide the necessary * @param cdev target ccw device
* command code information during SenseID processing. In this * @param buffer pointer to buffer for rdc data
* case the function returns -EINVAL. Otherwise the function * @param length size of rdc data
* allocates a decice specific data buffer and provides the * @returns 0 for success, negative error value on failure
* device characteristics together with the buffer size. Its *
* the callers responability to release the kernel memory if * Context:
* not longer needed. In case of persistent I/O problems -EBUSY * called for online device, lock not held
* is returned. **/
*/
int int
read_dev_chars (struct ccw_device *cdev, void **buffer, int length) read_dev_chars (struct ccw_device *cdev, void **buffer, int length)
{ {
...@@ -237,13 +302,11 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length) ...@@ -237,13 +302,11 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length)
char dbf_txt[15]; char dbf_txt[15];
unsigned long flags; unsigned long flags;
struct subchannel *sch; struct subchannel *sch;
int retry;
int ret; int ret;
struct ccw1 *rdc_ccw;
if (!cdev) if (!cdev)
return -ENODEV; return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE)
return -EINVAL;
if (!buffer || !length) if (!buffer || !length)
return -EINVAL; return -EINVAL;
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
...@@ -251,44 +314,38 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length) ...@@ -251,44 +314,38 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length)
sprintf (dbf_txt, "rddevch%x", sch->irq); sprintf (dbf_txt, "rddevch%x", sch->irq);
CIO_TRACE_EVENT (4, dbf_txt); CIO_TRACE_EVENT (4, dbf_txt);
cdev->private->iccws[0].cmd_code = CCW_CMD_RDC; rdc_ccw = kmalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
cdev->private->iccws[0].count = length; if (!rdc_ccw)
cdev->private->iccws[0].flags = CCW_FLAG_SLI; return -ENOMEM;
ret = set_normalized_cda (cdev->private->iccws, (*buffer)); memset(rdc_ccw, 0, sizeof(struct ccw1));
if (ret != 0) rdc_ccw->cmd_code = CCW_CMD_RDC;
rdc_ccw->count = length;
rdc_ccw->flags = CCW_FLAG_SLI;
ret = set_normalized_cda (rdc_ccw, (*buffer));
if (ret != 0) {
kfree(rdc_ccw);
return ret; return ret;
}
spin_lock_irqsave(&sch->lock, flags); spin_lock_irqsave(&sch->lock, flags);
/* Save interrupt handler. */ /* Save interrupt handler. */
handler = cdev->handler; handler = cdev->handler;
/* Temporarily install own handler. */ /* Temporarily install own handler. */
cdev->handler = ccw_device_wake_up; cdev->handler = ccw_device_wake_up;
for (retry = 5; retry > 0; retry--) { if (cdev->private->state != DEV_STATE_ONLINE)
/* 0x00524443 == ebcdic "RDC" */ ret = -ENODEV;
ret = cio_start (sch, cdev->private->iccws, 0x00524443, 0); else if (sch->schib.scsw.actl != 0 || cdev->private->flags.doverify)
if (ret == -ENODEV) ret = -EBUSY;
break; else
if (ret == 0) { /* 0x00D9C4C3 == ebcdic "RDC" */
/* Wait for end of request. */ ret = __ccw_device_retry_loop(cdev, rdc_ccw, 0x00D9C4C3, flags);
spin_unlock_irqrestore(&sch->lock, flags);
wait_event(cdev->private->wait_q,
sch->schib.scsw.actl == 0);
spin_lock_irqsave(&sch->lock, flags);
/* Check at least for channel end / device end */
if ((sch->schib.scsw.dstat !=
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
(sch->schib.scsw.cstat != 0)) {
ret = -EIO;
continue;
}
break;
}
}
/* Restore interrupt handler. */ /* Restore interrupt handler. */
cdev->handler = handler; cdev->handler = handler;
spin_unlock_irqrestore(&sch->lock, flags); spin_unlock_irqrestore(&sch->lock, flags);
clear_normalized_cda (cdev->private->iccws); clear_normalized_cda (rdc_ccw);
kfree(rdc_ccw);
return ret; return ret;
} }
...@@ -305,15 +362,11 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length) ...@@ -305,15 +362,11 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length)
struct ciw *ciw; struct ciw *ciw;
unsigned long flags; unsigned long flags;
char *rcd_buf; char *rcd_buf;
int retry;
int ret; int ret;
struct ccw1 *rcd_ccw;
if (!cdev) if (!cdev)
return -ENODEV; return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE)
return -EINVAL;
if (cdev->private->flags.esid == 0)
return -EOPNOTSUPP;
if (!buffer || !length) if (!buffer || !length)
return -EINVAL; return -EINVAL;
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
...@@ -328,40 +381,34 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length) ...@@ -328,40 +381,34 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length)
if (!ciw || ciw->cmd == 0) if (!ciw || ciw->cmd == 0)
return -EOPNOTSUPP; return -EOPNOTSUPP;
rcd_ccw = kmalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
if (!rcd_ccw)
return -ENOMEM;
memset(rcd_ccw, 0, sizeof(struct ccw1));
rcd_buf = kmalloc(ciw->count, GFP_KERNEL | GFP_DMA); rcd_buf = kmalloc(ciw->count, GFP_KERNEL | GFP_DMA);
if (!rcd_buf) if (!rcd_buf) {
kfree(rcd_ccw);
return -ENOMEM; return -ENOMEM;
}
memset (rcd_buf, 0, ciw->count); memset (rcd_buf, 0, ciw->count);
cdev->private->iccws[0].cmd_code = ciw->cmd; rcd_ccw->cmd_code = ciw->cmd;
cdev->private->iccws[0].cda = (__u32) __pa (rcd_buf); rcd_ccw->cda = (__u32) __pa (rcd_buf);
cdev->private->iccws[0].count = ciw->count; rcd_ccw->count = ciw->count;
cdev->private->iccws[0].flags = CCW_FLAG_SLI; rcd_ccw->flags = CCW_FLAG_SLI;
spin_lock_irqsave(&sch->lock, flags); spin_lock_irqsave(&sch->lock, flags);
/* Save interrupt handler. */ /* Save interrupt handler. */
handler = cdev->handler; handler = cdev->handler;
/* Temporarily install own handler. */ /* Temporarily install own handler. */
cdev->handler = ccw_device_wake_up; cdev->handler = ccw_device_wake_up;
for (ret = 0, retry = 5; retry > 0; retry--) { if (cdev->private->state != DEV_STATE_ONLINE)
/* 0x00524344 == ebcdic "RCD" */ ret = -ENODEV;
ret = cio_start (sch, cdev->private->iccws, 0x00524344, 0); else if (sch->schib.scsw.actl != 0 || cdev->private->flags.doverify)
if (ret == -ENODEV) ret = -EBUSY;
break; else
if (ret) /* 0x00D9C3C4 == ebcdic "RCD" */
continue; ret = __ccw_device_retry_loop(cdev, rcd_ccw, 0x00D9C3C4, flags);
/* Wait for end of request. */
spin_unlock_irqrestore(&sch->lock, flags);
wait_event(cdev->private->wait_q, sch->schib.scsw.actl == 0);
spin_lock_irqsave(&sch->lock, flags);
/* Check at least for channel end / device end */
if ((sch->schib.scsw.dstat !=
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
(sch->schib.scsw.cstat != 0)) {
ret = -EIO;
continue;
}
break;
}
/* Restore interrupt handler. */ /* Restore interrupt handler. */
cdev->handler = handler; cdev->handler = handler;
spin_unlock_irqrestore(&sch->lock, flags); spin_unlock_irqrestore(&sch->lock, flags);
...@@ -377,6 +424,7 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length) ...@@ -377,6 +424,7 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length)
*length = ciw->count; *length = ciw->count;
*buffer = rcd_buf; *buffer = rcd_buf;
} }
kfree(rcd_ccw);
return ret; return ret;
} }
......
...@@ -67,7 +67,8 @@ ccw_device_path_notoper(struct ccw_device *cdev) ...@@ -67,7 +67,8 @@ ccw_device_path_notoper(struct ccw_device *cdev)
sch->schib.pmcw.pnom); sch->schib.pmcw.pnom);
sch->lpm &= ~sch->schib.pmcw.pnom; sch->lpm &= ~sch->schib.pmcw.pnom;
dev_fsm_event(cdev, DEV_EVENT_VERIFY); if (cdev->private->options.pgroup)
cdev->private->flags.doverify = 1;
} }
/* /*
...@@ -92,6 +93,21 @@ ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb) ...@@ -92,6 +93,21 @@ ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb)
memcpy (&cdev->private->irb.ecw, irb->ecw, sizeof (irb->ecw)); memcpy (&cdev->private->irb.ecw, irb->ecw, sizeof (irb->ecw));
} }
/*
* Check if extended status word is valid.
*/
static inline int
ccw_device_accumulate_esw_valid(struct irb *irb)
{
if (irb->scsw.eswf && irb->scsw.stctl == SCSW_STCTL_STATUS_PEND)
return 0;
if (irb->scsw.stctl ==
(SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) &&
!(irb->scsw.actl & SCSW_ACTL_SUSPENDED))
return 0;
return 1;
}
/* /*
* Copy valid bits from the extended status word to device irb. * Copy valid bits from the extended status word to device irb.
*/ */
...@@ -101,12 +117,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb) ...@@ -101,12 +117,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb)
struct irb *cdev_irb; struct irb *cdev_irb;
struct sublog *cdev_sublog, *sublog; struct sublog *cdev_sublog, *sublog;
/* Check if extended status word is valid. */ if (!ccw_device_accumulate_esw_valid(irb))
if (irb->scsw.eswf && irb->scsw.stctl == SCSW_STCTL_STATUS_PEND)
return;
if (irb->scsw.stctl ==
(SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) &&
!(irb->scsw.actl & SCSW_ACTL_SUSPENDED))
return; return;
cdev_irb = &cdev->private->irb; cdev_irb = &cdev->private->irb;
...@@ -169,6 +180,8 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb) ...@@ -169,6 +180,8 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb)
cdev_irb->esw.esw0.erw.auth = irb->esw.esw0.erw.auth; cdev_irb->esw.esw0.erw.auth = irb->esw.esw0.erw.auth;
/* Copy path verification required flag. */ /* Copy path verification required flag. */
cdev_irb->esw.esw0.erw.pvrf = irb->esw.esw0.erw.pvrf; cdev_irb->esw.esw0.erw.pvrf = irb->esw.esw0.erw.pvrf;
if (irb->esw.esw0.erw.pvrf && cdev->private->options.pgroup)
cdev->private->flags.doverify = 1;
/* Copy concurrent sense bit. */ /* Copy concurrent sense bit. */
cdev_irb->esw.esw0.erw.cons = irb->esw.esw0.erw.cons; cdev_irb->esw.esw0.erw.cons = irb->esw.esw0.erw.cons;
if (irb->esw.esw0.erw.cons) if (irb->esw.esw0.erw.cons)
...@@ -339,6 +352,10 @@ ccw_device_accumulate_basic_sense(struct ccw_device *cdev, struct irb *irb) ...@@ -339,6 +352,10 @@ ccw_device_accumulate_basic_sense(struct ccw_device *cdev, struct irb *irb)
cdev->private->irb.esw.esw0.erw.cons = 1; cdev->private->irb.esw.esw0.erw.cons = 1;
cdev->private->flags.dosense = 0; cdev->private->flags.dosense = 0;
} }
/* Check if path verification is required. */
if (ccw_device_accumulate_esw_valid(irb) &&
irb->esw.esw0.erw.pvrf && cdev->private->options.pgroup)
cdev->private->flags.doverify = 1;
} }
/* /*
......
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
#include "ioasm.h" #include "ioasm.h"
#include "chsc.h" #include "chsc.h"
#define VERSION_QDIO_C "$Revision: 1.55 $" #define VERSION_QDIO_C "$Revision: 1.61 $"
/****************** MODULE PARAMETER VARIABLES ********************/ /****************** MODULE PARAMETER VARIABLES ********************/
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>"); MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
...@@ -117,60 +117,6 @@ qdio_get_micros(void) ...@@ -117,60 +117,6 @@ qdio_get_micros(void)
asm volatile ("STCK %0" : "=m" (time)); asm volatile ("STCK %0" : "=m" (time));
return time>>12; /* time>>12 is microseconds*/ return time>>12; /* time>>12 is microseconds*/
} }
static inline unsigned long
qdio_get_millis(void)
{
return (unsigned long)(qdio_get_micros()>>12);
}
static __inline__ int
atomic_return_add (int i, atomic_t *v)
{
int old, new;
__CS_LOOP(old, new, v, i, "ar");
return old;
}
static void
qdio_wait_nonbusy(unsigned int timeout)
{
unsigned int start;
char dbf_text[15];
sprintf(dbf_text,"wtnb%4x",timeout);
QDIO_DBF_TEXT3(0,trace,dbf_text);
start=qdio_get_millis();
for (;;) {
set_task_state(current,TASK_INTERRUPTIBLE);
if (qdio_get_millis()-start>timeout) {
goto out;
}
schedule_timeout(((start+timeout-qdio_get_millis())>>10)*HZ);
}
out:
set_task_state(current,TASK_RUNNING);
}
static int
qdio_wait_for_no_use_count(atomic_t *use_count)
{
unsigned long start;
QDIO_DBF_TEXT3(0,trace,"wtnousec");
start=qdio_get_millis();
for (;;) {
if (qdio_get_millis()-start>QDIO_NO_USE_COUNT_TIMEOUT) {
QDIO_DBF_TEXT1(1,trace,"WTNOUSTO");
return -ETIME;
}
if (!atomic_read(use_count)) {
QDIO_DBF_TEXT3(0,trace,"wtnoused");
return 0;
}
qdio_wait_nonbusy(QDIO_NO_USE_COUNT_TIME);
}
}
/* /*
* unfortunately, we can't just xchg the values; in do_QDIO we want to reserve * unfortunately, we can't just xchg the values; in do_QDIO we want to reserve
...@@ -181,7 +127,7 @@ qdio_wait_for_no_use_count(atomic_t *use_count) ...@@ -181,7 +127,7 @@ qdio_wait_for_no_use_count(atomic_t *use_count)
static inline int static inline int
qdio_reserve_q(struct qdio_q *q) qdio_reserve_q(struct qdio_q *q)
{ {
return atomic_return_add(1,&q->use_count); return atomic_add_return(1,&q->use_count) - 1;
} }
static inline void static inline void
...@@ -1221,21 +1167,18 @@ static void ...@@ -1221,21 +1167,18 @@ static void
qdio_release_irq_memory(struct qdio_irq *irq_ptr) qdio_release_irq_memory(struct qdio_irq *irq_ptr)
{ {
int i; int i;
int available;
for (i=0;i<QDIO_MAX_QUEUES_PER_IRQ;i++) { for (i=0;i<QDIO_MAX_QUEUES_PER_IRQ;i++) {
if (!irq_ptr->input_qs[i]) if (!irq_ptr->input_qs[i])
goto next; goto next;
available=0;
if (irq_ptr->input_qs[i]->slib) if (irq_ptr->input_qs[i]->slib)
kfree(irq_ptr->input_qs[i]->slib); kfree(irq_ptr->input_qs[i]->slib);
kfree(irq_ptr->input_qs[i]); kfree(irq_ptr->input_qs[i]);
next: next:
if (!irq_ptr->output_qs[i]) if (!irq_ptr->output_qs[i])
continue; continue;
available=0;
if (irq_ptr->output_qs[i]->slib) if (irq_ptr->output_qs[i]->slib)
kfree(irq_ptr->output_qs[i]->slib); kfree(irq_ptr->output_qs[i]->slib);
...@@ -1285,20 +1228,12 @@ qdio_set_impl_params(struct qdio_irq *irq_ptr, ...@@ -1285,20 +1228,12 @@ qdio_set_impl_params(struct qdio_irq *irq_ptr,
} }
static int static int
qdio_alloc_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, qdio_alloc_qs(struct qdio_irq *irq_ptr,
int no_input_qs, int no_output_qs, int no_input_qs, int no_output_qs)
qdio_handler_t *input_handler,
qdio_handler_t *output_handler,
unsigned long int_parm,int q_format,
unsigned long flags,
void **inbound_sbals_array,
void **outbound_sbals_array)
{ {
int i;
struct qdio_q *q; struct qdio_q *q;
int i,j,result=0; int result=-ENOMEM;
char dbf_text[20]; /* see qdio_initialize */
void *ptr;
int available;
for (i=0;i<no_input_qs;i++) { for (i=0;i<no_input_qs;i++) {
q=kmalloc(sizeof(struct qdio_q),GFP_KERNEL); q=kmalloc(sizeof(struct qdio_q),GFP_KERNEL);
...@@ -1307,17 +1242,67 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, ...@@ -1307,17 +1242,67 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev,
QDIO_PRINT_ERR("kmalloc of q failed!\n"); QDIO_PRINT_ERR("kmalloc of q failed!\n");
goto out; goto out;
} }
memset(q,0,sizeof(struct qdio_q)); memset(q,0,sizeof(struct qdio_q));
sprintf(dbf_text,"in-q%4x",i); q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL);
QDIO_DBF_TEXT0(0,setup,dbf_text); if (!q->slib) {
QDIO_DBF_HEX0(0,setup,&q,sizeof(void*)); QDIO_PRINT_ERR("kmalloc of slib failed!\n");
goto out;
}
irq_ptr->input_qs[i]=q;
}
for (i=0;i<no_output_qs;i++) {
q=kmalloc(sizeof(struct qdio_q),GFP_KERNEL);
if (!q) {
goto out;
}
memset(q,0,sizeof(struct qdio_q));
q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL); q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL);
if (!q->slib) { if (!q->slib) {
QDIO_PRINT_ERR("kmalloc of slib failed!\n"); QDIO_PRINT_ERR("kmalloc of slib failed!\n");
goto out; goto out;
} }
irq_ptr->output_qs[i]=q;
}
result=0;
out:
return result;
}
static void
qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev,
int no_input_qs, int no_output_qs,
qdio_handler_t *input_handler,
qdio_handler_t *output_handler,
unsigned long int_parm,int q_format,
unsigned long flags,
void **inbound_sbals_array,
void **outbound_sbals_array)
{
struct qdio_q *q;
int i,j;
char dbf_text[20]; /* see qdio_initialize */
void *ptr;
int available;
sprintf(dbf_text,"qfqs%4x",cdev->private->irq);
QDIO_DBF_TEXT0(0,setup,dbf_text);
for (i=0;i<no_input_qs;i++) {
q=irq_ptr->input_qs[i];
memset(q,0,((char*)&q->slib)-((char*)q));
sprintf(dbf_text,"in-q%4x",i);
QDIO_DBF_TEXT0(0,setup,dbf_text);
QDIO_DBF_HEX0(0,setup,&q,sizeof(void*));
memset(q->slib,0,PAGE_SIZE); memset(q->slib,0,PAGE_SIZE);
q->sl=(struct sl*)(((char*)q->slib)+PAGE_SIZE/2); q->sl=(struct sl*)(((char*)q->slib)+PAGE_SIZE/2);
...@@ -1328,7 +1313,6 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, ...@@ -1328,7 +1313,6 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev,
q->queue_type=q_format; q->queue_type=q_format;
q->int_parm=int_parm; q->int_parm=int_parm;
irq_ptr->input_qs[i]=q;
q->irq=irq_ptr->irq; q->irq=irq_ptr->irq;
q->irq_ptr = irq_ptr; q->irq_ptr = irq_ptr;
q->cdev = cdev; q->cdev = cdev;
...@@ -1380,22 +1364,13 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, ...@@ -1380,22 +1364,13 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev,
} }
for (i=0;i<no_output_qs;i++) { for (i=0;i<no_output_qs;i++) {
q=kmalloc(sizeof(struct qdio_q),GFP_KERNEL); q=irq_ptr->output_qs[i];
memset(q,0,((char*)&q->slib)-((char*)q));
if (!q) {
goto out;
}
memset(q,0,sizeof(struct qdio_q));
sprintf(dbf_text,"outq%4x",i); sprintf(dbf_text,"outq%4x",i);
QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_DBF_TEXT0(0,setup,dbf_text);
QDIO_DBF_HEX0(0,setup,&q,sizeof(void*)); QDIO_DBF_HEX0(0,setup,&q,sizeof(void*));
q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL);
if (!q->slib) {
QDIO_PRINT_ERR("kmalloc of slib failed!\n");
goto out;
}
memset(q->slib,0,PAGE_SIZE); memset(q->slib,0,PAGE_SIZE);
q->sl=(struct sl*)(((char*)q->slib)+PAGE_SIZE/2); q->sl=(struct sl*)(((char*)q->slib)+PAGE_SIZE/2);
...@@ -1406,7 +1381,6 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, ...@@ -1406,7 +1381,6 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev,
q->queue_type=q_format; q->queue_type=q_format;
q->int_parm=int_parm; q->int_parm=int_parm;
irq_ptr->output_qs[i]=q;
q->is_input_q=0; q->is_input_q=0;
q->irq=irq_ptr->irq; q->irq=irq_ptr->irq;
q->cdev = cdev; q->cdev = cdev;
...@@ -1446,10 +1420,6 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, ...@@ -1446,10 +1420,6 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev,
/* q->sbal[j]->element[1].sbalf.i1.key=QDIO_STORAGE_KEY;*/ /* q->sbal[j]->element[1].sbalf.i1.key=QDIO_STORAGE_KEY;*/
} }
} }
result=1;
out:
return result;
} }
static void static void
...@@ -1645,7 +1615,6 @@ qdio_timeout_handler(struct ccw_device *cdev) ...@@ -1645,7 +1615,6 @@ qdio_timeout_handler(struct ccw_device *cdev)
} }
ccw_device_set_timeout(cdev, 0); ccw_device_set_timeout(cdev, 0);
wake_up(&cdev->private->wait_q); wake_up(&cdev->private->wait_q);
} }
static void static void
...@@ -1703,7 +1672,6 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) ...@@ -1703,7 +1672,6 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
switch (irq_ptr->state) { switch (irq_ptr->state) {
case QDIO_IRQ_STATE_INACTIVE: case QDIO_IRQ_STATE_INACTIVE:
/* FIXME: defer this past interrupt time */
qdio_establish_handle_irq(cdev, cstat, dstat); qdio_establish_handle_irq(cdev, cstat, dstat);
break; break;
...@@ -2128,8 +2096,12 @@ qdio_shutdown(struct ccw_device *cdev, int how) ...@@ -2128,8 +2096,12 @@ qdio_shutdown(struct ccw_device *cdev, int how)
for (i=0;i<irq_ptr->no_input_qs;i++) { for (i=0;i<irq_ptr->no_input_qs;i++) {
qdio_unmark_q(irq_ptr->input_qs[i]); qdio_unmark_q(irq_ptr->input_qs[i]);
tasklet_kill(&irq_ptr->input_qs[i]->tasklet); tasklet_kill(&irq_ptr->input_qs[i]->tasklet);
if (qdio_wait_for_no_use_count(&irq_ptr->input_qs[i]-> wait_event_interruptible_timeout(cdev->private->wait_q,
use_count)) !atomic_read(&irq_ptr->
input_qs[i]->
use_count),
QDIO_NO_USE_COUNT_TIMEOUT*HZ);
if (atomic_read(&irq_ptr->input_qs[i]->use_count))
/* /*
* FIXME: * FIXME:
* nobody cares about such retval, * nobody cares about such retval,
...@@ -2142,8 +2114,12 @@ qdio_shutdown(struct ccw_device *cdev, int how) ...@@ -2142,8 +2114,12 @@ qdio_shutdown(struct ccw_device *cdev, int how)
for (i=0;i<irq_ptr->no_output_qs;i++) { for (i=0;i<irq_ptr->no_output_qs;i++) {
tasklet_kill(&irq_ptr->output_qs[i]->tasklet); tasklet_kill(&irq_ptr->output_qs[i]->tasklet);
if (qdio_wait_for_no_use_count(&irq_ptr->output_qs[i]-> wait_event_interruptible_timeout(cdev->private->wait_q,
use_count)) !atomic_read(&irq_ptr->
output_qs[i]->
use_count),
QDIO_NO_USE_COUNT_TIMEOUT*HZ);
if (atomic_read(&irq_ptr->output_qs[i]->use_count))
/* /*
* FIXME: * FIXME:
* nobody cares about such retval, * nobody cares about such retval,
...@@ -2176,16 +2152,7 @@ qdio_shutdown(struct ccw_device *cdev, int how) ...@@ -2176,16 +2152,7 @@ qdio_shutdown(struct ccw_device *cdev, int how)
wait_event(cdev->private->wait_q, wait_event(cdev->private->wait_q,
irq_ptr->state == QDIO_IRQ_STATE_INACTIVE || irq_ptr->state == QDIO_IRQ_STATE_INACTIVE ||
irq_ptr->state == QDIO_IRQ_STATE_ERR); irq_ptr->state == QDIO_IRQ_STATE_ERR);
/* Ignore errors. */
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
out:
up(&irq_ptr->setting_up_sema);
return result;
}
static inline void
qdio_cleanup_finish(struct ccw_device *cdev, struct qdio_irq *irq_ptr)
{
if (irq_ptr->is_thinint_irq) { if (irq_ptr->is_thinint_irq) {
qdio_put_indicator((__u32*)irq_ptr->dev_st_chg_ind); qdio_put_indicator((__u32*)irq_ptr->dev_st_chg_ind);
tiqdio_set_subchannel_ind(irq_ptr,1); tiqdio_set_subchannel_ind(irq_ptr,1);
...@@ -2196,8 +2163,12 @@ qdio_cleanup_finish(struct ccw_device *cdev, struct qdio_irq *irq_ptr) ...@@ -2196,8 +2163,12 @@ qdio_cleanup_finish(struct ccw_device *cdev, struct qdio_irq *irq_ptr)
if ((void*)cdev->handler == (void*)qdio_handler) if ((void*)cdev->handler == (void*)qdio_handler)
cdev->handler=irq_ptr->original_int_handler; cdev->handler=irq_ptr->original_int_handler;
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE); /* Ignore errors. */
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
ccw_device_set_timeout(cdev, 0);
out:
up(&irq_ptr->setting_up_sema);
return result;
} }
int int
...@@ -2216,10 +2187,6 @@ qdio_free(struct ccw_device *cdev) ...@@ -2216,10 +2187,6 @@ qdio_free(struct ccw_device *cdev)
QDIO_DBF_TEXT1(0,trace,dbf_text); QDIO_DBF_TEXT1(0,trace,dbf_text);
QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_DBF_TEXT0(0,setup,dbf_text);
if (cdev->private->state != DEV_STATE_ONLINE)
return -EINVAL;
qdio_cleanup_finish(cdev, irq_ptr);
cdev->private->qdio_data = 0; cdev->private->qdio_data = 0;
up(&irq_ptr->setting_up_sema); up(&irq_ptr->setting_up_sema);
...@@ -2234,9 +2201,6 @@ qdio_allocate_do_dbf(struct qdio_initialize *init_data) ...@@ -2234,9 +2201,6 @@ qdio_allocate_do_dbf(struct qdio_initialize *init_data)
{ {
char dbf_text[20]; /* if a printf would print out more than 8 chars */ char dbf_text[20]; /* if a printf would print out more than 8 chars */
sprintf(dbf_text,"qalc%4x",init_data->cdev->private->irq);
QDIO_DBF_TEXT0(0,setup,dbf_text);
QDIO_DBF_TEXT0(0,trace,dbf_text);
sprintf(dbf_text,"qfmt:%x",init_data->q_format); sprintf(dbf_text,"qfmt:%x",init_data->q_format);
QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_DBF_TEXT0(0,setup,dbf_text);
QDIO_DBF_TEXT0(0,setup,init_data->adapter_name); QDIO_DBF_TEXT0(0,setup,init_data->adapter_name);
...@@ -2409,6 +2373,8 @@ qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat) ...@@ -2409,6 +2373,8 @@ qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat)
struct qdio_irq *irq_ptr; struct qdio_irq *irq_ptr;
char dbf_text[15]; char dbf_text[15];
irq_ptr = cdev->private->qdio_data;
sprintf(dbf_text,"qehi%4x",cdev->private->irq); sprintf(dbf_text,"qehi%4x",cdev->private->irq);
QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_DBF_TEXT0(0,setup,dbf_text);
QDIO_DBF_TEXT0(0,trace,dbf_text); QDIO_DBF_TEXT0(0,trace,dbf_text);
...@@ -2418,28 +2384,6 @@ qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat) ...@@ -2418,28 +2384,6 @@ qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat)
return; return;
} }
irq_ptr = cdev->private->qdio_data;
if (MACHINE_IS_VM)
irq_ptr->qdioac=qdio_check_siga_needs(irq_ptr->irq);
else
irq_ptr->qdioac=CHSC_FLAG_SIGA_INPUT_NECESSARY
| CHSC_FLAG_SIGA_OUTPUT_NECESSARY;
sprintf(dbf_text,"qdioac%2x",irq_ptr->qdioac);
QDIO_DBF_TEXT2(0,setup,dbf_text);
sprintf(dbf_text,"qib ac%2x",irq_ptr->qib.ac);
QDIO_DBF_TEXT2(0,setup,dbf_text);
irq_ptr->hydra_gives_outbound_pcis=
irq_ptr->qib.ac&QIB_AC_OUTBOUND_PCI_SUPPORTED;
irq_ptr->sync_done_on_outb_pcis=
irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS;
qdio_initialize_set_siga_flags_input(irq_ptr);
qdio_initialize_set_siga_flags_output(irq_ptr);
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED); qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED);
ccw_device_set_timeout(cdev, 0); ccw_device_set_timeout(cdev, 0);
} }
...@@ -2456,7 +2400,7 @@ qdio_initialize(struct qdio_initialize *init_data) ...@@ -2456,7 +2400,7 @@ qdio_initialize(struct qdio_initialize *init_data)
rc = qdio_allocate(init_data); rc = qdio_allocate(init_data);
if (rc == 0) { if (rc == 0) {
rc = qdio_establish(init_data->cdev); rc = qdio_establish(init_data);
if (rc != 0) if (rc != 0)
qdio_free(init_data->cdev); qdio_free(init_data->cdev);
} }
...@@ -2468,13 +2412,12 @@ qdio_initialize(struct qdio_initialize *init_data) ...@@ -2468,13 +2412,12 @@ qdio_initialize(struct qdio_initialize *init_data)
int int
qdio_allocate(struct qdio_initialize *init_data) qdio_allocate(struct qdio_initialize *init_data)
{ {
int i;
struct qdio_irq *irq_ptr; struct qdio_irq *irq_ptr;
struct ciw *ciw;
int result;
int is_iqdio;
char dbf_text[15]; char dbf_text[15];
sprintf(dbf_text,"qalc%4x",init_data->cdev->private->irq);
QDIO_DBF_TEXT0(0,setup,dbf_text);
QDIO_DBF_TEXT0(0,trace,dbf_text);
if ( (init_data->no_input_qs>QDIO_MAX_QUEUES_PER_IRQ) || if ( (init_data->no_input_qs>QDIO_MAX_QUEUES_PER_IRQ) ||
(init_data->no_output_qs>QDIO_MAX_QUEUES_PER_IRQ) || (init_data->no_output_qs>QDIO_MAX_QUEUES_PER_IRQ) ||
((init_data->no_input_qs) && (!init_data->input_handler)) || ((init_data->no_input_qs) && (!init_data->input_handler)) ||
...@@ -2501,7 +2444,8 @@ qdio_allocate(struct qdio_initialize *init_data) ...@@ -2501,7 +2444,8 @@ qdio_allocate(struct qdio_initialize *init_data)
} }
memset(irq_ptr,0,sizeof(struct qdio_irq)); memset(irq_ptr,0,sizeof(struct qdio_irq));
/* wipes qib.ac, required by ar7063 */
init_MUTEX(&irq_ptr->setting_up_sema);
irq_ptr->qdr=kmalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA); irq_ptr->qdr=kmalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA);
if (!(irq_ptr->qdr)) { if (!(irq_ptr->qdr)) {
...@@ -2510,10 +2454,38 @@ qdio_allocate(struct qdio_initialize *init_data) ...@@ -2510,10 +2454,38 @@ qdio_allocate(struct qdio_initialize *init_data)
QDIO_PRINT_ERR("kmalloc of irq_ptr->qdr failed!\n"); QDIO_PRINT_ERR("kmalloc of irq_ptr->qdr failed!\n");
return -ENOMEM; return -ENOMEM;
} }
memset(irq_ptr->qdr,0,sizeof(struct qdr));
QDIO_DBF_TEXT0(0,setup,"qdr:"); QDIO_DBF_TEXT0(0,setup,"qdr:");
QDIO_DBF_HEX0(0,setup,&irq_ptr->qdr,sizeof(void*)); QDIO_DBF_HEX0(0,setup,&irq_ptr->qdr,sizeof(void*));
if (qdio_alloc_qs(irq_ptr,
init_data->no_input_qs,
init_data->no_output_qs)) {
qdio_release_irq_memory(irq_ptr);
return -ENOMEM;
}
init_data->cdev->private->qdio_data = irq_ptr;
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE);
return 0;
}
int qdio_fill_irq(struct qdio_initialize *init_data)
{
int i;
char dbf_text[15];
struct ciw *ciw;
int is_iqdio;
struct qdio_irq *irq_ptr;
irq_ptr = init_data->cdev->private->qdio_data;
memset(irq_ptr,0,((char*)&irq_ptr->qdr)-((char*)irq_ptr));
/* wipes qib.ac, required by ar7063 */
memset(irq_ptr->qdr,0,sizeof(struct qdr));
irq_ptr->int_parm=init_data->int_parm; irq_ptr->int_parm=init_data->int_parm;
irq_ptr->irq = init_data->cdev->private->irq; irq_ptr->irq = init_data->cdev->private->irq;
...@@ -2548,19 +2520,14 @@ qdio_allocate(struct qdio_initialize *init_data) ...@@ -2548,19 +2520,14 @@ qdio_allocate(struct qdio_initialize *init_data)
irq_ptr->aqueue.cmd=DEFAULT_ACTIVATE_QS_CMD; irq_ptr->aqueue.cmd=DEFAULT_ACTIVATE_QS_CMD;
irq_ptr->aqueue.count=DEFAULT_ACTIVATE_QS_COUNT; irq_ptr->aqueue.count=DEFAULT_ACTIVATE_QS_COUNT;
if (!qdio_alloc_qs(irq_ptr, init_data->cdev, qdio_fill_qs(irq_ptr, init_data->cdev,
init_data->no_input_qs, init_data->no_input_qs,
init_data->no_output_qs, init_data->no_output_qs,
init_data->input_handler, init_data->input_handler,
init_data->output_handler,init_data->int_parm, init_data->output_handler,init_data->int_parm,
init_data->q_format,init_data->flags, init_data->q_format,init_data->flags,
init_data->input_sbal_addr_array, init_data->input_sbal_addr_array,
init_data->output_sbal_addr_array)) { init_data->output_sbal_addr_array);
qdio_release_irq_memory(irq_ptr);
return -ENOMEM;
}
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE);
if (!try_module_get(THIS_MODULE)) { if (!try_module_get(THIS_MODULE)) {
QDIO_PRINT_CRIT("try_module_get() failed!\n"); QDIO_PRINT_CRIT("try_module_get() failed!\n");
...@@ -2568,10 +2535,6 @@ qdio_allocate(struct qdio_initialize *init_data) ...@@ -2568,10 +2535,6 @@ qdio_allocate(struct qdio_initialize *init_data)
return -EINVAL; return -EINVAL;
} }
init_MUTEX_LOCKED(&irq_ptr->setting_up_sema);
init_data->cdev->private->qdio_data = irq_ptr;
qdio_fill_thresholds(irq_ptr,init_data->no_input_qs, qdio_fill_thresholds(irq_ptr,init_data->no_input_qs,
init_data->no_output_qs, init_data->no_output_qs,
init_data->min_input_threshold, init_data->min_input_threshold,
...@@ -2636,31 +2599,21 @@ qdio_allocate(struct qdio_initialize *init_data) ...@@ -2636,31 +2599,21 @@ qdio_allocate(struct qdio_initialize *init_data)
irq_ptr->original_int_handler = init_data->cdev->handler; irq_ptr->original_int_handler = init_data->cdev->handler;
init_data->cdev->handler = qdio_handler; init_data->cdev->handler = qdio_handler;
/* the thinint CHSC stuff */
if (irq_ptr->is_thinint_irq) {
result = tiqdio_set_subchannel_ind(irq_ptr,0);
if (result) {
up(&irq_ptr->setting_up_sema);
qdio_cleanup(init_data->cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
return result;
}
tiqdio_set_delay_target(irq_ptr,TIQDIO_DELAY_TARGET);
}
up(&irq_ptr->setting_up_sema); up(&irq_ptr->setting_up_sema);
return 0; return 0;
} }
int int
qdio_establish(struct ccw_device *cdev) qdio_establish(struct qdio_initialize *init_data)
{ {
struct qdio_irq *irq_ptr; struct qdio_irq *irq_ptr;
unsigned long saveflags; unsigned long saveflags;
int result, result2; int result, result2;
struct ccw_device *cdev;
char dbf_text[20]; char dbf_text[20];
cdev=init_data->cdev;
irq_ptr = cdev->private->qdio_data; irq_ptr = cdev->private->qdio_data;
if (!irq_ptr) if (!irq_ptr)
return -EINVAL; return -EINVAL;
...@@ -2670,6 +2623,20 @@ qdio_establish(struct ccw_device *cdev) ...@@ -2670,6 +2623,20 @@ qdio_establish(struct ccw_device *cdev)
down(&irq_ptr->setting_up_sema); down(&irq_ptr->setting_up_sema);
qdio_fill_irq(init_data);
/* the thinint CHSC stuff */
if (irq_ptr->is_thinint_irq) {
result = tiqdio_set_subchannel_ind(irq_ptr,0);
if (result) {
up(&irq_ptr->setting_up_sema);
qdio_cleanup(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
return result;
}
tiqdio_set_delay_target(irq_ptr,TIQDIO_DELAY_TARGET);
}
sprintf(dbf_text,"qest%4x",cdev->private->irq); sprintf(dbf_text,"qest%4x",cdev->private->irq);
QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_DBF_TEXT0(0,setup,dbf_text);
QDIO_DBF_TEXT0(0,trace,dbf_text); QDIO_DBF_TEXT0(0,trace,dbf_text);
...@@ -2724,6 +2691,26 @@ qdio_establish(struct ccw_device *cdev) ...@@ -2724,6 +2691,26 @@ qdio_establish(struct ccw_device *cdev)
result = -EIO; result = -EIO;
} }
if (MACHINE_IS_VM)
irq_ptr->qdioac=qdio_check_siga_needs(irq_ptr->irq);
else
irq_ptr->qdioac=CHSC_FLAG_SIGA_INPUT_NECESSARY
| CHSC_FLAG_SIGA_OUTPUT_NECESSARY;
sprintf(dbf_text,"qdioac%2x",irq_ptr->qdioac);
QDIO_DBF_TEXT2(0,setup,dbf_text);
sprintf(dbf_text,"qib ac%2x",irq_ptr->qib.ac);
QDIO_DBF_TEXT2(0,setup,dbf_text);
irq_ptr->hydra_gives_outbound_pcis=
irq_ptr->qib.ac&QIB_AC_OUTBOUND_PCI_SUPPORTED;
irq_ptr->sync_done_on_outb_pcis=
irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS;
qdio_initialize_set_siga_flags_input(irq_ptr);
qdio_initialize_set_siga_flags_output(irq_ptr);
up(&irq_ptr->setting_up_sema); up(&irq_ptr->setting_up_sema);
return result; return result;
...@@ -2806,10 +2793,23 @@ qdio_activate(struct ccw_device *cdev, int flags) ...@@ -2806,10 +2793,23 @@ qdio_activate(struct ccw_device *cdev, int flags)
} }
} }
qdio_wait_nonbusy(QDIO_ACTIVATE_TIMEOUT); wait_event_interruptible_timeout(cdev->private->wait_q,
((irq_ptr->state ==
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ACTIVE); QDIO_IRQ_STATE_STOPPED) ||
(irq_ptr->state ==
QDIO_IRQ_STATE_ERR)),
(QDIO_ACTIVATE_TIMEOUT>>10)*HZ);
switch (irq_ptr->state) {
case QDIO_IRQ_STATE_STOPPED:
case QDIO_IRQ_STATE_ERR:
qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
result = -EIO;
break;
default:
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ACTIVE);
result = 0;
}
out: out:
up(&irq_ptr->setting_up_sema); up(&irq_ptr->setting_up_sema);
...@@ -2855,7 +2855,7 @@ do_qdio_handle_inbound(struct qdio_q *q, unsigned int callflags, ...@@ -2855,7 +2855,7 @@ do_qdio_handle_inbound(struct qdio_q *q, unsigned int callflags,
int used_elements; int used_elements;
/* This is the inbound handling of queues */ /* This is the inbound handling of queues */
used_elements=atomic_return_add(count, &q->number_of_buffers_used); used_elements=atomic_add_return(count, &q->number_of_buffers_used) - count;
qdio_do_qdio_fill_input(q,qidx,count,buffers); qdio_do_qdio_fill_input(q,qidx,count,buffers);
...@@ -2897,7 +2897,7 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags, ...@@ -2897,7 +2897,7 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags,
qdio_do_qdio_fill_output(q,qidx,count,buffers); qdio_do_qdio_fill_output(q,qidx,count,buffers);
used_elements=atomic_return_add(count, &q->number_of_buffers_used); used_elements=atomic_add_return(count, &q->number_of_buffers_used) - count;
if (callflags&QDIO_FLAG_DONT_SIGA) { if (callflags&QDIO_FLAG_DONT_SIGA) {
#ifdef QDIO_PERFORMANCE_STATS #ifdef QDIO_PERFORMANCE_STATS
......
#ifndef _CIO_QDIO_H #ifndef _CIO_QDIO_H
#define _CIO_QDIO_H #define _CIO_QDIO_H
#define VERSION_CIO_QDIO_H "$Revision: 1.18 $" #define VERSION_CIO_QDIO_H "$Revision: 1.20 $"
//#define QDIO_DBF_LIKE_HELL //#define QDIO_DBF_LIKE_HELL
...@@ -56,7 +56,6 @@ ...@@ -56,7 +56,6 @@
#define QDIO_STATS_CLASSES 2 #define QDIO_STATS_CLASSES 2
#define QDIO_STATS_COUNT_NEEDED 2*/ #define QDIO_STATS_COUNT_NEEDED 2*/
#define QDIO_NO_USE_COUNT_TIME 10
#define QDIO_NO_USE_COUNT_TIMEOUT 1000 /* wait for 1 sec on each q before #define QDIO_NO_USE_COUNT_TIMEOUT 1000 /* wait for 1 sec on each q before
exiting without having use_count exiting without having use_count
of the queue to 0 */ of the queue to 0 */
...@@ -577,9 +576,6 @@ struct qdio_q { ...@@ -577,9 +576,6 @@ struct qdio_q {
volatile struct qdio_q *list_next; volatile struct qdio_q *list_next;
volatile struct qdio_q *list_prev; volatile struct qdio_q *list_prev;
struct slib *slib; /* a page is allocated under this pointer,
sl points into this page, offset PAGE_SIZE/2
(after slib) */
struct sl *sl; struct sl *sl;
volatile struct sbal *sbal[QDIO_MAX_BUFFERS_PER_Q]; volatile struct sbal *sbal[QDIO_MAX_BUFFERS_PER_Q];
...@@ -605,6 +601,11 @@ struct qdio_q { ...@@ -605,6 +601,11 @@ struct qdio_q {
__u64 last_transfer_time; __u64 last_transfer_time;
} timing; } timing;
unsigned int queue_type; unsigned int queue_type;
/* leave this member at the end. won't be cleared in qdio_fill_qs */
struct slib *slib; /* a page is allocated under this pointer,
sl points into this page, offset PAGE_SIZE/2
(after slib) */
} __attribute__ ((aligned(256))); } __attribute__ ((aligned(256)));
struct qdio_irq { struct qdio_irq {
...@@ -619,20 +620,14 @@ struct qdio_irq { ...@@ -619,20 +620,14 @@ struct qdio_irq {
unsigned int sync_done_on_outb_pcis; unsigned int sync_done_on_outb_pcis;
enum qdio_irq_states state; enum qdio_irq_states state;
struct semaphore setting_up_sema;
unsigned int no_input_qs; unsigned int no_input_qs;
unsigned int no_output_qs; unsigned int no_output_qs;
unsigned char qdioac; unsigned char qdioac;
struct qdio_q *input_qs[QDIO_MAX_QUEUES_PER_IRQ];
struct qdio_q *output_qs[QDIO_MAX_QUEUES_PER_IRQ];
struct ccw1 ccw; struct ccw1 ccw;
struct qdr *qdr;
struct ciw equeue; struct ciw equeue;
struct ciw aqueue; struct ciw aqueue;
...@@ -641,5 +636,10 @@ struct qdio_irq { ...@@ -641,5 +636,10 @@ struct qdio_irq {
void (*original_int_handler) (struct ccw_device *, void (*original_int_handler) (struct ccw_device *,
unsigned long, struct irb *); unsigned long, struct irb *);
/* leave these four members together at the end. won't be cleared in qdio_fill_irq */
struct qdr *qdr;
struct qdio_q *input_qs[QDIO_MAX_QUEUES_PER_IRQ];
struct qdio_q *output_qs[QDIO_MAX_QUEUES_PER_IRQ];
struct semaphore setting_up_sema;
}; };
#endif #endif
/* /*
* $Id: cu3088.c,v 1.26 2003/01/17 13:46:13 cohuck Exp $ * $Id: cu3088.c,v 1.30 2003/08/28 11:14:11 cohuck Exp $
* *
* CTC / LCS ccw_device driver * CTC / LCS ccw_device driver
* *
...@@ -56,7 +56,6 @@ static struct ccw_device_id cu3088_ids[] = { ...@@ -56,7 +56,6 @@ static struct ccw_device_id cu3088_ids[] = {
static struct ccw_driver cu3088_driver; static struct ccw_driver cu3088_driver;
struct device cu3088_root_dev = { struct device cu3088_root_dev = {
.name = "CU3088 Devices",
.bus_id = "cu3088", .bus_id = "cu3088",
}; };
...@@ -64,7 +63,7 @@ static ssize_t ...@@ -64,7 +63,7 @@ static ssize_t
group_write(struct device_driver *drv, const char *buf, size_t count) group_write(struct device_driver *drv, const char *buf, size_t count)
{ {
const char *start, *end; const char *start, *end;
char bus_ids[2][BUS_ID_SIZE+1], *argv[2]; char bus_ids[2][BUS_ID_SIZE], *argv[2];
int i; int i;
int ret; int ret;
struct ccwgroup_driver *cdrv; struct ccwgroup_driver *cdrv;
...@@ -79,7 +78,7 @@ group_write(struct device_driver *drv, const char *buf, size_t count) ...@@ -79,7 +78,7 @@ group_write(struct device_driver *drv, const char *buf, size_t count)
if (!(end = strchr(start, delim[i]))) if (!(end = strchr(start, delim[i])))
return count; return count;
len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start)+1; len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start + 1);
strlcpy (bus_ids[i], start, len); strlcpy (bus_ids[i], start, len);
argv[i] = bus_ids[i]; argv[i] = bus_ids[i];
start = end + 1; start = end + 1;
......
...@@ -76,7 +76,7 @@ typedef void qdio_handler_t(struct ccw_device *,unsigned int,unsigned int, ...@@ -76,7 +76,7 @@ typedef void qdio_handler_t(struct ccw_device *,unsigned int,unsigned int,
#define QDIO_FLAG_CLEANUP_USING_CLEAR 0x01 #define QDIO_FLAG_CLEANUP_USING_CLEAR 0x01
#define QDIO_FLAG_CLEANUP_USING_HALT 0x02 #define QDIO_FLAG_CLEANUP_USING_HALT 0x02
struct qdio_initialize{ struct qdio_initialize {
struct ccw_device *cdev; struct ccw_device *cdev;
unsigned char q_format; unsigned char q_format;
unsigned char adapter_name[8]; unsigned char adapter_name[8];
...@@ -99,9 +99,10 @@ struct qdio_initialize{ ...@@ -99,9 +99,10 @@ struct qdio_initialize{
void **input_sbal_addr_array; /* addr of n*128 void ptrs */ void **input_sbal_addr_array; /* addr of n*128 void ptrs */
void **output_sbal_addr_array; /* addr of n*128 void ptrs */ void **output_sbal_addr_array; /* addr of n*128 void ptrs */
}; };
extern int qdio_initialize(struct qdio_initialize *init_data); extern int qdio_initialize(struct qdio_initialize *init_data);
extern int qdio_allocate(struct qdio_initialize *init_data); extern int qdio_allocate(struct qdio_initialize *init_data);
extern int qdio_establish(struct ccw_device *); extern int qdio_establish(struct qdio_initialize *init_data);
extern int qdio_activate(struct ccw_device *,int flags); extern int qdio_activate(struct ccw_device *,int flags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment