Patchwork usb-msd: Add usb-storage, removable=on|off property

login
register
mail settings
Submitter Stefan Hajnoczi
Date Jan. 15, 2011, midnight
Message ID <1295049643-23443-1-git-send-email-stefanha@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/79020/
State New
Headers show

Comments

Stefan Hajnoczi - Jan. 15, 2011, midnight
USB Mass Storage Devices sometimes have the RMB (removable) bit set in
the SCSI INQUIRY response.  Thumbdrives tend to have the bit set whereas
hard disks do not.

Operating systems differentiate between removable devices and fixed
devices.  Under Linux, the anaconda installer looks for removable
devices.  Under Windows, only fixed devices may have more than one
partition and AutoRun is also affected by the removable bit.

For these reasons, allow USB Mass Storage Devices to override the
removable bit:

qemu -usb
     -drive if=none,file=test.img,cache=none,id=disk0
     -device usb-storage,drive=disk0,removable=on

The default is off.

Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
---
 hw/scsi-disk.c |    6 +++++-
 hw/usb-msd.c   |   19 +++++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletions(-)
Christoph Hellwig - Jan. 17, 2011, 9:21 a.m.
The actual removable bit looks fine, but I don't think the connection of
the change callback looks sane.  What's the rationale for it?
Stefan Hajnoczi - Jan. 17, 2011, 10:04 a.m.
On Mon, Jan 17, 2011 at 10:21:26AM +0100, Christoph Hellwig wrote:
> The actual removable bit looks fine, but I don't think the connection of
> the change callback looks sane.  What's the rationale for it?

Since we're using bdrv_set_removable(), the user may try to eject the
block device from the QEMU monitor.  At that point we have a closed
BlockDriverState and all operations will (at best) error since there is
no medium.

The callback removes the USB MSD so that eject is equivalent to removing
the device.  It's a hack and we could remove it, but then we're left
with a weird guest-visible state that you can't get into with a physical
USB thumbdrive.

I was considering not using bdrv_set_removable() and instead adding a
hint to the BlockDriverState which gets checked when constructing the
SCSI INQUIRY response.  If we take that approach, then QEMU doesn't
consider the block device removable in the eject/change medium sense.

Stefan
Christoph Hellwig - Jan. 17, 2011, 4:01 p.m.
On Mon, Jan 17, 2011 at 10:04:05AM +0000, Stefan Hajnoczi wrote:
> I was considering not using bdrv_set_removable() and instead adding a
> hint to the BlockDriverState which gets checked when constructing the
> SCSI INQUIRY response.  If we take that approach, then QEMU doesn't
> consider the block device removable in the eject/change medium sense.

I think that's the better approach.  What about moving the removable
qdev property up from the usb driver into the scsi-disk driver?  That's
where it logically belongs, and avoids the need to add another hint.
Stefan Hajnoczi - Jan. 17, 2011, 6:08 p.m.
On Mon, Jan 17, 2011 at 4:01 PM, Christoph Hellwig <hch@lst.de> wrote:
> On Mon, Jan 17, 2011 at 10:04:05AM +0000, Stefan Hajnoczi wrote:
>> I was considering not using bdrv_set_removable() and instead adding a
>> hint to the BlockDriverState which gets checked when constructing the
>> SCSI INQUIRY response.  If we take that approach, then QEMU doesn't
>> consider the block device removable in the eject/change medium sense.
>
> I think that's the better approach.  What about moving the removable
> qdev property up from the usb driver into the scsi-disk driver?  That's
> where it logically belongs, and avoids the need to add another hint.

That could work although the plumbing to get us to the point where a
scsi-disk qdev is created isn't very pretty either.  I'll try adding a
removable property and setting it in a sane way.

Stefan

Patch

diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 6cb317c..39c1279 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -548,7 +548,6 @@  static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
 
     if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
         outbuf[0] = 5;
-        outbuf[1] = 0x80;
         memcpy(&outbuf[16], "QEMU CD-ROM     ", 16);
     } else {
         outbuf[0] = 0;
@@ -557,6 +556,11 @@  static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
     memcpy(&outbuf[8], "QEMU    ", 8);
     memset(&outbuf[32], 0, 4);
     memcpy(&outbuf[32], s->version, MIN(4, strlen(s->version)));
+
+    if (bdrv_is_removable(s->bs)) {
+        outbuf[1] |= 0x80;
+    }
+
     /*
      * We claim conformance to SPC-3, which is required for guests
      * to ask for modern features like READ CAPACITY(16) or the
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 0a95d8d..2dce9df 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -50,6 +50,7 @@  typedef struct {
     SCSIBus bus;
     BlockConf conf;
     SCSIDevice *scsi_dev;
+    uint32_t removable;
     int result;
     /* For async completion.  */
     USBPacket *packet;
@@ -520,6 +521,13 @@  static void usb_msd_password_cb(void *opaque, int err)
         qdev_unplug(&s->dev.qdev);
 }
 
+static void usb_msd_change_cb(void *opaque)
+{
+    MSDState *s = opaque;
+
+    qdev_unplug(&s->dev.qdev);
+}
+
 static int usb_msd_initfn(USBDevice *dev)
 {
     MSDState *s = DO_UPCAST(MSDState, dev, dev);
@@ -548,6 +556,16 @@  static int usb_msd_initfn(USBDevice *dev)
     if (!s->scsi_dev) {
         return -1;
     }
+
+    /*
+     * Allow overriding the SCSI INQUIRY removable (RMB) bit for hard disks.
+     * USB thumbdrives tend report removable media but USB hard disks do not.
+     */
+    if (s->removable && bdrv_get_type_hint(bs) == BDRV_TYPE_HD) {
+        bdrv_set_change_cb(bs, usb_msd_change_cb, s);
+        bdrv_set_removable(bs, 1);
+    }
+
     s->bus.qbus.allow_hotplug = 0;
     usb_msd_handle_reset(dev);
 
@@ -634,6 +652,7 @@  static struct USBDeviceInfo msd_info = {
     .usbdevice_init = usb_msd_init,
     .qdev.props     = (Property[]) {
         DEFINE_BLOCK_PROPERTIES(MSDState, conf),
+        DEFINE_PROP_BIT("removable", MSDState, removable, 1, false),
         DEFINE_PROP_END_OF_LIST(),
     },
 };