Merge remote-tracking branch 'bonzini/scsi-next' into staging
authorAnthony Liguori <aliguori@us.ibm.com>
Wed, 23 Jan 2013 15:08:54 +0000 (09:08 -0600)
committerAnthony Liguori <aliguori@us.ibm.com>
Wed, 23 Jan 2013 15:08:54 +0000 (09:08 -0600)
# By Peter Lieven (3) and others
# Via Paolo Bonzini
* bonzini/scsi-next:
  scsi: Drop useless null test in scsi_unit_attention()
  lsi: use qbus_reset_all to reset SCSI bus
  scsi: fix segfault with 0-byte disk
  iscsi: add support for iSCSI NOPs [v2]
  iscsi: partly avoid iovec linearization in iscsi_aio_writev
  iscsi: add iscsi_create support

1  2 
block/iscsi.c
hw/scsi-disk.c

diff --combined block/iscsi.c
index f08cf9663b4dc8b4c92eb976d68264f4fcd610bf,249778986d784d0543c63c560a6ea72fa19f0315..fd54a1550ea0d5edbfcaeb6cdc715d9f3e678559
@@@ -48,6 -48,7 +48,7 @@@ typedef struct IscsiLun 
      int block_size;
      uint64_t num_blocks;
      int events;
+     QEMUTimer *nop_timer;
  } IscsiLun;
  
  typedef struct IscsiAIOCB {
@@@ -66,6 -67,9 +67,9 @@@
  #endif
  } IscsiAIOCB;
  
