s390x: improve error handling for SSCH and RSCH

Simplify the error handling of the SSCH and RSCH handler avoiding
arbitrary and cryptic error codes being used to tell how the instruction
is supposed to end.  Let the code detecting the condition tell how it's
to be handled in a less ambiguous way.  It's best to handle SSCH and RSCH
in one go as the emulation of the two shares a lot of code.

For passthrough this change isn't pure refactoring, but changes the way
kernel reported EFAULT is handled. After clarifying the kernel interface
we decided that EFAULT shall be mapped to unit exception.  Same goes for
unexpected error codes and absence of required ORB flags.

Signed-off-by: Halil Pasic <pasic@linux.vnet.ibm.com>
Message-Id: <20171017140453.51099-4-pasic@linux.vnet.ibm.com>
Tested-by: Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
[CH: cosmetic changes]
Reviewed-by: Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
master
Halil Pasic 2017-10-17 16:04:49 +02:00 committed by Cornelia Huck
parent e443ef9f21
commit 66dc50f705
6 changed files with 75 additions and 126 deletions

View File

@ -1181,12 +1181,11 @@ static void sch_handle_start_func_virtual(SubchDev *sch)
} }
static int sch_handle_start_func_passthrough(SubchDev *sch) static IOInstEnding sch_handle_start_func_passthrough(SubchDev *sch)
{ {
PMCW *p = &sch->curr_status.pmcw; PMCW *p = &sch->curr_status.pmcw;
SCSW *s = &sch->curr_status.scsw; SCSW *s = &sch->curr_status.scsw;
int ret;
ORB *orb = &sch->orb; ORB *orb = &sch->orb;
if (!(s->ctrl & SCSW_ACTL_SUSP)) { if (!(s->ctrl & SCSW_ACTL_SUSP)) {
@ -1200,31 +1199,12 @@ static int sch_handle_start_func_passthrough(SubchDev *sch)
*/ */
if (!(orb->ctrl0 & ORB_CTRL0_MASK_PFCH) || if (!(orb->ctrl0 & ORB_CTRL0_MASK_PFCH) ||
!(orb->ctrl0 & ORB_CTRL0_MASK_C64)) { !(orb->ctrl0 & ORB_CTRL0_MASK_C64)) {
return -EINVAL; warn_report("vfio-ccw requires PFCH and C64 flags set");
sch_gen_unit_exception(sch);
css_inject_io_interrupt(sch);
return IOINST_CC_EXPECTED;
} }
return s390_ccw_cmd_request(sch);
ret = s390_ccw_cmd_request(orb, s, sch->driver_data);
switch (ret) {
/* Currently we don't update control block and just return the cc code. */
case 0:
break;
case -EBUSY:
break;
case -ENODEV:
break;
case -EACCES:
/* Let's reflect an inaccessible host device by cc 3. */
ret = -ENODEV;
break;
default:
/*
* All other return codes will trigger a program check,
* or set cc to 1.
*/
break;
};
return ret;
} }
/* /*
@ -1233,7 +1213,7 @@ static int sch_handle_start_func_passthrough(SubchDev *sch)
* read/writes) asynchronous later on if we start supporting more than * read/writes) asynchronous later on if we start supporting more than
* our current very simple devices. * our current very simple devices.
*/ */
int do_subchannel_work_virtual(SubchDev *sch) IOInstEnding do_subchannel_work_virtual(SubchDev *sch)
{ {
SCSW *s = &sch->curr_status.scsw; SCSW *s = &sch->curr_status.scsw;
@ -1247,12 +1227,12 @@ int do_subchannel_work_virtual(SubchDev *sch)
sch_handle_start_func_virtual(sch); sch_handle_start_func_virtual(sch);
} }
css_inject_io_interrupt(sch); css_inject_io_interrupt(sch);
return 0; /* inst must succeed if this func is called */
return IOINST_CC_EXPECTED;
} }
int do_subchannel_work_passthrough(SubchDev *sch) IOInstEnding do_subchannel_work_passthrough(SubchDev *sch)
{ {
int ret = 0;
SCSW *s = &sch->curr_status.scsw; SCSW *s = &sch->curr_status.scsw;
if (s->ctrl & SCSW_FCTL_CLEAR_FUNC) { if (s->ctrl & SCSW_FCTL_CLEAR_FUNC) {
@ -1262,16 +1242,15 @@ int do_subchannel_work_passthrough(SubchDev *sch)
/* TODO: Halt handling */ /* TODO: Halt handling */
sch_handle_halt_func(sch); sch_handle_halt_func(sch);
} else if (s->ctrl & SCSW_FCTL_START_FUNC) { } else if (s->ctrl & SCSW_FCTL_START_FUNC) {
ret = sch_handle_start_func_passthrough(sch); return sch_handle_start_func_passthrough(sch);
} }
return IOINST_CC_EXPECTED;
return ret;
} }
static int do_subchannel_work(SubchDev *sch) static IOInstEnding do_subchannel_work(SubchDev *sch)
{ {
if (!sch->do_subchannel_work) { if (!sch->do_subchannel_work) {
return -EINVAL; return IOINST_CC_STATUS_PRESENT;
} }
g_assert(sch->curr_status.scsw.ctrl & SCSW_CTRL_MASK_FCTL); g_assert(sch->curr_status.scsw.ctrl & SCSW_CTRL_MASK_FCTL);
return sch->do_subchannel_work(sch); return sch->do_subchannel_work(sch);
@ -1561,27 +1540,23 @@ static void css_update_chnmon(SubchDev *sch)
} }
} }
int css_do_ssch(SubchDev *sch, ORB *orb) IOInstEnding css_do_ssch(SubchDev *sch, ORB *orb)
{ {
SCSW *s = &sch->curr_status.scsw; SCSW *s = &sch->curr_status.scsw;
PMCW *p = &sch->curr_status.pmcw; PMCW *p = &sch->curr_status.pmcw;
int ret;
if (~(p->flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { if (~(p->flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) {
ret = -ENODEV; return IOINST_CC_NOT_OPERATIONAL;
goto out;
} }
if (s->ctrl & SCSW_STCTL_STATUS_PEND) { if (s->ctrl & SCSW_STCTL_STATUS_PEND) {
ret = -EINPROGRESS; return IOINST_CC_STATUS_PRESENT;
goto out;
} }
if (s->ctrl & (SCSW_FCTL_START_FUNC | if (s->ctrl & (SCSW_FCTL_START_FUNC |
SCSW_FCTL_HALT_FUNC | SCSW_FCTL_HALT_FUNC |
SCSW_FCTL_CLEAR_FUNC)) { SCSW_FCTL_CLEAR_FUNC)) {
ret = -EBUSY; return IOINST_CC_BUSY;
goto out;
} }
/* If monitoring is active, update counter. */ /* If monitoring is active, update counter. */
@ -1594,10 +1569,7 @@ int css_do_ssch(SubchDev *sch, ORB *orb)
s->ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND); s->ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND);
s->flags &= ~SCSW_FLAGS_MASK_PNO; s->flags &= ~SCSW_FLAGS_MASK_PNO;
ret = do_subchannel_work(sch); return do_subchannel_work(sch);
out:
return ret;
} }
static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw, static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw,
@ -1844,27 +1816,23 @@ void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
} }
} }
int css_do_rsch(SubchDev *sch) IOInstEnding css_do_rsch(SubchDev *sch)
{ {
SCSW *s = &sch->curr_status.scsw; SCSW *s = &sch->curr_status.scsw;
PMCW *p = &sch->curr_status.pmcw; PMCW *p = &sch->curr_status.pmcw;
int ret;
if (~(p->flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { if (~(p->flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) {
ret = -ENODEV; return IOINST_CC_NOT_OPERATIONAL;
goto out;
} }
if (s->ctrl & SCSW_STCTL_STATUS_PEND) { if (s->ctrl & SCSW_STCTL_STATUS_PEND) {
ret = -EINPROGRESS; return IOINST_CC_STATUS_PRESENT;
goto out;
} }
if (((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || if (((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) ||
(s->ctrl & SCSW_ACTL_RESUME_PEND) || (s->ctrl & SCSW_ACTL_RESUME_PEND) ||
(!(s->ctrl & SCSW_ACTL_SUSP))) { (!(s->ctrl & SCSW_ACTL_SUSP))) {
ret = -EINVAL; return IOINST_CC_BUSY;
goto out;
} }
/* If monitoring is active, update counter. */ /* If monitoring is active, update counter. */
@ -1873,11 +1841,7 @@ int css_do_rsch(SubchDev *sch)
} }
s->ctrl |= SCSW_ACTL_RESUME_PEND; s->ctrl |= SCSW_ACTL_RESUME_PEND;
do_subchannel_work(sch); return do_subchannel_work(sch);
ret = 0;
out:
return ret;
} }
int css_do_rchp(uint8_t cssid, uint8_t chpid) int css_do_rchp(uint8_t cssid, uint8_t chpid)

View File

@ -18,15 +18,14 @@
#include "hw/s390x/css-bridge.h" #include "hw/s390x/css-bridge.h"
#include "hw/s390x/s390-ccw.h" #include "hw/s390x/s390-ccw.h"
int s390_ccw_cmd_request(ORB *orb, SCSW *scsw, void *data) IOInstEnding s390_ccw_cmd_request(SubchDev *sch)
{ {
S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(data); S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data);
if (cdc->handle_request) { if (!cdc->handle_request) {
return cdc->handle_request(orb, scsw, data); return IOINST_CC_STATUS_PRESENT;
} else {
return -ENOSYS;
} }
return cdc->handle_request(sch);
} }
static void s390_ccw_get_dev_info(S390CCWDevice *cdev, static void s390_ccw_get_dev_info(S390CCWDevice *cdev,

View File

@ -47,9 +47,9 @@ struct VFIODeviceOps vfio_ccw_ops = {
.vfio_compute_needs_reset = vfio_ccw_compute_needs_reset, .vfio_compute_needs_reset = vfio_ccw_compute_needs_reset,
}; };
static int vfio_ccw_handle_request(ORB *orb, SCSW *scsw, void *data) static IOInstEnding vfio_ccw_handle_request(SubchDev *sch)
{ {
S390CCWDevice *cdev = data; S390CCWDevice *cdev = sch->driver_data;
VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
struct ccw_io_region *region = vcdev->io_region; struct ccw_io_region *region = vcdev->io_region;
int ret; int ret;
@ -60,8 +60,8 @@ static int vfio_ccw_handle_request(ORB *orb, SCSW *scsw, void *data)
memset(region, 0, sizeof(*region)); memset(region, 0, sizeof(*region));
memcpy(region->orb_area, orb, sizeof(ORB)); memcpy(region->orb_area, &sch->orb, sizeof(ORB));
memcpy(region->scsw_area, scsw, sizeof(SCSW)); memcpy(region->scsw_area, &sch->curr_status.scsw, sizeof(SCSW));
again: again:
ret = pwrite(vcdev->vdev.fd, region, ret = pwrite(vcdev->vdev.fd, region,
@ -71,10 +71,24 @@ again:
goto again; goto again;
} }
error_report("vfio-ccw: wirte I/O region failed with errno=%d", errno); error_report("vfio-ccw: wirte I/O region failed with errno=%d", errno);
return -errno; ret = -errno;
} else {
ret = region->ret_code;
}
switch (ret) {
case 0:
return IOINST_CC_EXPECTED;
case -EBUSY:
return IOINST_CC_BUSY;
case -ENODEV:
case -EACCES:
return IOINST_CC_NOT_OPERATIONAL;
case -EFAULT:
default:
sch_gen_unit_exception(sch);
css_inject_io_interrupt(sch);
return IOINST_CC_EXPECTED;
} }
return region->ret_code;
} }
static void vfio_ccw_reset(DeviceState *dev) static void vfio_ccw_reset(DeviceState *dev)

View File

@ -136,11 +136,22 @@ struct SubchDev {
/* transport-provided data: */ /* transport-provided data: */
int (*ccw_cb) (SubchDev *, CCW1); int (*ccw_cb) (SubchDev *, CCW1);
void (*disable_cb)(SubchDev *); void (*disable_cb)(SubchDev *);
int (*do_subchannel_work) (SubchDev *); IOInstEnding (*do_subchannel_work) (SubchDev *);
SenseId id; SenseId id;
void *driver_data; void *driver_data;
}; };
static inline void sch_gen_unit_exception(SubchDev *sch)
{
sch->curr_status.scsw.ctrl &= ~SCSW_ACTL_START_PEND;
sch->curr_status.scsw.ctrl |= SCSW_STCTL_PRIMARY |
SCSW_STCTL_SECONDARY |
SCSW_STCTL_ALERT |
SCSW_STCTL_STATUS_PEND;
sch->curr_status.scsw.cpa = sch->channel_prog + 8;
sch->curr_status.scsw.dstat = SCSW_DSTAT_UNIT_EXCEP;
}
extern const VMStateDescription vmstate_subch_dev; extern const VMStateDescription vmstate_subch_dev;
/* /*
@ -199,9 +210,9 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); void css_generate_chp_crws(uint8_t cssid, uint8_t chpid);
void css_generate_css_crws(uint8_t cssid); void css_generate_css_crws(uint8_t cssid);
void css_clear_sei_pending(void); void css_clear_sei_pending(void);
int s390_ccw_cmd_request(ORB *orb, SCSW *scsw, void *data); IOInstEnding s390_ccw_cmd_request(SubchDev *sch);
int do_subchannel_work_virtual(SubchDev *sub); IOInstEnding do_subchannel_work_virtual(SubchDev *sub);
int do_subchannel_work_passthrough(SubchDev *sub); IOInstEnding do_subchannel_work_passthrough(SubchDev *sub);
typedef enum { typedef enum {
CSS_IO_ADAPTER_VIRTIO = 0, CSS_IO_ADAPTER_VIRTIO = 0,
@ -232,7 +243,7 @@ int css_do_msch(SubchDev *sch, const SCHIB *schib);
int css_do_xsch(SubchDev *sch); int css_do_xsch(SubchDev *sch);
int css_do_csch(SubchDev *sch); int css_do_csch(SubchDev *sch);
int css_do_hsch(SubchDev *sch); int css_do_hsch(SubchDev *sch);
int css_do_ssch(SubchDev *sch, ORB *orb); IOInstEnding css_do_ssch(SubchDev *sch, ORB *orb);
int css_do_tsch_get_irb(SubchDev *sch, IRB *irb, int *irb_len); int css_do_tsch_get_irb(SubchDev *sch, IRB *irb, int *irb_len);
void css_do_tsch_update_subch(SubchDev *sch); void css_do_tsch_update_subch(SubchDev *sch);
int css_do_stcrw(CRW *crw); int css_do_stcrw(CRW *crw);
@ -243,7 +254,7 @@ int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid,
void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo); void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo);
int css_enable_mcsse(void); int css_enable_mcsse(void);
int css_enable_mss(void); int css_enable_mss(void);
int css_do_rsch(SubchDev *sch); IOInstEnding css_do_rsch(SubchDev *sch);
int css_do_rchp(uint8_t cssid, uint8_t chpid); int css_do_rchp(uint8_t cssid, uint8_t chpid);
bool css_present(uint8_t cssid); bool css_present(uint8_t cssid);
#endif #endif

View File

@ -33,7 +33,7 @@ typedef struct S390CCWDeviceClass {
CCWDeviceClass parent_class; CCWDeviceClass parent_class;
void (*realize)(S390CCWDevice *dev, char *sysfsdev, Error **errp); void (*realize)(S390CCWDevice *dev, char *sysfsdev, Error **errp);
void (*unrealize)(S390CCWDevice *dev, Error **errp); void (*unrealize)(S390CCWDevice *dev, Error **errp);
int (*handle_request) (ORB *, SCSW *, void *); IOInstEnding (*handle_request) (SubchDev *sch);
} S390CCWDeviceClass; } S390CCWDeviceClass;
#endif #endif

View File

@ -218,8 +218,6 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
SubchDev *sch; SubchDev *sch;
ORB orig_orb, orb; ORB orig_orb, orb;
uint64_t addr; uint64_t addr;
int ret = -ENODEV;
int cc;
CPUS390XState *env = &cpu->env; CPUS390XState *env = &cpu->env;
uint8_t ar; uint8_t ar;
@ -239,33 +237,11 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
} }
trace_ioinst_sch_id("ssch", cssid, ssid, schid); trace_ioinst_sch_id("ssch", cssid, ssid, schid);
sch = css_find_subch(m, cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid);
if (sch && css_subch_visible(sch)) { if (!sch || !css_subch_visible(sch)) {
ret = css_do_ssch(sch, &orb); setcc(cpu, 3);
}
switch (ret) {
case -ENODEV:
cc = 3;
break;
case -EBUSY:
cc = 2;
break;
case -EFAULT:
/*
* TODO:
* I'm wondering whether there is something better
* to do for us here (like setting some device or
* subchannel status).
*/
program_interrupt(env, PGM_ADDRESSING, 4);
return; return;
case 0:
cc = 0;
break;
default:
cc = 1;
break;
} }
setcc(cpu, cc); setcc(cpu, css_do_ssch(sch, &orb));
} }
void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb) void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb)
@ -784,8 +760,6 @@ void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1)
{ {
int cssid, ssid, schid, m; int cssid, ssid, schid, m;
SubchDev *sch; SubchDev *sch;
int ret = -ENODEV;
int cc;
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
program_interrupt(&cpu->env, PGM_OPERAND, 4); program_interrupt(&cpu->env, PGM_OPERAND, 4);
@ -793,24 +767,11 @@ void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1)
} }
trace_ioinst_sch_id("rsch", cssid, ssid, schid); trace_ioinst_sch_id("rsch", cssid, ssid, schid);
sch = css_find_subch(m, cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid);
if (sch && css_subch_visible(sch)) { if (!sch || !css_subch_visible(sch)) {
ret = css_do_rsch(sch); setcc(cpu, 3);
return;
} }
switch (ret) { setcc(cpu, css_do_rsch(sch));
case -ENODEV:
cc = 3;
break;
case -EINVAL:
cc = 2;
break;
case 0:
cc = 0;
break;
default:
cc = 1;
break;
}
setcc(cpu, cc);
} }
#define RCHP_REG1_RES(_reg) (_reg & 0x00000000ff00ff00) #define RCHP_REG1_RES(_reg) (_reg & 0x00000000ff00ff00)