Skip to content

Commit

Permalink
[S390] cio: provide functions for fcx enabled I/O
Browse files Browse the repository at this point in the history
Provide functions for assembling and starting fcx enabled I/O request
blocks.

Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
  • Loading branch information
Peter Oberparleiter authored and Heiko Carstens committed Jul 14, 2008
1 parent 23d805b commit 83262d6
Show file tree
Hide file tree
Showing 11 changed files with 939 additions and 38 deletions.
2 changes: 1 addition & 1 deletion drivers/s390/cio/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Makefile for the S/390 common i/o drivers
#

obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o scsw.o
obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o scsw.o fcx.o
ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o
Expand Down
81 changes: 70 additions & 11 deletions drivers/s390/cio/cio.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <asm/chpid.h>
#include <asm/airq.h>
#include <asm/cpu.h>
#include <asm/fcx.h>
#include "cio.h"
#include "css.h"
#include "chsc.h"
Expand Down Expand Up @@ -167,30 +168,30 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
{
char dbf_txt[15];
int ccode;
struct orb *orb;
union orb *orb;

CIO_TRACE_EVENT(4, "stIO");
CIO_TRACE_EVENT(4, sch->dev.bus_id);

orb = &to_io_private(sch)->orb;
/* sch is always under 2G. */
orb->intparm = (u32)(addr_t)sch;
orb->fmt = 1;
orb->cmd.intparm = (u32)(addr_t)sch;
orb->cmd.fmt = 1;

orb->pfch = sch->options.prefetch == 0;
orb->spnd = sch->options.suspend;
orb->ssic = sch->options.suspend && sch->options.inter;
orb->lpm = (lpm != 0) ? lpm : sch->lpm;
orb->cmd.pfch = sch->options.prefetch == 0;
orb->cmd.spnd = sch->options.suspend;
orb->cmd.ssic = sch->options.suspend && sch->options.inter;
orb->cmd.lpm = (lpm != 0) ? lpm : sch->lpm;
#ifdef CONFIG_64BIT
/*
* for 64 bit we always support 64 bit IDAWs with 4k page size only
*/
orb->c64 = 1;
orb->i2k = 0;
orb->cmd.c64 = 1;
orb->cmd.i2k = 0;
#endif
orb->key = key >> 4;
orb->cmd.key = key >> 4;
/* issue "Start Subchannel" */
orb->cpa = (__u32) __pa(cpa);
orb->cmd.cpa = (__u32) __pa(cpa);
ccode = ssch(sch->schid, orb);

/* process condition code */
Expand Down Expand Up @@ -1067,3 +1068,61 @@ int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
iplinfo->is_qdio = schib.pmcw.qf;
return 0;
}

/**
* cio_tm_start_key - perform start function
* @sch: subchannel on which to perform the start function
* @tcw: transport-command word to be started
* @lpm: mask of paths to use
* @key: storage key to use for storage access
*
* Start the tcw on the given subchannel. Return zero on success, non-zero
* otherwise.
*/
int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key)
{
int cc;
union orb *orb = &to_io_private(sch)->orb;

memset(orb, 0, sizeof(union orb));
orb->tm.intparm = (u32) (addr_t) sch;
orb->tm.key = key >> 4;
orb->tm.b = 1;
orb->tm.lpm = lpm ? lpm : sch->lpm;
orb->tm.tcw = (u32) (addr_t) tcw;
cc = ssch(sch->schid, orb);
switch (cc) {
case 0:
return 0;
case 1:
case 2:
return -EBUSY;
default:
return cio_start_handle_notoper(sch, lpm);
}
}

/**
* cio_tm_intrg - perform interrogate function
* @sch - subchannel on which to perform the interrogate function
*
* If the specified subchannel is running in transport-mode, perform the
* interrogate function. Return zero on success, non-zero otherwie.
*/
int cio_tm_intrg(struct subchannel *sch)
{
int cc;

if (!to_io_private(sch)->orb.tm.b)
return -EINVAL;
cc = xsch(sch->schid);
switch (cc) {
case 0:
case 2:
return 0;
case 1:
return -EBUSY;
default:
return -ENODEV;
}
}
5 changes: 5 additions & 0 deletions drivers/s390/cio/cio.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <asm/chpid.h>
#include <asm/cio.h>
#include <asm/fcx.h>
#include "chsc.h"
#include "schid.h"

Expand Down Expand Up @@ -100,6 +102,9 @@ extern int cio_set_options (struct subchannel *, int);
extern int cio_get_options (struct subchannel *);
extern int cio_modify (struct subchannel *);

