From: Alexander Graf <agraf@suse.de>
To: David Gibson <david@gibson.dropbear.id.au>
Cc: paulus@samba.org, qemu-devel@nongnu.org, anton@samba.org
Subject: [Qemu-devel] Re: [PATCH 24/26] Implement PAPR virtual SCSI interface (ibmvscsi)
Date: Wed, 16 Mar 2011 17:41:54 +0100 [thread overview]
Message-ID: <4D80E852.5020109@suse.de> (raw)
In-Reply-To: <1300251423-6715-25-git-send-email-david@gibson.dropbear.id.au>
On 03/16/2011 05:57 AM, David Gibson wrote:
> This patch implements the infrastructure and hypercalls necessary for
> the PAPR specified Virtual SCSI interface. This is the normal method
> for providing (virtual) disks to PAPR partitions.
>
> Signed-off-by: Ben Herrenschmidt<benh@kernel.crashing.org>
> Signed-off-by: David Gibson<dwg@au1.ibm.com>
> ---
> Makefile.target | 2 +-
> hw/ppc-viosrp.h | 216 ++++++++++++
> hw/spapr.c | 10 +-
> hw/spapr_vio.h | 3 +
> hw/spapr_vscsi.c | 960 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> hw/srp.h | 241 ++++++++++++++
> 6 files changed, 1430 insertions(+), 2 deletions(-)
> create mode 100644 hw/ppc-viosrp.h
> create mode 100644 hw/spapr_vscsi.c
> create mode 100644 hw/srp.h
>
> diff --git a/Makefile.target b/Makefile.target
> index ef86d43..49f9e9a 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -233,7 +233,7 @@ obj-ppc-y += ppc_oldworld.o
> obj-ppc-y += ppc_newworld.o
> # IBM pSeries (sPAPR)
> obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
> -obj-ppc-y += xics.o spapr_vty.o spapr_llan.o
> +obj-ppc-y += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
> # PowerPC 4xx boards
> obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
> obj-ppc-y += ppc440.o ppc440_bamboo.o
> diff --git a/hw/ppc-viosrp.h b/hw/ppc-viosrp.h
> new file mode 100644
> index 0000000..9afcf7a
> --- /dev/null
> +++ b/hw/ppc-viosrp.h
> @@ -0,0 +1,216 @@
> +/*****************************************************************************/
> +/* srp.h -- SCSI RDMA Protocol definitions */
> +/* */
> +/* Written By: Colin Devilbis, IBM Corporation */
> +/* */
> +/* Copyright (C) 2003 IBM Corporation */
> +/* */
> +/* This program is free software; you can redistribute it and/or modify */
> +/* it under the terms of the GNU General Public License as published by */
> +/* the Free Software Foundation; either version 2 of the License, or */
> +/* (at your option) any later version. */
> +/* */
> +/* This program is distributed in the hope that it will be useful, */
> +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
> +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
> +/* GNU General Public License for more details. */
> +/* */
> +/* You should have received a copy of the GNU General Public License */
> +/* along with this program; if not, write to the Free Software */
> +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
> +/* */
> +/* */
> +/* This file contains structures and definitions for IBM RPA (RS/6000 */
> +/* platform architecture) implementation of the SRP (SCSI RDMA Protocol) */
> +/* standard. SRP is used on IBM iSeries and pSeries platforms to send SCSI */
> +/* commands between logical partitions. */
> +/* */
> +/* SRP Information Units (IUs) are sent on a "Command/Response Queue" (CRQ) */
> +/* between partitions. The definitions in this file are architected, */
> +/* and cannot be changed without breaking compatibility with other versions */
> +/* of Linux and other operating systems (AIX, OS/400) that talk this protocol*/
> +/* between logical partitions */
> +/*****************************************************************************/
> +#ifndef PPC_VIOSRP_H
> +#define PPC_VIOSRP_H
> +
> +#define SRP_VERSION "16.a"
> +#define SRP_MAX_IU_LEN 256
> +#define SRP_MAX_LOC_LEN 32
> +
> +union srp_iu {
> + struct srp_login_req login_req;
> + struct srp_login_rsp login_rsp;
> + struct srp_login_rej login_rej;
> + struct srp_i_logout i_logout;
> + struct srp_t_logout t_logout;
> + struct srp_tsk_mgmt tsk_mgmt;
> + struct srp_cmd cmd;
> + struct srp_rsp rsp;
> + uint8_t reserved[SRP_MAX_IU_LEN];
> +};
> +
> +enum viosrp_crq_formats {
> + VIOSRP_SRP_FORMAT = 0x01,
> + VIOSRP_MAD_FORMAT = 0x02,
> + VIOSRP_OS400_FORMAT = 0x03,
> + VIOSRP_AIX_FORMAT = 0x04,
> + VIOSRP_LINUX_FORMAT = 0x06,
> + VIOSRP_INLINE_FORMAT = 0x07
> +};
> +
> +enum viosrp_crq_status {
> + VIOSRP_OK = 0x0,
> + VIOSRP_NONRECOVERABLE_ERR = 0x1,
> + VIOSRP_VIOLATES_MAX_XFER = 0x2,
> + VIOSRP_PARTNER_PANIC = 0x3,
> + VIOSRP_DEVICE_BUSY = 0x8,
> + VIOSRP_ADAPTER_FAIL = 0x10,
> + VIOSRP_OK2 = 0x99,
> +};
> +
> +struct viosrp_crq {
> + uint8_t valid; /* used by RPA */
> + uint8_t format; /* SCSI vs out-of-band */
> + uint8_t reserved;
> + uint8_t status; /* non-scsi failure? (e.g. DMA failure) */
> + uint16_t timeout; /* in seconds */
> + uint16_t IU_length; /* in bytes */
> + uint64_t IU_data_ptr; /* the TCE for transferring data */
> +};
> +
> +/* MADs are Management requests above and beyond the IUs defined in the SRP
> + * standard.
> + */
> +enum viosrp_mad_types {
> + VIOSRP_EMPTY_IU_TYPE = 0x01,
> + VIOSRP_ERROR_LOG_TYPE = 0x02,
> + VIOSRP_ADAPTER_INFO_TYPE = 0x03,
> + VIOSRP_HOST_CONFIG_TYPE = 0x04,
> + VIOSRP_CAPABILITIES_TYPE = 0x05,
> + VIOSRP_ENABLE_FAST_FAIL = 0x08,
> +};
> +
> +enum viosrp_mad_status {
> + VIOSRP_MAD_SUCCESS = 0x00,
> + VIOSRP_MAD_NOT_SUPPORTED = 0xF1,
> + VIOSRP_MAD_FAILED = 0xF7,
> +};
> +
> +enum viosrp_capability_type {
> + MIGRATION_CAPABILITIES = 0x01,
> + RESERVATION_CAPABILITIES = 0x02,
> +};
> +
> +enum viosrp_capability_support {
> + SERVER_DOES_NOT_SUPPORTS_CAP = 0x0,
> + SERVER_SUPPORTS_CAP = 0x01,
> + SERVER_CAP_DATA = 0x02,
> +};
> +
> +enum viosrp_reserve_type {
> + CLIENT_RESERVE_SCSI_2 = 0x01,
> +};
> +
> +enum viosrp_capability_flag {
> + CLIENT_MIGRATED = 0x01,
> + CLIENT_RECONNECT = 0x02,
> + CAP_LIST_SUPPORTED = 0x04,
> + CAP_LIST_DATA = 0x08,
> +};
> +
> +/*
> + * Common MAD header
> + */
> +struct mad_common {
> + uint32_t type;
> + uint16_t status;
> + uint16_t length;
> + uint64_t tag;
Is this an in-memory representation? If so, it should be packed, right?
Same goes for the ones below.
> +};
> +
> +/*
> + * All SRP (and MAD) requests normally flow from the
> + * client to the server. There is no way for the server to send
> + * an asynchronous message back to the client. The Empty IU is used
> + * to hang out a meaningless request to the server so that it can respond
> + * asynchrouously with something like a SCSI AER
> + */
> +struct viosrp_empty_iu {
> + struct mad_common common;
> + uint64_t buffer;
> + uint32_t port;
> +};
> +
> +struct viosrp_error_log {
> + struct mad_common common;
> + uint64_t buffer;
> +};
> +
> +struct viosrp_adapter_info {
> + struct mad_common common;
> + uint64_t buffer;
> +};
> +
> +struct viosrp_host_config {
> + struct mad_common common;
> + uint64_t buffer;
> +};
> +
> +struct viosrp_fast_fail {
> + struct mad_common common;
> +};
> +
> +struct viosrp_capabilities {
> + struct mad_common common;
> + uint64_t buffer;
> +};
> +
> +struct mad_capability_common {
> + uint32_t cap_type;
> + uint16_t length;
> + uint16_t server_support;
> +};
> +
> +struct mad_reserve_cap {
> + struct mad_capability_common common;
> + uint32_t type;
> +};
> +
> +struct mad_migration_cap {
> + struct mad_capability_common common;
> + uint32_t ecl;
> +};
> +
> +struct capabilities{
Space, but unused struct
> + uint32_t flags;
> + char name[SRP_MAX_LOC_LEN];
> + char loc[SRP_MAX_LOC_LEN];
> + struct mad_migration_cap migration;
> + struct mad_reserve_cap reserve;
> +};
> +
> +union mad_iu {
> + struct viosrp_empty_iu empty_iu;
> + struct viosrp_error_log error_log;
> + struct viosrp_adapter_info adapter_info;
> + struct viosrp_host_config host_config;
> + struct viosrp_fast_fail fast_fail;
> + struct viosrp_capabilities capabilities;
> +};
> +
> +union viosrp_iu {
> + union srp_iu srp;
> + union mad_iu mad;
> +};
> +
> +struct mad_adapter_info_data {
> + char srp_version[8];
> + char partition_name[96];
> + uint32_t partition_number;
> + uint32_t mad_version;
> + uint32_t os_type;
> + uint32_t port_max_txu[8]; /* per-port maximum transfer */
> +};
> +
> +#endif
> diff --git a/hw/spapr.c b/hw/spapr.c
> index cb97a16..5f868fc 100644
> --- a/hw/spapr.c
> +++ b/hw/spapr.c
> @@ -28,6 +28,7 @@
> #include "hw.h"
> #include "elf.h"
> #include "net.h"
> +#include "blockdev.h"
>
> #include "hw/boards.h"
> #include "hw/ppc.h"
> @@ -316,7 +317,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
> qemu_free(filename);
>
> /* Set up Interrupt Controller */
> - spapr->icp = xics_system_init(smp_cpus, envs, MAX_SERIAL_PORTS + nb_nics);
> + spapr->icp = xics_system_init(smp_cpus, envs, MAX_SERIAL_PORTS + nb_nics + drive_get_max_bus(IF_SCSI) + 1);
This looks like it's exceeding 80 characters :)
>
> /* Set up VIO bus */
> spapr->vio_bus = spapr_vio_bus_init();
> @@ -346,6 +347,12 @@ static void ppc_spapr_init(ram_addr_t ram_size,
> }
> }
>
> + for (i = 0; i<= drive_get_max_bus(IF_SCSI); i++) {
> + spapr_vscsi_create(spapr->vio_bus, 0x2000 + i,
> + xics_find_qirq(spapr->icp, irq), irq);
> + irq++;
> + }
> +
> if (kernel_filename) {
> uint64_t lowaddr = 0;
>
> @@ -406,6 +413,7 @@ static QEMUMachine spapr_machine = {
> .max_cpus = MAX_CPUS,
> .no_vga = 1,
> .no_parallel = 1,
> + .use_scsi = 1,
> };
>
> static void spapr_machine_init(void)
> diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
> index ba16795..b7d0daa 100644
> --- a/hw/spapr_vio.h
> +++ b/hw/spapr_vio.h
> @@ -101,4 +101,7 @@ void spapr_vty_create(VIOsPAPRBus *bus,
> void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd,
> qemu_irq qirq, uint32_t vio_irq_num);
>
> +void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg,
> + qemu_irq qirq, uint32_t vio_irq_num);
> +
> #endif /* _HW_SPAPR_VIO_H */
> diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
> new file mode 100644
> index 0000000..0a67095
> --- /dev/null
> +++ b/hw/spapr_vscsi.c
> @@ -0,0 +1,960 @@
License header
> +/* TODO:
> + *
> + * - Cleanups :-)
> + * - Sort out better how to assign devices to VSCSI instances
> + * - Fix residual counts
> + * - Add indirect descriptors support
> + * - Maybe do autosense (PAPR seems to mandate it, linux doesn't care)
> + */
> +#include "hw.h"
> +#include "scsi.h"
> +#include "scsi-defs.h"
> +#include "net.h" /* Remove that when we can */
> +#include "srp.h"
> +#include "hw/qdev.h"
> +#include "hw/spapr.h"
> +#include "hw/spapr_vio.h"
> +#include "hw/ppc-viosrp.h"
> +
> +#include<libfdt.h>
> +
> +//#define DEBUG_VSCSI
> +
> +#ifdef DEBUG_VSCSI
> +#define dprintf(fmt, ...) \
> + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define dprintf(fmt, ...) \
> + do { } while (0)
> +#endif
> +
> +#define min(a, b) ((a)< (b) ? (a) : (b))
There's MIN for that
> +
> +/*
> + * Virtual SCSI device
> + */
> +
> +/* Random numbers */
> +#define VSCSI_MAX_SECTORS 4096/*1024*//*256*/
Probably good to just remove the commented out ones
> +#define VSCSI_REQ_LIMIT 24
> +
> +#define SCSI_SENSE_BUF_SIZE 96
> +#define SRP_RSP_SENSE_DATA_LEN 18
> +
> +typedef union vscsi_crq {
> + struct viosrp_crq s;
> + uint8_t raw[16];
> +} vscsi_crq;
> +
> +typedef struct vscsi_req
> +{
> + vscsi_crq crq;
> + union viosrp_iu iu;
> +
> + /* SCSI request tracking */
> + SCSIDevice *sdev;
> + uint32_t qtag; /* qemu tag != srp tag */
> + int lun;
> + int active;
> + long data_len;
> + int writing;
> + int sensing;
> + int senselen;
> + uint8_t sense[SCSI_SENSE_BUF_SIZE];
> +
> + /* RDMA related bits */
> + uint8_t dma_fmt;
> + struct srp_direct_buf ext_desc;
> + struct srp_direct_buf *cur_desc;
> + struct srp_indirect_buf *ind_desc;
> + int local_desc;
> + int total_desc;
> +
> +} vscsi_req;
> +
> +
> +typedef struct {
> + VIOsPAPRDevice vdev;
> + SCSIBus bus;
> + vscsi_req reqs[VSCSI_REQ_LIMIT];
> +} VSCSIState;
> +
> +/* XXX Debug only */
> +static VSCSIState *dbg_vscsi_state;
> +
> +
> +static struct vscsi_req *vscsi_get_req(VSCSIState *s)
> +{
> + vscsi_req *req;
> + int i;
> +
> + for (i = 0; i< VSCSI_REQ_LIMIT; i++) {
> + req =&s->reqs[i];
> + if (!req->active) {
> + memset(req, 0, sizeof(*req));
> + req->qtag = i;
> + req->active = 1;
> + return req;
> + }
> + }
> + return NULL;
> +}
> +
> +static void vscsi_put_req(VSCSIState *s, vscsi_req *req)
> +{
> + req->active = 0;
> +}
> +
> +static vscsi_req *vscsi_find_req(VSCSIState *s, uint32_t tag)
> +{
> + if (tag>= VSCSI_REQ_LIMIT || !s->reqs[tag].active) {
> + return NULL;
> + }
> + return&s->reqs[tag];
> +}
> +
> +static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun)
> +{
> + /* XXX Figure that one out properly ! This is crackpot */
> + *id = (srp_lun>> 56)& 0x7f;
> + *lun = (srp_lun>> 48)& 0xff;
> +}
> +
> +static int vscsi_send_iu(VSCSIState *s, vscsi_req *req,
> + uint64_t length, uint8_t format)
> +{
> + long rc, rc1;
> +
> + /* First copy the SRP */
> + rc = spapr_tce_dma_write(&s->vdev, req->crq.s.IU_data_ptr,
> +&req->iu, length);
> + if (rc) {
> + fprintf(stderr, "vscsi_send_iu: DMA write failure !\n");
> + }
> +
> + req->crq.s.valid = 0x80;
> + req->crq.s.format = format;
> + req->crq.s.reserved = 0x00;
> + req->crq.s.timeout = cpu_to_be16(0x0000);
> + req->crq.s.IU_length = cpu_to_be16(length);
> + req->crq.s.IU_data_ptr = req->iu.srp.rsp.tag; /* right byte order */
> +
> + if (rc == 0) {
> + req->crq.s.status = 0x99; /* Just needs to be non-zero */
> + } else {
> + req->crq.s.status = 0x00;
> + }
> +
> + rc1 = spapr_vio_send_crq(&s->vdev, req->crq.raw);
> + if (rc1) {
> + fprintf(stderr, "vscsi_send_iu: Error sending response\n");
> + return rc1;
> + }
> +
> + return rc;
> +}
> +
> +static void vscsi_makeup_sense(VSCSIState *s, vscsi_req *req,
> + uint8_t key, uint8_t asc, uint8_t ascq)
> +{
> + req->senselen = SRP_RSP_SENSE_DATA_LEN;
> +
> + /* Valid bit and 'current errors' */
> + req->sense[0] = (0x1<< 7 | 0x70);
> + /* Sense key */
> + req->sense[2] = key;
> + /* Additional sense length */
> + req->sense[7] = 0xa; /* 10 bytes */
> + /* Additional sense code */
> + req->sense[12] = asc;
> + req->sense[13] = ascq;
> +}
> +
> +static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req,
> + uint8_t status, int32_t res_in, int32_t res_out)
> +{
> + union viosrp_iu *iu =&req->iu;
> + uint64_t tag = iu->srp.rsp.tag;
> + int total_len = sizeof(iu->srp.rsp);
> +
> + dprintf("VSCSI: Sending resp status: 0x%x, "
> + "res_in: %d, res_out: %d \n", status, res_in, res_out);
> +
> + memset(iu, 0, sizeof(struct srp_rsp));
> + iu->srp.rsp.opcode = SRP_RSP;
> + iu->srp.rsp.req_lim_delta = cpu_to_be32(1);
> + iu->srp.rsp.tag = tag;
> +
> + /* Handle residuals */
> + if (res_in< 0) {
> + iu->srp.rsp.flags |= SRP_RSP_FLAG_DIUNDER;
> + res_in = -res_in;
> + } else if (res_in) {
> + iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER;
> + }
> + if (res_out< 0) {
> + iu->srp.rsp.flags |= SRP_RSP_FLAG_DOUNDER;
> + res_out = -res_out;
> + } else if (res_out) {
> + iu->srp.rsp.flags |= SRP_RSP_FLAG_DOOVER;
> + }
> + iu->srp.rsp.data_in_res_cnt = cpu_to_be32(res_in);
> + iu->srp.rsp.data_out_res_cnt = cpu_to_be32(res_out);
> +
> + /* We don't do response data */
> + /* iu->srp.rsp.flags&= ~SRP_RSP_FLAG_RSPVALID; */
> + iu->srp.rsp.resp_data_len = cpu_to_be32(0);
> +
> + /* Handle success vs. failure */
> + iu->srp.rsp.status = status;
> + if (status) {
> + iu->srp.rsp.sol_not = (iu->srp.cmd.sol_not& 0x04)>> 2;
> + if (req->senselen) {
> + req->iu.srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID;
> + req->iu.srp.rsp.sense_data_len = cpu_to_be32(req->senselen);
> + memcpy(req->iu.srp.rsp.data, req->sense, req->senselen);
> + total_len += req->senselen;
> + }
> + } else {
> + iu->srp.rsp.sol_not = (iu->srp.cmd.sol_not& 0x02)>> 1;
> + }
> +
> + vscsi_send_iu(s, req, total_len, VIOSRP_SRP_FORMAT);
> + return 0;
> +}
> +
> +static inline void vscsi_swap_desc(struct srp_direct_buf *desc)
> +{
> + desc->va = be64_to_cpu(desc->va);
> + desc->len = be32_to_cpu(desc->len);
> +}
> +
> +static int vscsi_srp_direct_data(VSCSIState *s, vscsi_req *req,
> + uint8_t *buf, uint32_t len)
> +{
> + struct srp_direct_buf *md = req->cur_desc;
> + uint32_t llen;
> + int rc;
> +
> + dprintf("VSCSI: direct segment 0x%x bytes, va=0x%llx desc len=0x%x\n",
> + len, (unsigned long long)md->va, md->len);
> +
> + llen = min(len, md->len);
> + if (llen) {
> + if (req->writing) { /* writing = to device = reading from memory */
> + rc = spapr_tce_dma_read(&s->vdev, md->va, buf, llen);
> + } else {
> + rc = spapr_tce_dma_write(&s->vdev, md->va, buf, llen);
> + }
> + }
> + md->len -= llen;
> + md->va += llen;
> +
> + if (rc) {
> + return -1;
> + }
> + return llen;
> +}
> +
> +static int vscsi_srp_indirect_data(VSCSIState *s, vscsi_req *req,
> + uint8_t *buf, uint32_t len)
> +{
> + struct srp_direct_buf *td =&req->ind_desc->table_desc;
> + struct srp_direct_buf *md = req->cur_desc;
> + int rc = 0;
> + uint32_t llen, total = 0;
> +
> + dprintf("VSCSI: indirect segment 0x%x bytes, td va=0x%llx len=0x%x\n",
> + len, (unsigned long long)td->va, td->len);
> +
> + /* While we have data ... */
> + while(len) {
> + /* If we have a descriptor but it's empty, go fetch a new one */
> + if (md&& md->len == 0) {
> + /* More local available, use one */
> + if (req->local_desc) {
> + md = ++req->cur_desc;
> + --req->local_desc;
> + --req->total_desc;
> + td->va += sizeof(struct srp_direct_buf);
> + } else {
> + md = req->cur_desc = NULL;
> + }
> + }
> + /* No descriptor at hand, fetch one */
> + if (!md) {
> + if (!req->total_desc) {
> + dprintf("VSCSI: Out of descriptors !\n");
> + break;
> + }
> + md = req->cur_desc =&req->ext_desc;
> + dprintf("VSCSI: Reading desc from 0x%llx\n", (unsigned long long)td->va);
> + rc = spapr_tce_dma_read(&s->vdev, td->va, md, sizeof(struct srp_direct_buf));
> + if (rc) {
> + dprintf("VSCSI: tce_dma_read -> %d reading ext_desc\n", rc);
> + break;
> + }
> + vscsi_swap_desc(md);
> + td->va += sizeof(struct srp_direct_buf);
> + --req->total_desc;
> + }
> + dprintf("VSCSI: [desc va=0x%llx,len=0x%x] remaining=0x%x\n",
> + (unsigned long long)md->va, md->len, len);
> +
> + /* Perform transfer */
> + llen = min(len, md->len);
> + if (req->writing) { /* writing = to device = reading from memory */
> + rc = spapr_tce_dma_read(&s->vdev, md->va, buf, llen);
> +
spurious line
> + } else {
> + rc = spapr_tce_dma_write(&s->vdev, md->va, buf, llen);
> + }
> + if (rc) {
> + dprintf("VSCSI: tce_dma_r/w(%d) -> %d\n", req->writing, rc);
> + break;
> + }
> + dprintf("VSCSI: data: %02x %02x %02x %02x...\n",
> + buf[0], buf[1], buf[2], buf[3]);
> +
> + len -= llen;
> + buf += llen;
> + total += llen;
> + md->va += llen;
> + md->len -= llen;
> + }
> + return rc ? -1 : total;
> +}
> +
> +static int vscsi_srp_transfer_data(VSCSIState *s, vscsi_req *req,
> + int writing, uint8_t *buf, uint32_t len)
> +{
> + int err = 0;
> +
> + switch (req->dma_fmt) {
> + case SRP_NO_DATA_DESC:
> + dprintf("VSCSI: no data desc transfer, skipping 0x%x bytes\n", len);
> + break;
> + case SRP_DATA_DESC_DIRECT:
> + err = vscsi_srp_direct_data(s, req, buf, len);
> + break;
> + case SRP_DATA_DESC_INDIRECT:
> + err = vscsi_srp_indirect_data(s, req, buf, len);
> + break;
> + }
> + return err;
> +}
> +
> +/* Bits from linux srp */
> +static int data_out_desc_size(struct srp_cmd *cmd)
> +{
> + int size = 0;
> + uint8_t fmt = cmd->buf_fmt>> 4;
> +
> + switch (fmt) {
> + case SRP_NO_DATA_DESC:
> + break;
> + case SRP_DATA_DESC_DIRECT:
> + size = sizeof(struct srp_direct_buf);
> + break;
> + case SRP_DATA_DESC_INDIRECT:
> + size = sizeof(struct srp_indirect_buf) +
> + sizeof(struct srp_direct_buf) * cmd->data_out_desc_cnt;
> + break;
> + default:
> + break;
> + }
> + return size;
> +}
> +
> +static int vscsi_preprocess_desc(vscsi_req *req)
> +{
> + struct srp_cmd *cmd =&req->iu.srp.cmd;
> + int offset, i;
> +
> + offset = cmd->add_cdb_len& ~3;
> +
> + if (req->writing) {
> + req->dma_fmt = cmd->buf_fmt>> 4;
> + } else {
> + offset += data_out_desc_size(cmd);
> + req->dma_fmt = cmd->buf_fmt& ((1U<< 4) - 1);
> + }
> +
> + switch (req->dma_fmt) {
> + case SRP_NO_DATA_DESC:
> + break;
> + case SRP_DATA_DESC_DIRECT:
> + req->cur_desc = (struct srp_direct_buf *)(cmd->add_data + offset);
> + req->total_desc = req->local_desc = 1;
> + vscsi_swap_desc(req->cur_desc);
> + dprintf("VSCSI: using direct RDMA %s, 0x%x bytes MD: 0x%llx\n",
> + req->writing ? "write" : "read",
> + req->cur_desc->len, (unsigned long long)req->cur_desc->va);
> + break;
> + case SRP_DATA_DESC_INDIRECT:
> + req->ind_desc = (struct srp_indirect_buf *)(cmd->add_data + offset);
> + vscsi_swap_desc(&req->ind_desc->table_desc);
> + req->total_desc = req->ind_desc->table_desc.len / sizeof(struct srp_direct_buf);
> + req->local_desc = req->writing ? cmd->data_out_desc_cnt :
> + cmd->data_in_desc_cnt;
> + for (i = 0; i< req->local_desc; i++)
Braces
> + vscsi_swap_desc(&req->ind_desc->desc_list[i]);
> + req->cur_desc = req->local_desc ?&req->ind_desc->desc_list[0] : NULL;
> + dprintf("VSCSI: using indirect RDMA %s, 0x%x bytes %d descs (%d local) VA: 0x%llx\n",
> + req->writing ? "read" : "write", be32_to_cpu(req->ind_desc->len),
> + req->total_desc, req->local_desc,
> + (unsigned long long)req->ind_desc->table_desc.va);
> + break;
> + default:
> + fprintf(stderr,
> + "vscsi_preprocess_desc: Unknown format %x\n", req->dma_fmt);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
> +{
> + SCSIDevice *sdev = req->sdev;
> + uint8_t *cdb = req->iu.srp.cmd.cdb;
> + int n;
> +
> + cdb[0] = 3;
> + cdb[1] = 0;
> + cdb[2] = 0;
> + cdb[3] = 0;
> + cdb[4] = 96;
> + cdb[5] = 0;
> + req->sensing = 1;
> + n = sdev->info->send_command(sdev, req->qtag, cdb, req->lun);
> + dprintf("VSCSI: Queued request sense tag 0x%x \n", req->qtag);
> + if (n< 0) {
> + fprintf(stderr, "VSCSI: REQUEST_SENSE wants write data !?!?!?\n");
> + sdev->info->cancel_io(sdev, req->qtag);
> + vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
> + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
> + vscsi_put_req(s, req);
> + return;
> + } else if (n == 0) {
> + return;
> + }
> + sdev->info->read_data(sdev, req->qtag);
> +}
> +
> +/* Callback to indicate that the SCSI layer has completed a transfer. */
> +static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
> + uint32_t arg)
> +{
> + VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, bus->qbus.parent);
> + vscsi_req *req = vscsi_find_req(s, tag);
> + SCSIDevice *sdev;
> + uint8_t *buf;
> + int32_t res_in = 0, res_out = 0;
> + int len, rc = 0;
> +
> + dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x arg=0x%x, req=%p\n",
> + reason, tag, arg, req);
> + if (req == NULL) {
> + fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", tag);
> + return;
> + }
> + sdev = req->sdev;
> +
> + if (req->sensing) {
> + if (reason == SCSI_REASON_DONE) {
> + dprintf("VSCSI: Sense done !\n");
> + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
> + vscsi_put_req(s, req);
> + } else {
> + uint8_t *buf = sdev->info->get_buf(sdev, tag);
> +
> + len = min(arg, SCSI_SENSE_BUF_SIZE);
> + dprintf("VSCSI: Sense data, %d bytes:\n", len);
> + dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
> + buf[0], buf[1], buf[2], buf[3],
> + buf[4], buf[5], buf[6], buf[7]);
> + dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
> + buf[8], buf[9], buf[10], buf[11],
> + buf[12], buf[13], buf[14], buf[15]);
> + memcpy(req->sense, buf, len);
> + req->senselen = len;
> + sdev->info->read_data(sdev, req->qtag);
> + }
> + return;
> + }
> +
> + if (reason == SCSI_REASON_DONE) {
> + dprintf("VSCSI: Command complete err=%d\n", arg);
> + if (arg == 0) {
> + /* We handle overflows, not underflows for normal commands,
> + * but hopefully nobody cares
> + */
> + if (req->writing)
Braces
> + res_out = req->data_len;
> + else
> + res_in = req->data_len;
> + vscsi_send_rsp(s, req, 0, res_in, res_out);
> + } else if (arg == CHECK_CONDITION) {
> + dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n");
> + vscsi_send_request_sense(s, req);
> + return;
> + } else {
> + vscsi_send_rsp(s, req, arg, 0, 0);
> + }
> + vscsi_put_req(s, req);
> + return;
> + }
> +
> + /* "arg" is how much we have read for reads and how much we want
> + * to write for writes (ie, how much is to be DMA'd)
> + */
> + if (arg) {
> + buf = sdev->info->get_buf(sdev, tag);
> + rc = vscsi_srp_transfer_data(s, req, req->writing, buf, arg);
> + }
> + if (rc< 0) {
> + fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc);
> + sdev->info->cancel_io(sdev, req->qtag);
> + vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
> + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
> + vscsi_put_req(s, req);
> + return;
> + }
> +
> + /* Start next chunk */
> + req->data_len -= rc;
> + if (req->writing) {
> + sdev->info->write_data(sdev, req->qtag);
> + } else {
> + sdev->info->read_data(sdev, req->qtag);
> + }
> +}
> +
> +static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
> +{
> + union viosrp_iu *iu =&req->iu;
> + struct srp_login_rsp *rsp =&iu->srp.login_rsp;
> + uint64_t tag = iu->srp.rsp.tag;
> +
> + dprintf("VSCSI: Got login, sendin response !\n");
> +
> + /* TODO handle case that requested size is wrong and
> + * buffer format is wrong
> + */
> + memset(iu, 0, sizeof(struct srp_login_rsp));
> + rsp->opcode = SRP_LOGIN_RSP;
> + /* Don't advertise quite as many request as we support to
> + * keep room for management stuff etc...
> + */
> + rsp->req_lim_delta = cpu_to_be32(VSCSI_REQ_LIMIT-2);
> + rsp->tag = tag;
> + rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu));
> + rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu));
> + /* direct and indirect */
> + rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT);
> +
> + vscsi_send_iu(s, req, sizeof(*rsp), VIOSRP_SRP_FORMAT);
> +}
> +
> +static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req)
> +{
> + uint8_t *cdb = req->iu.srp.cmd.cdb;
> + uint8_t resp_data[36];
> + int rc, len, alen;
> +
> + /* We dont do EVPD. Also check that page_code is 0 */
> + if ((cdb[1]& 0x01) || (cdb[1]& 0x01) || cdb[2] != 0) {
> + /* Send INVALID FIELD IN CDB */
> + vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0);
> + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
> + return;
> + }
> + alen = cdb[3];
> + alen = (alen<< 8) | cdb[4];
> + len = min(alen, 36);
> +
> + /* Fake up inquiry using PQ=3 */
> + memset(resp_data, 0, 36);
> + resp_data[0] = 0x7f; /* Not capable of supporting a device here */
> + resp_data[2] = 0x06; /* SPS-4 */
> + resp_data[3] = 0x02; /* Resp data format */
> + resp_data[4] = 36 - 5; /* Additional length */
> + resp_data[7] = 0x10; /* Sync transfers */
> + memcpy(&resp_data[16], "QEMU EMPTY ", 16);
> + memcpy(&resp_data[8], "QEMU ", 8);
> +
> + req->writing = 0;
> + vscsi_preprocess_desc(req);
> + rc = vscsi_srp_transfer_data(s, req, 0, resp_data, len);
> + if (rc< 0) {
> + vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
> + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
> + } else {
> + vscsi_send_rsp(s, req, 0, 36 - rc, 0);
> + }
> +}
> +
> +static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
> +{
> + union srp_iu *srp =&req->iu.srp;
> + SCSIDevice *sdev;
> + int n, id, lun;
> +
> + vscsi_decode_id_lun(be64_to_cpu(srp->cmd.lun),&id,&lun);
> +
> + /* Qemu vs. linux issue with LUNs to be sorted out ... */
> + sdev = (id< 8&& lun< 16) ? s->bus.devs[id] : NULL;
> + if (!sdev) {
> + dprintf("VSCSI: Command for id %d with no drive\n", id);
> + if (srp->cmd.cdb[0] == INQUIRY) {
> + vscsi_inquiry_no_target(s, req);
> + } else {
> + vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0x00);
> + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
> + } return 1;
> + }
> +
> + req->sdev = sdev;
> + req->lun = lun;
> + n = sdev->info->send_command(sdev, req->qtag, srp->cmd.cdb, lun);
> +
> + dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n",
> + req->qtag, srp->cmd.cdb[0], id, lun, n);
> +
> + if (n) {
> + /* Transfer direction must be set before preprocessing the
> + * descriptors
> + */
> + req->writing = (n< 1);
> +
> + /* Preprocess RDMA descriptors */
> + vscsi_preprocess_desc(req);
> + }
> +
> + /* Get transfer direction and initiate transfer */
> + if (n> 0) {
> + req->data_len = n;
> + sdev->info->read_data(sdev, req->qtag);
> + } else if (n< 0) {
> + req->data_len = -n;
> + sdev->info->write_data(sdev, req->qtag);
> + }
> + /* Don't touch req here, it may have been recycled already */
> +
> + return 0;
> +}
> +
> +static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
> +{
> + union viosrp_iu *iu =&req->iu;
> + int fn;
> +
> + fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n",
> + iu->srp.tsk_mgmt.tsk_mgmt_func);
> +
> + switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
> +#if 0 /* We really don't deal with these for now */
> + case SRP_TSK_ABORT_TASK:
> + fn = ABORT_TASK;
> + break;
> + case SRP_TSK_ABORT_TASK_SET:
> + fn = ABORT_TASK_SET;
> + break;
> + case SRP_TSK_CLEAR_TASK_SET:
> + fn = CLEAR_TASK_SET;
> + break;
> + case SRP_TSK_LUN_RESET:
> + fn = LOGICAL_UNIT_RESET;
> + break;
> + case SRP_TSK_CLEAR_ACA:
> + fn = CLEAR_ACA;
> + break;
> +#endif
> + default:
> + fn = 0;
> + }
> + if (fn) {
> + /* XXX Send/Handle target task management */
> + ;
> + } else {
> + vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x20, 0);
> + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
> + }
> + return !fn;
> +}
> +
> +static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req)
> +{
> + union srp_iu *srp =&req->iu.srp;
> + int done = 1;
> + uint8_t opcode = srp->rsp.opcode;
> +
> + switch (opcode) {
> + case SRP_LOGIN_REQ:
> + vscsi_process_login(s, req);
> + break;
> + case SRP_TSK_MGMT:
> + done = vscsi_process_tsk_mgmt(s, req);
> + break;
> + case SRP_CMD:
> + done = vscsi_queue_cmd(s, req);
> + break;
> + case SRP_LOGIN_RSP:
> + case SRP_I_LOGOUT:
> + case SRP_T_LOGOUT:
> + case SRP_RSP:
> + case SRP_CRED_REQ:
> + case SRP_CRED_RSP:
> + case SRP_AER_REQ:
> + case SRP_AER_RSP:
> + fprintf(stderr, "VSCSI: Unsupported opcode %02x\n", opcode);
> + break;
> + default:
> + fprintf(stderr, "VSCSI: Unknown type %02x\n", opcode);
> + }
> +
> + return done;
> +}
> +
> +static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req)
> +{
> + struct viosrp_adapter_info *sinfo;
> + struct mad_adapter_info_data info;
> + int rc;
> +
> + sinfo =&req->iu.mad.adapter_info;
> +
> +#if 0 /* What for ? */
> + rc = spapr_tce_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer),
> +&info, be16_to_cpu(sinfo->common.length));
> + if (rc) {
> + fprintf(stderr, "vscsi_send_adapter_info: DMA read failure !\n");
> + }
> +#endif
> + memset(&info, 0, sizeof(info));
> + strcpy(info.srp_version, SRP_VERSION);
> + strncpy(info.partition_name, "qemu", sizeof("qemu"));
> + info.partition_number = cpu_to_be32(0);
> + info.mad_version = cpu_to_be32(1);
> + info.os_type = cpu_to_be32(2);
> + info.port_max_txu[0] = cpu_to_be32(VSCSI_MAX_SECTORS<< 9);
> +
> + rc = spapr_tce_dma_write(&s->vdev, be64_to_cpu(sinfo->buffer),
> +&info, be16_to_cpu(sinfo->common.length));
> + if (rc) {
> + fprintf(stderr, "vscsi_send_adapter_info: DMA write failure !\n");
> + }
> +
> + sinfo->common.status = rc ? cpu_to_be32(1) : 0;
> +
> + return vscsi_send_iu(s, req, sizeof(*sinfo), VIOSRP_MAD_FORMAT);
> +}
> +
> +static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req)
> +{
> + union mad_iu *mad =&req->iu.mad;
> +
> + switch (be32_to_cpu(mad->empty_iu.common.type)) {
> + case VIOSRP_EMPTY_IU_TYPE:
> + fprintf(stderr, "Unsupported EMPTY MAD IU\n");
> + break;
> + case VIOSRP_ERROR_LOG_TYPE:
> + fprintf(stderr, "Unsupported ERROR LOG MAD IU\n");
> + mad->error_log.common.status = cpu_to_be16(1);
> + vscsi_send_iu(s, req, sizeof(mad->error_log), VIOSRP_MAD_FORMAT);
> + break;
> + case VIOSRP_ADAPTER_INFO_TYPE:
> + vscsi_send_adapter_info(s, req);
> + break;
> + case VIOSRP_HOST_CONFIG_TYPE:
> + mad->host_config.common.status = cpu_to_be16(1);
> + vscsi_send_iu(s, req, sizeof(mad->host_config), VIOSRP_MAD_FORMAT);
> + break;
> + default:
> + fprintf(stderr, "VSCSI: Unknown MAD type %02x\n",
> + be32_to_cpu(mad->empty_iu.common.type));
> + }
> +
> + return 1;
> +}
> +
> +static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq)
> +{
> + vscsi_req *req;
> + int done;
> +
> + req = vscsi_get_req(s);
> + if (req == NULL) {
> + fprintf(stderr, "VSCSI: Failed to get a request !\n");
> + return;
> + }
> +
> + /* We only support a limited number of descriptors, we know
> + * the ibmvscsi driver uses up to 10 max, so it should fit
> + * in our 256 bytes IUs. If not we'll have to increase the size
> + * of the structure.
> + */
> + if (crq->s.IU_length> sizeof(union viosrp_iu)) {
> + fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n",
> + crq->s.IU_length);
> + return;
> + }
> +
> + /* XXX Handle failure differently ? */
> + if (spapr_tce_dma_read(&s->vdev, crq->s.IU_data_ptr,&req->iu,
> + crq->s.IU_length)) {
> + fprintf(stderr, "vscsi_got_payload: DMA read failure !\n");
> + qemu_free(req);
> + }
> + memcpy(&req->crq, crq, sizeof(vscsi_crq));
> +
> + if (crq->s.format == VIOSRP_MAD_FORMAT) {
> + done = vscsi_handle_mad_req(s, req);
> + } else {
> + done = vscsi_handle_srp_req(s, req);
> + }
> +
> + if (done) {
> + vscsi_put_req(s, req);
> + }
> +}
> +
> +
> +static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data)
> +{
> + VSCSIState *s = DO_UPCAST(VSCSIState, vdev, dev);
> + vscsi_crq crq;
> +
> + memcpy(crq.raw, crq_data, 16);
> + crq.s.timeout = be16_to_cpu(crq.s.timeout);
> + crq.s.IU_length = be16_to_cpu(crq.s.IU_length);
> + crq.s.IU_data_ptr = be64_to_cpu(crq.s.IU_data_ptr);
> +
> + dprintf("VSCSI: do_crq %02x %02x ...\n", crq.raw[0], crq.raw[1]);
> +
> + switch(crq.s.valid) {
> + case 0xc0: /* Init command/response */
> +
> + /* Respond to initialization request */
> + if (crq.s.format == 0x01) {
> + memset(crq.raw, 0, 16);
> + crq.s.valid = 0xc0;
> + crq.s.format = 0x02;
> + spapr_vio_send_crq(dev, crq.raw);
> + }
> +
> + /* Note that in hotplug cases, we might get a 0x02
> + * as a result of us emitting the init request
> + */
> +
> + break;
> + case 0xff: /* Link event */
> +
> + /* Not handled for now */
> +
> + break;
> + case 0x80: /* Payloads */
> + switch (crq.s.format) {
> + case VIOSRP_SRP_FORMAT: /* AKA VSCSI request */
> + case VIOSRP_MAD_FORMAT: /* AKA VSCSI response */
> + vscsi_got_payload(s,&crq);
> + break;
> + case VIOSRP_OS400_FORMAT:
> + case VIOSRP_AIX_FORMAT:
> + case VIOSRP_LINUX_FORMAT:
> + case VIOSRP_INLINE_FORMAT:
> + fprintf(stderr, "vscsi_do_srq: Unsupported payload format %02x\n",
> + crq.s.format);
> + break;
> + default:
> + fprintf(stderr, "vscsi_do_srq: Unknown payload format %02x\n",
> + crq.s.format);
> + }
> + break;
> + default:
> + fprintf(stderr, "vscsi_do_crq: unknown CRQ %02x %02x ...\n",
> + crq.raw[0], crq.raw[1]);
> + };
> +
> + return 0;
> +}
> +
> +static int spapr_vscsi_init(VIOsPAPRDevice *dev)
> +{
> + VSCSIState *s = DO_UPCAST(VSCSIState, vdev, dev);
> + int i;
> +
> + dbg_vscsi_state = s;
> +
> + /* Initialize qemu request tags */
> + memset(s->reqs, 0, sizeof(s->reqs));
> + for (i = 0; i< VSCSI_REQ_LIMIT; i++)
Braces
> + s->reqs[i].qtag = i;
> +
> + dev->crq.SendFunc = vscsi_do_crq;
> +
> + scsi_bus_new(&s->bus,&dev->qdev, 1, VSCSI_REQ_LIMIT,
> + vscsi_command_complete);
> + if (!dev->qdev.hotplugged) {
> + scsi_bus_legacy_handle_cmdline(&s->bus);
> + }
> +
> + return 0;
> +}
> +
> +void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg,
> + qemu_irq qirq, uint32_t vio_irq_num)
> +{
> + DeviceState *dev;
> + VIOsPAPRDevice *sdev;
> +
> + dev = qdev_create(&bus->bus, "spapr-vscsi");
> + qdev_prop_set_uint32(dev, "reg", reg);
> +
> + qdev_init_nofail(dev);
> +
> + sdev = (VIOsPAPRDevice *)dev;
> + sdev->qirq = qirq;
> + sdev->vio_irq_num = vio_irq_num;
> +}
> +
> +static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
> +{
> + int ret;
> +
> + ret = fdt_setprop_cell(fdt, node_off, "#address-cells", 2);
> + if (ret< 0) {
> + return ret;
> + }
> +
> + ret = fdt_setprop_cell(fdt, node_off, "#size-cells", 0);
> + if (ret< 0) {
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static VIOsPAPRDeviceInfo spapr_vscsi = {
> + .init = spapr_vscsi_init,
> + .devnode = spapr_vscsi_devnode,
> + .dt_name = "v-scsi",
> + .dt_type = "vscsi",
> + .dt_compatible = "IBM,v-scsi",
> + .signal_mask = 0x00000001,
> + .qdev.name = "spapr-vscsi",
> + .qdev.size = sizeof(VSCSIState),
> + .qdev.props = (Property[]) {
> + DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x2000),
> + DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice,
> + rtce_window_size, 0x10000000),
> + DEFINE_PROP_END_OF_LIST(),
> + },
> +};
> +
> +static void spapr_vscsi_register(void)
> +{
> + spapr_vio_bus_register_withprop(&spapr_vscsi);
> +}
> +device_init(spapr_vscsi_register);
> diff --git a/hw/srp.h b/hw/srp.h
> new file mode 100644
> index 0000000..9d55fc4
> --- /dev/null
> +++ b/hw/srp.h
> @@ -0,0 +1,241 @@
> +/*
> + * Copyright (c) 2005 Cisco Systems. All rights reserved.
> + *
> + * This software is available to you under a choice of one of two
> + * licenses. You may choose to be licensed under the terms of the GNU
> + * General Public License (GPL) Version 2, available from the file
> + * COPYING in the main directory of this source tree, or the
> + * OpenIB.org BSD license below:
> + *
> + * Redistribution and use in source and binary forms, with or
> + * without modification, are permitted provided that the following
> + * conditions are met:
> + *
> + * - Redistributions of source code must retain the above
> + * copyright notice, this list of conditions and the following
> + * disclaimer.
> + *
> + * - Redistributions in binary form must reproduce the above
> + * copyright notice, this list of conditions and the following
> + * disclaimer in the documentation and/or other materials
> + * provided with the distribution.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + *
> + * $Id$
> + */
> +
> +#ifndef SCSI_SRP_H
> +#define SCSI_SRP_H
> +
> +/*
> + * Structures and constants for the SCSI RDMA Protocol (SRP) as
> + * defined by the INCITS T10 committee. This file was written using
> + * draft Revision 16a of the SRP standard.
> + */
> +
> +enum {
> +
> + SRP_LOGIN_REQ = 0x00,
> + SRP_TSK_MGMT = 0x01,
> + SRP_CMD = 0x02,
> + SRP_I_LOGOUT = 0x03,
> + SRP_LOGIN_RSP = 0xc0,
> + SRP_RSP = 0xc1,
> + SRP_LOGIN_REJ = 0xc2,
> + SRP_T_LOGOUT = 0x80,
> + SRP_CRED_REQ = 0x81,
> + SRP_AER_REQ = 0x82,
> + SRP_CRED_RSP = 0x41,
> + SRP_AER_RSP = 0x42
> +};
> +
> +enum {
> + SRP_BUF_FORMAT_DIRECT = 1<< 1,
> + SRP_BUF_FORMAT_INDIRECT = 1<< 2
> +};
> +
> +enum {
> + SRP_NO_DATA_DESC = 0,
> + SRP_DATA_DESC_DIRECT = 1,
> + SRP_DATA_DESC_INDIRECT = 2
> +};
> +
> +enum {
> + SRP_TSK_ABORT_TASK = 0x01,
> + SRP_TSK_ABORT_TASK_SET = 0x02,
> + SRP_TSK_CLEAR_TASK_SET = 0x04,
> + SRP_TSK_LUN_RESET = 0x08,
> + SRP_TSK_CLEAR_ACA = 0x40
> +};
> +
> +enum srp_login_rej_reason {
> + SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL = 0x00010000,
> + SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES = 0x00010001,
> + SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE = 0x00010002,
> + SRP_LOGIN_REJ_UNABLE_ASSOCIATE_CHANNEL = 0x00010003,
> + SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT = 0x00010004,
> + SRP_LOGIN_REJ_MULTI_CHANNEL_UNSUPPORTED = 0x00010005,
> + SRP_LOGIN_REJ_CHANNEL_LIMIT_REACHED = 0x00010006
> +};
> +
> +enum {
> + SRP_REV10_IB_IO_CLASS = 0xff00,
> + SRP_REV16A_IB_IO_CLASS = 0x0100
> +};
> +
> +struct srp_direct_buf {
> + uint64_t va;
> + uint32_t key;
> + uint32_t len;
> +};
> +
> +/*
> + * We need the packed attribute because the SRP spec puts the list of
> + * descriptors at an offset of 20, which is not aligned to the size of
> + * struct srp_direct_buf. The whole structure must be packed to avoid
> + * having the 20-byte structure padded to 24 bytes on 64-bit architectures.
> + */
> +struct srp_indirect_buf {
> + struct srp_direct_buf table_desc;
> + uint32_t len;
> + struct srp_direct_buf desc_list[0];
> +} __attribute__((packed));
> +
> +enum {
> + SRP_MULTICHAN_SINGLE = 0,
> + SRP_MULTICHAN_MULTI = 1
> +};
> +
> +struct srp_login_req {
> + uint8_t opcode;
> + uint8_t reserved1[7];
> + uint64_t tag;
> + uint32_t req_it_iu_len;
> + uint8_t reserved2[4];
> + uint16_t req_buf_fmt;
> + uint8_t req_flags;
> + uint8_t reserved3[5];
> + uint8_t initiator_port_id[16];
> + uint8_t target_port_id[16];
> +};
> +
> +/*
> + * The SRP spec defines the size of the LOGIN_RSP structure to be 52
> + * bytes, so it needs to be packed to avoid having it padded to 56
> + * bytes on 64-bit architectures.
> + */
> +struct srp_login_rsp {
> + uint8_t opcode;
> + uint8_t reserved1[3];
> + uint32_t req_lim_delta;
> + uint64_t tag;
> + uint32_t max_it_iu_len;
> + uint32_t max_ti_iu_len;
> + uint16_t buf_fmt;
> + uint8_t rsp_flags;
> + uint8_t reserved2[25];
> +} __attribute__((packed));
> +
> +struct srp_login_rej {
> + uint8_t opcode;
> + uint8_t reserved1[3];
> + uint32_t reason;
> + uint64_t tag;
> + uint8_t reserved2[8];
> + uint16_t buf_fmt;
> + uint8_t reserved3[6];
> +};
Why isn't this one packed? And the ones below?
> +
> +struct srp_i_logout {
> + uint8_t opcode;
> + uint8_t reserved[7];
> + uint64_t tag;
> +};
> +
> +struct srp_t_logout {
> + uint8_t opcode;
> + uint8_t sol_not;
> + uint8_t reserved[2];
> + uint32_t reason;
> + uint64_t tag;
> +};
> +
> +/*
> + * We need the packed attribute because the SRP spec only aligns the
> + * 8-byte LUN field to 4 bytes.
> + */
> +struct srp_tsk_mgmt {
> + uint8_t opcode;
> + uint8_t sol_not;
> + uint8_t reserved1[6];
> + uint64_t tag;
> + uint8_t reserved2[4];
> + uint64_t lun __attribute__((packed));
> + uint8_t reserved3[2];
> + uint8_t tsk_mgmt_func;
> + uint8_t reserved4;
> + uint64_t task_tag;
> + uint8_t reserved5[8];
> +};
> +
> +/*
> + * We need the packed attribute because the SRP spec only aligns the
> + * 8-byte LUN field to 4 bytes.
> + */
> +struct srp_cmd {
> + uint8_t opcode;
> + uint8_t sol_not;
> + uint8_t reserved1[3];
> + uint8_t buf_fmt;
> + uint8_t data_out_desc_cnt;
> + uint8_t data_in_desc_cnt;
> + uint64_t tag;
> + uint8_t reserved2[4];
> + uint64_t lun __attribute__((packed));
> + uint8_t reserved3;
> + uint8_t task_attr;
> + uint8_t reserved4;
> + uint8_t add_cdb_len;
> + uint8_t cdb[16];
> + uint8_t add_data[0];
> +};
> +
> +enum {
> + SRP_RSP_FLAG_RSPVALID = 1<< 0,
> + SRP_RSP_FLAG_SNSVALID = 1<< 1,
> + SRP_RSP_FLAG_DOOVER = 1<< 2,
> + SRP_RSP_FLAG_DOUNDER = 1<< 3,
> + SRP_RSP_FLAG_DIOVER = 1<< 4,
> + SRP_RSP_FLAG_DIUNDER = 1<< 5
> +};
> +
> +/*
> + * The SRP spec defines the size of the RSP structure to be 36 bytes,
> + * so it needs to be packed to avoid having it padded to 40 bytes on
> + * 64-bit architectures.
> + */
> +struct srp_rsp {
> + uint8_t opcode;
> + uint8_t sol_not;
> + uint8_t reserved1[2];
> + uint32_t req_lim_delta;
> + uint64_t tag;
> + uint8_t reserved2[2];
> + uint8_t flags;
> + uint8_t status;
> + uint32_t data_out_res_cnt;
> + uint32_t data_in_res_cnt;
> + uint32_t sense_data_len;
> + uint32_t resp_data_len;
> + uint8_t data[0];
> +} __attribute__((packed));
> +
> +#endif /* SCSI_SRP_H */
Alex
next prev parent reply other threads:[~2011-03-16 16:42 UTC|newest]
Thread overview: 82+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-03-16 4:56 [Qemu-devel] Implement emulation of pSeries logical partitions (v3) David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 01/26] Clean up PowerPC SLB handling code David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 02/26] Allow qemu_devtree_setprop() to take arbitrary values David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 03/26] Add a hook to allow hypercalls to be emulated on PowerPC David Gibson
2011-03-16 13:46 ` [Qemu-devel] " Alexander Graf
2011-03-16 16:58 ` Stefan Hajnoczi
2011-03-17 2:26 ` David Gibson
2011-03-16 20:44 ` [Qemu-devel] " Anthony Liguori
2011-03-17 4:55 ` David Gibson
2011-03-17 13:20 ` Anthony Liguori
2011-03-18 4:03 ` David Gibson
2011-03-18 6:57 ` Alexander Graf
2011-03-16 4:56 ` [Qemu-devel] [PATCH 04/26] Implement PowerPC slbmfee and slbmfev instructions David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 05/26] Implement missing parts of the logic for the POWER PURR David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 06/26] Correct ppc popcntb logic, implement popcntw and popcntd David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 07/26] Clean up slb_lookup() function David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 08/26] Parse SDR1 on mtspr instead of at translate time David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 09/26] Use "hash" more consistently in ppc mmu code David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 10/26] Better factor the ppc hash translation path David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 11/26] Support 1T segments on ppc David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 12/26] Add POWER7 support for ppc David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 13/26] Start implementing pSeries logical partition machine David Gibson
2011-03-16 14:30 ` [Qemu-devel] " Alexander Graf
2011-03-16 21:59 ` [Qemu-devel] " Anthony Liguori
2011-03-16 23:46 ` Alexander Graf
2011-03-17 3:08 ` David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 14/26] Implement the bus structure for PAPR virtual IO David Gibson
2011-03-16 14:43 ` [Qemu-devel] " Alexander Graf
2011-03-16 22:04 ` [Qemu-devel] " Anthony Liguori
2011-03-17 3:19 ` David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 15/26] Virtual hash page table handling on pSeries machine David Gibson
2011-03-16 15:03 ` [Qemu-devel] " Alexander Graf
2011-03-17 1:03 ` [Qemu-devel] Re: [PATCH 15/26] Virtual hash page table handling on pSeries machine' David Gibson
2011-03-17 7:35 ` Alexander Graf
2011-03-16 4:56 ` [Qemu-devel] [PATCH 16/26] Implement hcall based RTAS for pSeries machines David Gibson
2011-03-16 15:08 ` [Qemu-devel] " Alexander Graf
2011-03-17 1:22 ` David Gibson
2011-03-17 7:36 ` Alexander Graf
2011-03-16 22:08 ` [Qemu-devel] " Anthony Liguori
2011-03-16 4:56 ` [Qemu-devel] [PATCH 17/26] Implement assorted pSeries hcalls and RTAS methods David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 18/26] Implement the PAPR (pSeries) virtualized interrupt controller (xics) David Gibson
2011-03-16 15:47 ` [Qemu-devel] " Alexander Graf
2011-03-17 1:29 ` David Gibson
2011-03-17 7:37 ` Alexander Graf
2011-03-16 22:16 ` [Qemu-devel] " Anthony Liguori
2011-03-17 1:34 ` David Gibson
2011-03-17 13:13 ` Anthony Liguori
2011-03-23 3:48 ` David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 19/26] Add PAPR H_VIO_SIGNAL hypercall and infrastructure for VIO interrupts David Gibson
2011-03-16 15:49 ` [Qemu-devel] " Alexander Graf
2011-03-17 1:38 ` David Gibson
2011-03-17 7:38 ` Alexander Graf
2011-03-16 4:56 ` [Qemu-devel] [PATCH 20/26] Add (virtual) interrupt to PAPR virtual tty device David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 21/26] Implement TCE translation for sPAPR VIO David Gibson
2011-03-16 16:03 ` [Qemu-devel] " Alexander Graf
2011-03-16 20:05 ` Benjamin Herrenschmidt
2011-03-16 20:21 ` Anthony Liguori
2011-03-16 20:22 ` Anthony Liguori
2011-03-16 20:36 ` Benjamin Herrenschmidt
2011-03-17 1:43 ` David Gibson
2011-03-16 22:20 ` [Qemu-devel] " Anthony Liguori
2011-03-18 1:58 ` David Gibson
2011-03-16 4:56 ` [Qemu-devel] [PATCH 22/26] Implement sPAPR Virtual LAN (ibmveth) David Gibson
2011-03-16 16:12 ` [Qemu-devel] " Alexander Graf
2011-03-17 2:04 ` David Gibson
2011-03-16 22:29 ` [Qemu-devel] " Anthony Liguori
2011-03-17 2:09 ` David Gibson
2011-03-16 4:57 ` [Qemu-devel] [PATCH 23/26] Implement PAPR CRQ hypercalls David Gibson
2011-03-16 16:15 ` [Qemu-devel] " Alexander Graf
2011-03-16 4:57 ` [Qemu-devel] [PATCH 24/26] Implement PAPR virtual SCSI interface (ibmvscsi) David Gibson
2011-03-16 16:41 ` Alexander Graf [this message]
2011-03-16 16:51 ` [Qemu-devel] " Anthony Liguori
2011-03-16 20:08 ` Benjamin Herrenschmidt
2011-03-16 20:19 ` Anthony Liguori
2011-03-16 4:57 ` [Qemu-devel] [PATCH 25/26] Add a PAPR TCE-bypass mechanism for the pSeries machine David Gibson
2011-03-16 16:43 ` [Qemu-devel] " Alexander Graf
2011-03-17 2:21 ` David Gibson
2011-03-17 3:25 ` Benjamin Herrenschmidt
2011-03-17 7:44 ` Alexander Graf
2011-03-17 8:44 ` Benjamin Herrenschmidt
2011-03-17 9:37 ` Alexander Graf
2011-03-16 4:57 ` [Qemu-devel] [PATCH 26/26] Implement PAPR VPA functions for pSeries shared processor partitions David Gibson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4D80E852.5020109@suse.de \
--to=agraf@suse.de \
--cc=anton@samba.org \
--cc=david@gibson.dropbear.id.au \
--cc=paulus@samba.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).