Skip to content

Commit

Permalink
s390/cio: add basic protected virtualization support
Browse files Browse the repository at this point in the history
As virtio-ccw devices are channel devices, we need to use the
dma area within the common I/O layer for any communication with
the hypervisor.

Note that we do not need to use that area for control blocks
directly referenced by instructions, e.g. the orb.

It handles neither QDIO in the common code, nor any device type specific
stuff (like channel programs constructed by the DASD driver).

An interesting side effect is that virtio structures are now going to
get allocated in 31 bit addressable storage.

Signed-off-by: Halil Pasic <pasic@linux.ibm.com>
Reviewed-by: Sebastian Ott <sebott@linux.ibm.com>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Reviewed-by: Michael Mueller <mimu@linux.ibm.com>
Tested-by: Michael Mueller <mimu@linux.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
  • Loading branch information
halil-pasic authored and heicarst committed Jun 15, 2019
1 parent bb99332 commit 37db898
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 83 deletions.
4 changes: 4 additions & 0 deletions arch/s390/include/asm/ccwdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ extern int ccw_device_enable_console(struct ccw_device *);
extern void ccw_device_wait_idle(struct ccw_device *);
extern int ccw_device_force_console(struct ccw_device *);

extern void *ccw_device_dma_zalloc(struct ccw_device *cdev, size_t size);
extern void ccw_device_dma_free(struct ccw_device *cdev,
void *cpu_addr, size_t size);

int ccw_device_siosl(struct ccw_device *);

extern void ccw_device_get_schid(struct ccw_device *, struct subchannel_id *);
Expand Down
9 changes: 5 additions & 4 deletions drivers/s390/cio/ccwreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ static void ccwreq_stop(struct ccw_device *cdev, int rc)
return;
req->done = 1;
ccw_device_set_timeout(cdev, 0);
memset(&cdev->private->irb, 0, sizeof(struct irb));
memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));
if (rc && rc != -ENODEV && req->drc)
rc = req->drc;
req->callback(cdev, req->data, rc);
Expand All @@ -86,7 +86,7 @@ static void ccwreq_do(struct ccw_device *cdev)
continue;
}
/* Perform start function. */
memset(&cdev->private->irb, 0, sizeof(struct irb));
memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));
rc = cio_start(sch, cp, (u8) req->mask);
if (rc == 0) {
/* I/O started successfully. */
Expand Down Expand Up @@ -169,7 +169,7 @@ int ccw_request_cancel(struct ccw_device *cdev)
*/
static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb)
{
struct irb *irb = &cdev->private->irb;
struct irb *irb = &cdev->private->dma_area->irb;
struct cmd_scsw *scsw = &irb->scsw.cmd;
enum uc_todo todo;

Expand All @@ -187,7 +187,8 @@ static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb)
CIO_TRACE_EVENT(2, "sensedata");
CIO_HEX_EVENT(2, &cdev->private->dev_id,
sizeof(struct ccw_dev_id));
CIO_HEX_EVENT(2, &cdev->private->irb.ecw, SENSE_MAX_COUNT);
CIO_HEX_EVENT(2, &cdev->private->dma_area->irb.ecw,
SENSE_MAX_COUNT);
/* Check for command reject. */
if (irb->ecw[0] & SNS0_CMD_REJECT)
return IO_REJECTED;
Expand Down
68 changes: 57 additions & 11 deletions drivers/s390/cio/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/timer.h>
#include <linux/kernel_stat.h>
#include <linux/sched/signal.h>
#include <linux/dma-mapping.h>

#include <asm/ccwdev.h>
#include <asm/cio.h>
Expand Down Expand Up @@ -687,6 +688,9 @@ ccw_device_release(struct device *dev)
struct ccw_device *cdev;