int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
int cio_tm_intrg(struct subchannel *sch);

int cio_create_sch_lock(struct subchannel *);
void do_adapter_IO(void);
void do_IRQ(struct pt_regs *);
Expand Down
49 changes: 33 additions & 16 deletions drivers/s390/cio/device_fsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,43 @@ static void ccw_timeout_log(struct ccw_device *cdev)
struct schib schib;
struct subchannel *sch;
struct io_subchannel_private *private;
union orb *orb;
int cc;

sch = to_subchannel(cdev->dev.parent);
private = to_io_private(sch);
orb = &private->orb;
cc = stsch(sch->schid, &schib);

printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, "
"device information:\n", get_clock());
printk(KERN_WARNING "cio: orb:\n");
print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
&private->orb, sizeof(private->orb), 0);
orb, sizeof(*orb), 0);
printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id);
printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id);
printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, "
"vpm: %02x\n", sch->lpm, sch->opm, sch->vpm);

if ((void *)(addr_t)private->orb.cpa == &private->sense_ccw ||
(void *)(addr_t)private->orb.cpa == cdev->private->iccws)
printk(KERN_WARNING "cio: last channel program (intern):\n");
else
printk(KERN_WARNING "cio: last channel program:\n");

print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
(void *)(addr_t)private->orb.cpa,
sizeof(struct ccw1), 0);
if (orb->tm.b) {
printk(KERN_WARNING "cio: orb indicates transport mode\n");
printk(KERN_WARNING "cio: last tcw:\n");
print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
(void *)(addr_t)orb->tm.tcw,
sizeof(struct tcw), 0);
} else {
printk(KERN_WARNING "cio: orb indicates command mode\n");
if ((void *)(addr_t)orb->cmd.cpa == &private->sense_ccw ||
(void *)(addr_t)orb->cmd.cpa == cdev->private->iccws)
printk(KERN_WARNING "cio: last channel program "
"(intern):\n");
else
printk(KERN_WARNING "cio: last channel program:\n");

print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
(void *)(addr_t)orb->cmd.cpa,
sizeof(struct ccw1), 0);
}
printk(KERN_WARNING "cio: ccw device state: %d\n",
cdev->private->state);
printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc);
Expand Down Expand Up @@ -135,10 +147,13 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
/* Stage 1: cancel io. */
if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_HALT_PEND) &&
!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
ret = cio_cancel(sch);
if (ret != -EINVAL)
return ret;
/* cancel io unsuccessful. From now on it is asynchronous. */
if (!scsw_is_tm(&sch->schib.scsw)) {
ret = cio_cancel(sch);
if (ret != -EINVAL)
return ret;
}
/* cancel io unsuccessful or not applicable (transport mode).
* Continue with asynchronous instructions. */
cdev->private->iretry = 3; /* 3 halt retries. */
}
if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
Expand Down Expand Up @@ -751,11 +766,13 @@ static void
ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
{
struct irb *irb;
int is_cmd;

irb = (struct irb *) __LC_IRB;
is_cmd = !scsw_is_tm(&irb->scsw);
/* Check for unsolicited interrupt. */
if (!scsw_is_solicited(&irb->scsw)) {
if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
if (is_cmd && (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
!irb->esw.esw0.erw.cons) {
/* Unit check but no sense data. Need basic sense. */
if (ccw_device_do_sense(cdev, irb) != 0)
Expand All @@ -774,7 +791,7 @@ ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
}
/* Accumulate status and find out if a basic sense is needed. */
ccw_device_accumulate_irb(cdev, irb);
if (cdev->private->flags.dosense) {
if (is_cmd && cdev->private->flags.dosense) {
if (ccw_device_do_sense(cdev, irb) == 0) {
cdev->private->state = DEV_STATE_W4SENSE;
}
Expand Down
2 changes: 1 addition & 1 deletion drivers/s390/cio/device_id.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
if (irb->scsw.cmd.cc == 3) {
u8 lpm;

lpm = to_io_private(sch)->orb.lpm;
lpm = to_io_private(sch)->orb.cmd.lpm;
if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x "
"on subchannel 0.%x.%04x is "
Expand Down
117 changes: 117 additions & 0 deletions drivers/s390/cio/device_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <asm/ccwdev.h>
#include <asm/idals.h>
#include <asm/chpid.h>
#include <asm/fcx.h>

#include "cio.h"
#include "cio_debug.h"
Expand Down Expand Up @@ -569,6 +570,122 @@ void ccw_device_get_id(struct ccw_device *cdev, struct ccw_dev_id *dev_id)
}
EXPORT_SYMBOL(ccw_device_get_id);

/**
* ccw_device_tm_start_key - perform start function
* @cdev: ccw device on which to perform the start function
* @tcw: transport-command word to be started
* @intparm: user defined parameter to be passed to the interrupt handler
* @lpm: mask of paths to use
* @key: storage key to use for storage access
*
* Start the tcw on the given ccw device. Return zero on success, non-zero
* otherwise.
*/
int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw,
unsigned long intparm, u8 lpm, u8 key)
{
struct subchannel *sch;
int rc;

sch = to_subchannel(cdev->dev.parent);
if (cdev->private->state != DEV_STATE_ONLINE)
return -EIO;
/* Adjust requested path mask to excluded varied off paths. */
if (lpm) {
lpm &= sch->opm;
if (lpm == 0)
return -EACCES;
}
rc = cio_tm_start_key(sch, tcw, lpm, key);
if (rc == 0)
cdev->private->intparm = intparm;
return rc;
}
EXPORT_SYMBOL(ccw_device_tm_start_key);

/**
* ccw_device_tm_start_timeout_key - perform start function
* @cdev: ccw device on which to perform the start function
* @tcw: transport-command word to be started
* @intparm: user defined parameter to be passed to the interrupt handler
* @lpm: mask of paths to use
* @key: storage key to use for storage access
* @expires: time span in jiffies after which to abort request
*
* Start the tcw on the given ccw device. Return zero on success, non-zero
* otherwise.
*/
int ccw_device_tm_start_timeout_key(struct ccw_device *cdev, struct tcw *tcw,
unsigned long intparm, u8 lpm, u8 key,
int expires)
{
int ret;

ccw_device_set_timeout(cdev, expires);
ret = ccw_device_tm_start_key(cdev, tcw, intparm, lpm, key);
if (ret != 0)
ccw_device_set_timeout(cdev, 0);
return ret;
}
EXPORT_SYMBOL(ccw_device_tm_start_timeout_key);

/**
* ccw_device_tm_start - perform start function
* @cdev: ccw device on which to perform the start function
* @tcw: transport-command word to be started
* @intparm: user defined parameter to be passed to the interrupt handler
* @lpm: mask of paths to use
*
* Start the tcw on the given ccw device. Return zero on success, non-zero
* otherwise.
*/
int ccw_device_tm_start(struct ccw_device *cdev, struct tcw *tcw,
unsigned long intparm, u8 lpm)
{
return ccw_device_tm_start_key(cdev, tcw, intparm, lpm,
PAGE_DEFAULT_KEY);
}
EXPORT_SYMBOL(ccw_device_tm_start);

/**
* ccw_device_tm_start_timeout - perform start function
* @cdev: ccw device on which to perform the start function
* @tcw: transport-command word to be started
* @intparm: user defined parameter to be passed to the interrupt handler
* @lpm: mask of paths to use
* @expires: time span in jiffies after which to abort request
*
* Start the tcw on the given ccw device. Return zero on success, non-zero
* otherwise.
*/
int ccw_device_tm_start_timeout(struct ccw_device *cdev, struct tcw *tcw,
unsigned long intparm, u8 lpm, int expires)
{
return ccw_device_tm_start_timeout_key(cdev, tcw, intparm, lpm,
PAGE_DEFAULT_KEY, expires);
}
EXPORT_SYMBOL(ccw_device_tm_start_timeout);

/**
* ccw_device_tm_intrg - perform interrogate function
* @cdev: ccw device on which to perform the interrogate function
*
* Perform an interrogate function on the given ccw device. Return zero on
* success, non-zero otherwise.
*/
int ccw_device_tm_intrg(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);

if (cdev->private->state != DEV_STATE_ONLINE)
return -EIO;
if (!scsw_is_tm(&sch->schib.scsw) ||
!(scsw_actl(&sch->schib.scsw) | SCSW_ACTL_START_PEND))
return -EINVAL;
return cio_tm_intrg(sch);
}
EXPORT_SYMBOL(ccw_device_tm_intrg);

// FIXME: these have to go:

int
Expand Down
2 changes: 1 addition & 1 deletion drivers/s390/cio/device_pgid.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
if (irb->scsw.cmd.cc == 3) {
u8 lpm;

lpm = to_io_private(sch)->orb.lpm;
lpm = to_io_private(sch)->orb.cmd.lpm;
CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x,"
" lpm %02X, became 'not operational'\n",
cdev->private->dev_id.devno, sch->schid.ssid,
Expand Down
Loading

0 comments on commit 83262d6

Please sign in to comment.