+ #define NOP_INTERVAL 5000
+ #define MAX_NOP_FAILURES 3
  static void
  iscsi_bh_cb(void *p)
  {
@@@ -241,8 -245,17 +245,17 @@@ iscsi_aio_writev(BlockDriverState *bs, 
      /* XXX we should pass the iovec to write16 to avoid the extra copy */
      /* this will allow us to get rid of 'buf' completely */
      size = nb_sectors * BDRV_SECTOR_SIZE;
-     acb->buf = g_malloc(size);
-     qemu_iovec_to_buf(acb->qiov, 0, acb->buf, size);
+     data.size = MIN(size, acb->qiov->size);
+     /* if the iovec only contains one buffer we can pass it directly */
+     if (acb->qiov->niov == 1) {
+         acb->buf = NULL;
+         data.data = acb->qiov->iov[0].iov_base;
+     } else {
+         acb->buf = g_malloc(data.size);
+         qemu_iovec_to_buf(acb->qiov, 0, acb->buf, data.size);
+         data.data = acb->buf;
+     }
  
      acb->task = malloc(sizeof(struct scsi_task));
      if (acb->task == NULL) {
      *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
      acb->task->expxferlen = size;
  
-     data.data = acb->buf;
-     data.size = size;
      if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task,
                                   iscsi_aio_write16_cb,
                                   &data,
@@@ -762,6 -772,26 +772,26 @@@ static char *parse_initiator_name(cons
      }
  }
  
+ #if defined(LIBISCSI_FEATURE_NOP_COUNTER)
+ static void iscsi_nop_timed_event(void *opaque)
+ {
+     IscsiLun *iscsilun = opaque;
+     if (iscsi_get_nops_in_flight(iscsilun->iscsi) > MAX_NOP_FAILURES) {
+         error_report("iSCSI: NOP timeout. Reconnecting...");
+         iscsi_reconnect(iscsilun->iscsi);
+     }
+     if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) {
+         error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages.");
+         return;
+     }
+     qemu_mod_timer(iscsilun->nop_timer, qemu_get_clock_ms(rt_clock) + NOP_INTERVAL);
+     iscsi_set_events(iscsilun);
+ }
+ #endif
  /*
   * We support iscsi url's on the form
   * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
@@@ -922,6 -952,12 +952,12 @@@ static int iscsi_open(BlockDriverState 
  
      ret = 0;
  
+ #if defined(LIBISCSI_FEATURE_NOP_COUNTER)
+     /* Set up a timer for sending out iSCSI NOPs */
+     iscsilun->nop_timer = qemu_new_timer_ms(rt_clock, iscsi_nop_timed_event, iscsilun);
+     qemu_mod_timer(iscsilun->nop_timer, qemu_get_clock_ms(rt_clock) + NOP_INTERVAL);
+ #endif
  out:
      if (initiator_name != NULL) {
          g_free(initiator_name);
@@@ -947,6 -983,10 +983,10 @@@ static void iscsi_close(BlockDriverStat
      IscsiLun *iscsilun = bs->opaque;
      struct iscsi_context *iscsi = iscsilun->iscsi;
  
+     if (iscsilun->nop_timer) {
+         qemu_del_timer(iscsilun->nop_timer);
+         qemu_free_timer(iscsilun->nop_timer);
+     }
      qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL);
      iscsi_destroy_context(iscsi);
      memset(iscsilun, 0, sizeof(IscsiLun));
@@@ -957,6 -997,60 +997,60 @@@ static int iscsi_has_zero_init(BlockDri
      return 0;
  }
  
+ static int iscsi_create(const char *filename, QEMUOptionParameter *options)
+ {
+     int ret = 0;
+     int64_t total_size = 0;
+     BlockDriverState bs;
+     IscsiLun *iscsilun = NULL;
+     memset(&bs, 0, sizeof(BlockDriverState));
+     /* Read out options */
+     while (options && options->name) {
+         if (!strcmp(options->name, "size")) {
+             total_size = options->value.n / BDRV_SECTOR_SIZE;
+         }
+         options++;
+     }
+     bs.opaque = g_malloc0(sizeof(struct IscsiLun));
+     iscsilun = bs.opaque;
+     ret = iscsi_open(&bs, filename, 0);
+     if (ret != 0) {
+         goto out;
+     }
+     if (iscsilun->nop_timer) {
+         qemu_del_timer(iscsilun->nop_timer);
+         qemu_free_timer(iscsilun->nop_timer);
+     }
+     if (iscsilun->type != TYPE_DISK) {
+         ret = -ENODEV;
+         goto out;
+     }
+     if (bs.total_sectors < total_size) {
+         ret = -ENOSPC;
+     }
+     ret = 0;
+ out:
+     if (iscsilun->iscsi != NULL) {
+         iscsi_destroy_context(iscsilun->iscsi);
+     }
+     g_free(bs.opaque);
+     return ret;
+ }
+ static QEMUOptionParameter iscsi_create_options[] = {
+     {
+         .name = BLOCK_OPT_SIZE,
+         .type = OPT_SIZE,
+         .help = "Virtual disk size"
+     },
+     { NULL }
+ };
  static BlockDriver bdrv_iscsi = {
      .format_name     = "iscsi",
      .protocol_name   = "iscsi",
      .instance_size   = sizeof(IscsiLun),
      .bdrv_file_open  = iscsi_open,
      .bdrv_close      = iscsi_close,
+     .bdrv_create     = iscsi_create,
+     .create_options  = iscsi_create_options,
  
      .bdrv_getlength  = iscsi_getlength,
  
  #endif
  };
  
 +static QemuOptsList qemu_iscsi_opts = {
 +    .name = "iscsi",
 +    .head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head),
 +    .desc = {
 +        {
 +            .name = "user",
 +            .type = QEMU_OPT_STRING,
 +            .help = "username for CHAP authentication to target",
 +        },{
 +            .name = "password",
 +            .type = QEMU_OPT_STRING,
 +            .help = "password for CHAP authentication to target",
 +        },{
 +            .name = "header-digest",
 +            .type = QEMU_OPT_STRING,
 +            .help = "HeaderDigest setting. "
 +                    "{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}",
 +        },{
 +            .name = "initiator-name",
 +            .type = QEMU_OPT_STRING,
 +            .help = "Initiator iqn name to use when connecting",
 +        },
 +        { /* end of list */ }
 +    },
 +};
 +
  static void iscsi_block_init(void)
  {
      bdrv_register(&bdrv_iscsi);
 +    qemu_add_opts(&qemu_iscsi_opts);
  }
  
  block_init(iscsi_block_init);
diff --combined hw/scsi-disk.c
index 96db9a73c7ac5b57f9aed80bf96b2c32289b8a68,658e31566006da6c48a05c90148effcbd6595019..28e75bbf5bc73f3a629d4176f331474f6f5271f4
@@@ -85,7 -85,9 +85,7 @@@ static void scsi_free_request(SCSIReque
  {
      SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
  
 -    if (r->iov.iov_base) {
 -        qemu_vfree(r->iov.iov_base);
 -    }
 +    qemu_vfree(r->iov.iov_base);
  }
  
  /* Helper function for command completion with sense.  */
@@@ -1680,7 -1682,7 +1680,7 @@@ static int32_t scsi_disk_emulate_comman
          bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
          if (!nb_sectors) {
              scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
-             return -1;
+             return 0;
          }
          if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) {
              goto illegal_request;
              bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
              if (!nb_sectors) {
                  scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
-                 return -1;
+                 return 0;
              }
              if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) {
                  goto illegal_request;