cdev = to_ccwdev(dev);
cio_gp_dma_free(cdev->private->dma_pool, cdev->private->dma_area,
sizeof(*cdev->private->dma_area));
cio_gp_dma_destroy(cdev->private->dma_pool, &cdev->dev);
/* Release reference of parent subchannel. */
put_device(cdev->dev.parent);
kfree(cdev->private);
Expand All @@ -696,15 +700,33 @@ ccw_device_release(struct device *dev)
static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch)
{
struct ccw_device *cdev;
struct gen_pool *dma_pool;

cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (cdev) {
cdev->private = kzalloc(sizeof(struct ccw_device_private),
GFP_KERNEL | GFP_DMA);
if (cdev->private)
return cdev;
}
if (!cdev)
goto err_cdev;
cdev->private = kzalloc(sizeof(struct ccw_device_private),
GFP_KERNEL | GFP_DMA);
if (!cdev->private)
goto err_priv;
cdev->dev.coherent_dma_mask = sch->dev.coherent_dma_mask;
cdev->dev.dma_mask = &cdev->dev.coherent_dma_mask;
dma_pool = cio_gp_dma_create(&cdev->dev, 1);
if (!dma_pool)
goto err_dma_pool;
cdev->private->dma_pool = dma_pool;
cdev->private->dma_area = cio_gp_dma_zalloc(dma_pool, &cdev->dev,
sizeof(*cdev->private->dma_area));
if (!cdev->private->dma_area)
goto err_dma_area;
return cdev;
err_dma_area:
cio_gp_dma_destroy(dma_pool, &cdev->dev);
err_dma_pool:
kfree(cdev->private);
err_priv:
kfree(cdev);
err_cdev:
return ERR_PTR(-ENOMEM);
}

Expand Down Expand Up @@ -884,7 +906,7 @@ io_subchannel_recog_done(struct ccw_device *cdev)
wake_up(&ccw_device_init_wq);
break;
case DEV_STATE_OFFLINE:
/*
/*
* We can't register the device in interrupt context so
* we schedule a work item.
*/
Expand Down Expand Up @@ -1062,6 +1084,14 @@ static int io_subchannel_probe(struct subchannel *sch)
if (!io_priv)
goto out_schedule;

io_priv->dma_area = dma_alloc_coherent(&sch->dev,
sizeof(*io_priv->dma_area),
&io_priv->dma_area_dma, GFP_KERNEL);
if (!io_priv->dma_area) {
kfree(io_priv);
goto out_schedule;
}

set_io_private(sch, io_priv);
css_schedule_eval(sch->schid);
return 0;
Expand All @@ -1088,6 +1118,8 @@ static int io_subchannel_remove(struct subchannel *sch)
set_io_private(sch, NULL);
spin_unlock_irq(sch->lock);
out_free:
dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
io_priv->dma_area, io_priv->dma_area_dma);
kfree(io_priv);
sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
return 0;
Expand Down Expand Up @@ -1593,20 +1625,32 @@ struct ccw_device * __init ccw_device_create_console(struct ccw_driver *drv)
return ERR_CAST(sch);

io_priv = kzalloc(sizeof(*io_priv), GFP_KERNEL | GFP_DMA);
if (!io_priv) {
put_device(&sch->dev);
return ERR_PTR(-ENOMEM);
}
if (!io_priv)
goto err_priv;
io_priv->dma_area = dma_alloc_coherent(&sch->dev,
sizeof(*io_priv->dma_area),
&io_priv->dma_area_dma, GFP_KERNEL);
if (!io_priv->dma_area)
goto err_dma_area;
set_io_private(sch, io_priv);
cdev = io_subchannel_create_ccwdev(sch);
if (IS_ERR(cdev)) {
dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
io_priv->dma_area, io_priv->dma_area_dma);
set_io_private(sch, NULL);
put_device(&sch->dev);
kfree(io_priv);
return cdev;
}
cdev->drv = drv;
ccw_device_set_int_class(cdev);
return cdev;

err_dma_area:
kfree(io_priv);
err_priv:
put_device(&sch->dev);
return ERR_PTR(-ENOMEM);
}

void __init ccw_device_destroy_console(struct ccw_device *cdev)
Expand All @@ -1617,6 +1661,8 @@ void __init ccw_device_destroy_console(struct ccw_device *cdev)
set_io_private(sch, NULL);
put_device(&sch->dev);
put_device(&cdev->dev);
dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
io_priv->dma_area, io_priv->dma_area_dma);
kfree(io_priv);
}

