diff --git a/block.c b/block.c
index af2ab4f..2467b8e 100644
--- a/block.c
+++ b/block.c
@@ -507,10 +507,11 @@ static int find_image_format(const char *filename, BlockDriver **pdrv)
         return ret;
     }
 
-    /* Return the raw BlockDriver * to scsi-generic devices or empty drives */
-    if (bs->sg || !bdrv_is_inserted(bs)) {
+    /* Return the raw BlockDriver * to scsi-generic devices,
+       iscsi devices or empty drives */
+    if (bs->sg || bs->iscsi || !bdrv_is_inserted(bs)) {
         bdrv_delete(bs);
-        drv = bdrv_find_format("raw");
+        drv = bdrv_find_format(bs->iscsi ? "iscsi" : "raw");
         if (!drv) {
             ret = -ENOENT;
         }
@@ -550,9 +551,10 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
 {
     BlockDriver *drv = bs->drv;
 
-    /* Do not attempt drv->bdrv_getlength() on scsi-generic devices */
-    if (bs->sg)
+    /* Do not attempt drv->bdrv_getlength() on scsi-generic/iscsi devices */
+    if (bs->sg || bs->iscsi) {
         return 0;
+    }
 
     /* query actual device if possible, otherwise just trust the hint */
     if (drv->bdrv_getlength) {
@@ -879,6 +881,7 @@ void bdrv_close(BlockDriverState *bs)
         bs->encrypted = 0;
         bs->valid_key = 0;
         bs->sg = 0;
+        bs->iscsi = 0;
         bs->growable = 0;
 
         if (bs->file != NULL) {
@@ -2355,6 +2358,11 @@ int bdrv_is_sg(BlockDriverState *bs)
     return bs->sg;
 }
 
+int bdrv_is_iscsi(BlockDriverState *bs)
+{
+    return bs->iscsi;
+}
+
 int bdrv_enable_write_cache(BlockDriverState *bs)
 {
     return bs->enable_write_cache;
diff --git a/block.h b/block.h
index 7408acc..642194b 100644
--- a/block.h
+++ b/block.h
@@ -279,6 +279,7 @@ void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
 BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read);
 int bdrv_is_read_only(BlockDriverState *bs);
 int bdrv_is_sg(BlockDriverState *bs);
+int bdrv_is_iscsi(BlockDriverState *bs);
 int bdrv_enable_write_cache(BlockDriverState *bs);
 int bdrv_is_inserted(BlockDriverState *bs);
 int bdrv_media_changed(BlockDriverState *bs);
diff --git a/block/iscsi.c b/block/iscsi.c
index 9642ee6..301ab69 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -34,10 +34,15 @@
 #include <iscsi/iscsi.h>
 #include <iscsi/scsi-lowlevel.h>
 
+#ifdef __linux__
+#include <scsi/sg.h>
+#include <hw/scsi-defs.h>
+#endif
 
 typedef struct IscsiLun {
     struct iscsi_context *iscsi;
     int lun;
+    enum scsi_inquiry_peripheral_device_type type;
     int block_size;
     unsigned long num_blocks;
     IOHandler *read_handler;
@@ -55,6 +60,9 @@ typedef struct IscsiAIOCB {
     int canceled;
     size_t read_size;
     size_t read_offset;
+#ifdef __linux__
+    sg_io_hdr_t *ioh;
+#endif
 } IscsiAIOCB;
 
 struct IscsiTask {
@@ -526,6 +534,140 @@ iscsi_aio_discard(BlockDriverState *bs,
     return &acb->common;
 }
 
+#ifdef __linux__
+static void
+iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
+                     void *command_data, void *opaque)
+{
+    IscsiAIOCB *acb = opaque;
+
+    if (acb->canceled != 0) {
+        qemu_aio_release(acb);
+        scsi_free_scsi_task(acb->task);
+        acb->task = NULL;
+        return;
+    }
+
+    acb->status = 0;
+    if (status < 0) {
+        error_report("Failed to ioctl(SG_IO) to iSCSI lun. %s",
+                     iscsi_get_error(iscsi));
+        acb->status = -EIO;
+    }
+
+    acb->ioh->driver_status = 0;
+    acb->ioh->host_status   = 0;
+    acb->ioh->resid         = 0;
+
+#define SG_ERR_DRIVER_SENSE    0x08
+
+    if (status == SCSI_STATUS_CHECK_CONDITION && acb->task->datain.size >= 2) {
+        int ss;
+
+        acb->ioh->driver_status |= SG_ERR_DRIVER_SENSE;
+
+        acb->ioh->sb_len_wr = acb->task->datain.size - 2;
+        ss = (acb->ioh->mx_sb_len >= acb->ioh->sb_len_wr) ?
+             acb->ioh->mx_sb_len : acb->ioh->sb_len_wr;
+        memcpy(acb->ioh->sbp, &acb->task->datain.data[2], ss);
+    }
+
+    iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+    scsi_free_scsi_task(acb->task);
+    acb->task = NULL;
+}
+
+static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
+        unsigned long int req, void *buf,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    IscsiLun *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+    struct iscsi_data data;
+    IscsiAIOCB *acb;
+
+    if (req != SG_IO) {
+        error_report("iSCSI: iscsi_aio_ioctl called for non-SG_IO "
+                     "ioctl");
+        return NULL;
+    }
+
+    acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+
+    acb->iscsilun = iscsilun;
+    acb->canceled    = 0;
+    acb->buf         = NULL;
+    acb->ioh         = buf;
+
+    acb->task = malloc(sizeof(struct scsi_task));
+    if (acb->task == NULL) {
+        error_report("iSCSI: Failed to allocate task for scsi command. %s",
+                     iscsi_get_error(iscsi));
+        qemu_aio_release(acb);
+        return NULL;
+    }
+    memset(acb->task, 0, sizeof(struct scsi_task));
+
+    switch (acb->ioh->dxfer_direction) {
+    case SG_DXFER_TO_DEV:
+        acb->task->xfer_dir = SCSI_XFER_WRITE;
+        break;
+    case SG_DXFER_FROM_DEV:
+        acb->task->xfer_dir = SCSI_XFER_READ;
+        break;
+    default:
+        acb->task->xfer_dir = SCSI_XFER_NONE;
+        break;
+    }
+
+    acb->task->cdb_size = acb->ioh->cmd_len;
+    memcpy(&acb->task->cdb[0], acb->ioh->cmdp, acb->ioh->cmd_len);
+    acb->task->expxferlen = acb->ioh->dxfer_len;
+
+    if (acb->task->xfer_dir == SCSI_XFER_WRITE) {
+        data.data = acb->ioh->dxferp;
+        data.size = acb->ioh->dxfer_len;
+    }
+    if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task,
+                                 iscsi_aio_ioctl_cb,
+                                 (acb->task->xfer_dir == SCSI_XFER_WRITE) ?
+                                     &data : NULL,
+                                 acb) != 0) {
+        scsi_free_scsi_task(acb->task);
+        qemu_aio_release(acb);
+        return NULL;
+    }
+
+    /* tell libiscsi to read straight into the buffer we got from ioctl */
+    if (acb->task->xfer_dir == SCSI_XFER_READ) {
+        scsi_task_add_data_in_buffer(acb->task,
+                                     acb->ioh->dxfer_len,
+                                     acb->ioh->dxferp);
+    }
+
+    iscsi_set_events(iscsilun);
+
+    return &acb->common;
+}
+
+static int iscsi_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
+{
+    IscsiLun *iscsilun = bs->opaque;
+
+    switch (req) {
+    case SG_GET_VERSION_NUM:
+        *(int *)buf = 30000;
+        break;
+    case SG_GET_SCSI_ID:
+        ((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type;
+        break;
+    default:
+        return -1;
+    }
+    return 0;
+}
+#endif
+
 static int64_t
 iscsi_getlength(BlockDriverState *bs)
 {
@@ -575,18 +717,33 @@ iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status,
 }
 
 static void
-iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
+iscsi_inquiry_cb(struct iscsi_context *iscsi, int status, void *command_data,
                  void *opaque)
 {
     struct IscsiTask *itask = opaque;
-    struct scsi_task *task;
+    struct scsi_task *task = command_data;
+    struct scsi_inquiry_standard *inq;
 
     if (status != 0) {
         itask->status   = 1;
         itask->complete = 1;
+        scsi_free_scsi_task(task);
+        return;
+    }
+
+    inq = scsi_datain_unmarshall(task);
+    if (inq == NULL) {
+        error_report("iSCSI: Failed to unmarshall inquiry data.");
+        itask->status   = 1;
+        itask->complete = 1;
+        scsi_free_scsi_task(task);
         return;
     }
 
+    itask->iscsilun->type = inq->periperal_device_type;
+
+    scsi_free_scsi_task(task);
+
     task = iscsi_readcapacity16_task(iscsi, itask->iscsilun->lun,
                                    iscsi_readcapacity16_cb, opaque);
     if (task == NULL) {
@@ -597,6 +754,30 @@ iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
     }
 }
 
+static void
+iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
+                 void *opaque)
+{
+    struct IscsiTask *itask = opaque;
+    struct scsi_task *task;
+
+    if (status != 0) {
+        itask->status   = 1;
+        itask->complete = 1;
+        return;
+    }
+
+    task = iscsi_inquiry_task(iscsi, itask->iscsilun->lun,
+                              0, 0, 36,
+                              iscsi_inquiry_cb, opaque);
+    if (task == NULL) {
+        error_report("iSCSI: failed to send inquiry command.");
+        itask->status   = 1;
+        itask->complete = 1;
+        return;
+    }
+}
+
 static int parse_chap(struct iscsi_context *iscsi, const char *target)
 {
     QemuOptsList *list;
@@ -803,6 +984,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
     if (iscsi_url != NULL) {
         iscsi_destroy_url(iscsi_url);
     }
+    bs->iscsi = 1;
     return 0;
 
 failed:
@@ -846,6 +1028,11 @@ static BlockDriver bdrv_iscsi = {
     .bdrv_aio_flush  = iscsi_aio_flush,
 
     .bdrv_aio_discard = iscsi_aio_discard,
+
+#ifdef __linux__
+    .bdrv_ioctl       = iscsi_ioctl,
+    .bdrv_aio_ioctl   = iscsi_aio_ioctl,
+#endif
 };
 
 static void iscsi_block_init(void)
diff --git a/block/raw.c b/block/raw.c
index 7086e31..62fc8c6 100644
--- a/block/raw.c
+++ b/block/raw.c
@@ -6,6 +6,7 @@
 static int raw_open(BlockDriverState *bs, int flags)
 {
     bs->sg = bs->file->sg;
+    bs->iscsi = bs->file->iscsi;
     return 0;
 }
 
diff --git a/block_int.h b/block_int.h
index b80e66d..a6a9a1a 100644
--- a/block_int.h
+++ b/block_int.h
@@ -269,6 +269,7 @@ struct BlockDriverState {
     int encrypted; /* if true, the media is encrypted */
     int valid_key; /* if true, a valid encryption key has been set */
     int sg;        /* if true, the device is a /dev/sg* */
+    int iscsi;     /* if true, the device is a iscsi://... */
     int copy_on_read; /* if true, copy read backing sectors into image
                          note this is a reference count */
 
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 8ab9bcd..1466b1d 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -199,7 +199,12 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
     const char *driver;
     DeviceState *dev;
 
-    driver = bdrv_is_sg(bdrv) ? "scsi-generic" : "scsi-disk";
+    if (bdrv_is_sg(bdrv) || bdrv_is_iscsi(bdrv)) {
+        driver = "scsi-generic";
+    } else {
+        driver = "scsi-disk";
+    }
+
     dev = qdev_create(&bus->qbus, driver);
     qdev_prop_set_uint32(dev, "scsi-id", unit);
     if (bootindex >= 0) {
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 045c764..925dbcf 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -1711,6 +1711,10 @@ static int scsi_initfn(SCSIDevice *dev)
         error_report("unwanted /dev/sg*");
         return -1;
     }
+    if (bdrv_is_iscsi(s->qdev.conf.bs)) {
+        error_report("unwanted iscsi://*");
+        return -1;
+    }
 
     if (s->features & (1 << SCSI_DISK_F_REMOVABLE)) {
         bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_cd_block_ops, s);
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index d856d23..dfc5c54 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -401,8 +401,8 @@ static int scsi_generic_initfn(SCSIDevice *s)
     }
 
     /* check we are really using a /dev/sg* file */
-    if (!bdrv_is_sg(s->conf.bs)) {
-        error_report("not /dev/sg*");
+    if (!bdrv_is_sg(s->conf.bs) && !bdrv_is_iscsi(s->conf.bs)) {
+        error_report("not /dev/sg* or iscsi://");
         return -1;
     }
 