Expand Down
49 changes: 29 additions & 20 deletions drivers/s390/cio/device_fsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ static void ccw_timeout_log(struct ccw_device *cdev)
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)
if ((void *)(addr_t)orb->cmd.cpa ==
&private->dma_area->sense_ccw ||
(void *)(addr_t)orb->cmd.cpa ==
cdev->private->dma_area->iccws)
printk(KERN_WARNING "cio: last channel program "
"(intern):\n");
else
Expand Down Expand Up @@ -143,18 +145,22 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
void ccw_device_update_sense_data(struct ccw_device *cdev)
{
memset(&cdev->id, 0, sizeof(cdev->id));
cdev->id.cu_type = cdev->private->senseid.cu_type;
cdev->id.cu_model = cdev->private->senseid.cu_model;
cdev->id.dev_type = cdev->private->senseid.dev_type;
cdev->id.dev_model = cdev->private->senseid.dev_model;
cdev->id.cu_type = cdev->private->dma_area->senseid.cu_type;
cdev->id.cu_model = cdev->private->dma_area->senseid.cu_model;
cdev->id.dev_type = cdev->private->dma_area->senseid.dev_type;
cdev->id.dev_model = cdev->private->dma_area->senseid.dev_model;
}

int ccw_device_test_sense_data(struct ccw_device *cdev)
{
return cdev->id.cu_type == cdev->private->senseid.cu_type &&
cdev->id.cu_model == cdev->private->senseid.cu_model &&
cdev->id.dev_type == cdev->private->senseid.dev_type &&
cdev->id.dev_model == cdev->private->senseid.dev_model;
return cdev->id.cu_type ==
cdev->private->dma_area->senseid.cu_type &&
cdev->id.cu_model ==
cdev->private->dma_area->senseid.cu_model &&
cdev->id.dev_type ==
cdev->private->dma_area->senseid.dev_type &&
cdev->id.dev_model ==
cdev->private->dma_area->senseid.dev_model;
}

/*
Expand Down Expand Up @@ -342,7 +348,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
cio_disable_subchannel(sch);

/* Reset device status. */
memset(&cdev->private->irb, 0, sizeof(struct irb));
memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));

cdev->private->state = state;

Expand Down Expand Up @@ -509,13 +515,14 @@ void ccw_device_verify_done(struct ccw_device *cdev, int err)
ccw_device_done(cdev, DEV_STATE_ONLINE);
/* Deliver fake irb to device driver, if needed. */
if (cdev->private->flags.fake_irb) {
create_fake_irb(&cdev->private->irb,
create_fake_irb(&cdev->private->dma_area->irb,
cdev->private->flags.fake_irb);
cdev->private->flags.fake_irb = 0;
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
&cdev->private->irb);
memset(&cdev->private->irb, 0, sizeof(struct irb));
&cdev->private->dma_area->irb);
memset(&cdev->private->dma_area->irb, 0,
sizeof(struct irb));
}
ccw_device_report_path_events(cdev);
ccw_device_handle_broken_paths(cdev);
Expand Down Expand Up @@ -672,7 +679,8 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)

if (scsw_actl(&sch->schib.scsw) != 0 ||
(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
(scsw_stctl(&cdev->private->irb.scsw) & SCSW_STCTL_STATUS_PEND)) {
(scsw_stctl(&cdev->private->dma_area->irb.scsw) &
SCSW_STCTL_STATUS_PEND)) {
/*
* No final status yet or final status not yet delivered
* to the device driver. Can't do path verification now,
Expand Down Expand Up @@ -719,7 +727,7 @@ static int ccw_device_call_handler(struct ccw_device *cdev)
* - fast notification was requested (primary status)
* - unsolicited interrupts
*/
stctl = scsw_stctl(&cdev->private->irb.scsw);
stctl = scsw_stctl(&cdev->private->dma_area->irb.scsw);
ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
(stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
(stctl == SCSW_STCTL_STATUS_PEND);
Expand All @@ -735,9 +743,9 @@ static int ccw_device_call_handler(struct ccw_device *cdev)

if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
&cdev->private->irb);
&cdev->private->dma_area->irb);

memset(&cdev->private->irb, 0, sizeof(struct irb));
memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));
return 1;
}

Expand All @@ -759,7 +767,8 @@ ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
/* Unit check but no sense data. Need basic sense. */
if (ccw_device_do_sense(cdev, irb) != 0)
goto call_handler_unsol;
memcpy(&cdev->private->irb, irb, sizeof(struct irb));
memcpy(&cdev->private->dma_area->irb, irb,
sizeof(struct irb));
cdev->private->state = DEV_STATE_W4SENSE;
cdev->private->intparm = 0;
return;
Expand Down Expand Up @@ -842,7 +851,7 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
if (scsw_fctl(&irb->scsw) &
(SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
cdev->private->flags.dosense = 0;
memset(&cdev->private->irb, 0, sizeof(struct irb));
memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));
ccw_device_accumulate_irb(cdev, irb);
goto call_handler;
}
Expand Down
20 changes: 11 additions & 9 deletions drivers/s390/cio/device_id.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ static int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag)
static int diag210_get_dev_info(struct ccw_device *cdev)
{
struct ccw_dev_id *dev_id = &cdev->private->dev_id;
struct senseid *senseid = &cdev->private->senseid;
struct senseid *senseid = &cdev->private->dma_area->senseid;
struct diag210 diag_data;
int rc;

Expand Down Expand Up @@ -134,25 +134,27 @@ static int diag210_get_dev_info(struct ccw_device *cdev)
static void snsid_init(struct ccw_device *cdev)
{
cdev->private->flags.esid = 0;
memset(&cdev->private->senseid, 0, sizeof(cdev->private->senseid));
cdev->private->senseid.cu_type = 0xffff;

memset(&cdev->private->dma_area->senseid, 0,
sizeof(cdev->private->dma_area->senseid));
cdev->private->dma_area->senseid.cu_type = 0xffff;
}

/*
* Check for complete SENSE ID data.
*/
static int snsid_check(struct ccw_device *cdev, void *data)
{
struct cmd_scsw *scsw = &cdev->private->irb.scsw.cmd;
struct cmd_scsw *scsw = &cdev->private->dma_area->irb.scsw.cmd;
int len = sizeof(struct senseid) - scsw->count;

/* Check for incomplete SENSE ID data. */
if (len < SENSE_ID_MIN_LEN)
goto out_restart;
if (cdev->private->senseid.cu_type == 0xffff)
if (cdev->private->dma_area->senseid.cu_type == 0xffff)
goto out_restart;
/* Check for incompatible SENSE ID data. */
if (cdev->private->senseid.reserved != 0xff)
if (cdev->private->dma_area->senseid.reserved != 0xff)
return -EOPNOTSUPP;
/* Check for extended-identification information. */
if (len > SENSE_ID_BASIC_LEN)
Expand All @@ -170,7 +172,7 @@ static int snsid_check(struct ccw_device *cdev, void *data)
static void snsid_callback(struct ccw_device *cdev, void *data, int rc)
{
struct ccw_dev_id *id = &cdev->private->dev_id;
struct senseid *senseid = &cdev->private->senseid;
struct senseid *senseid = &cdev->private->dma_area->senseid;
int vm = 0;

if (rc && MACHINE_IS_VM) {
Expand Down Expand Up @@ -200,15 +202,15 @@ void ccw_device_sense_id_start(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct ccw_request *req = &cdev->private->req;
struct ccw1 *cp = cdev->private->iccws;
struct ccw1 *cp = cdev->private->dma_area->iccws;

CIO_TRACE_EVENT(4, "snsid");
CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
/* Data setup. */
snsid_init(cdev);
/* Channel program setup. */
cp->cmd_code = CCW_CMD_SENSE_ID;
cp->cda = (u32) (addr_t) &cdev->private->senseid;
cp->cda = (u32) (addr_t) &cdev->private->dma_area->senseid;
cp->count = sizeof(struct senseid);
cp->flags = CCW_FLAG_SLI;
/* Request setup. */
Expand Down

0 comments on commit 37db898

Please sign in to comment.