From patchwork Fri Dec 3 11:09:47 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: ronnie sahlberg X-Patchwork-Id: 74123 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 2A00DB70B0 for ; Fri, 3 Dec 2010 22:47:32 +1100 (EST) Received: from localhost ([127.0.0.1]:40892 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1POTjP-00065N-O4 for incoming@patchwork.ozlabs.org; Fri, 03 Dec 2010 06:23:15 -0500 Received: from [140.186.70.92] (port=59040 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1POTY6-0001gQ-U3 for qemu-devel@nongnu.org; Fri, 03 Dec 2010 06:11:37 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1POTY4-0002jL-QM for qemu-devel@nongnu.org; Fri, 03 Dec 2010 06:11:34 -0500 Received: from mail-gy0-f173.google.com ([209.85.160.173]:62863) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1POTY4-0002f4-JC for qemu-devel@nongnu.org; Fri, 03 Dec 2010 06:11:32 -0500 Received: by mail-gy0-f173.google.com with SMTP id 5so5066682gye.4 for ; Fri, 03 Dec 2010 03:11:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:received:from:to:cc:subject :date:message-id:x-mailer:in-reply-to:references; bh=x0Hqbx6p3onhYMWO0W/WJlNQuJ1DAyd89v3uUwJLols=; b=a//2LFSR4jVCcs0E4iZ8I20SgFAXjPBrKiPQ26e8+dDQpha0TlMaBmFC+mh/2K3qO9 4f9oJrvj9Kzzl7kqZmGmztSWSqY5LXDU/pY66xVJtp/CzHUs0/tceSO7UP/h9HPL4Dii qn3xAu82+oVbbiURi/3HjZ1axgjqI4F97wFPM= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; b=WQWJ8GCcj1Pk3on1lVumt7Kf9iNHsyr1fLqzzmhfgDZe/4/SKFyuaLQp2aAjW9oVPh 8JGkKOG3Z/ClxJ9v5yioXEXcyIIqM2UEnTMMrGszwFBcv1oyCW1Hkc2int6IAxEGwZqr g9j6j3JFItP0Tk4Zd5dL1n3GuFh2i0SFeKnKI= Received: by 10.42.171.9 with SMTP id h9mr148017icz.235.1291374690605; Fri, 03 Dec 2010 03:11:30 -0800 (PST) Received: from ronniesahlberg@gmail.com (CPE-121-216-183-74.lnse2.ken.bigpond.net.au [121.216.183.74]) by mx.google.com with ESMTPS id i16sm1482494ibl.18.2010.12.03.03.11.26 (version=TLSv1/SSLv3 cipher=RC4-MD5); Fri, 03 Dec 2010 03:11:30 -0800 (PST) Received: by ronniesahlberg@gmail.com (sSMTP sendmail emulation); Fri, 03 Dec 2010 22:11:02 +1100 From: ronniesahlberg@gmail.com To: qemu-devel@nongnu.org Date: Fri, 3 Dec 2010 22:09:47 +1100 Message-Id: <1291374593-17448-9-git-send-email-ronniesahlberg@gmail.com> X-Mailer: git-send-email 1.7.3.1 In-Reply-To: <1291374593-17448-1-git-send-email-ronniesahlberg@gmail.com> References: <1291374593-17448-1-git-send-email-ronniesahlberg@gmail.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 2) Cc: Ronnie Sahlberg Subject: [Qemu-devel] [PATCH 08/14] ./block/iscsi/sscsi-command.c X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Ronnie Sahlberg iscsi client library : scsi-command.c This file contains functions for providing a fully async and fully nonblocking interface to send scsi commands to a target and handle replies coming back. This layer is fully async and nonblocking. Client applications can send and keep arbitrary number of commands in flight and will be notified of the scsi command completions through a callback mechanism. ... ./block/iscsi/ contains a copy of a general purpose iscsi client library which is aimed at providing a clientside api for iscsi for both qemu/kvm as well as otther scsi related utilities. As such, there is need to make merging across various consumers, qemu/kvm being one of many here, as easy as possible when features are added to the library. As such, no consumer/qemu specific code is used in this library as well as coding guidelined might not be adhered to 100% It is the intention that this library will be useful for many and that iscsi use spawned from this will flourish. Signed-off-by: Ronnie Sahlberg --- block/iscsi/scsi-command.c | 528 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 528 insertions(+), 0 deletions(-) create mode 100644 block/iscsi/scsi-command.c diff --git a/block/iscsi/scsi-command.c b/block/iscsi/scsi-command.c new file mode 100644 index 0000000..f699c3f --- /dev/null +++ b/block/iscsi/scsi-command.c @@ -0,0 +1,528 @@ +/* + Copyright (C) 2010 by Ronnie Sahlberg + + 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, see . +*/ + +#include +#include +#include +#include +#include "iscsi.h" +#include "iscsi-private.h" +#include "scsi-lowlevel.h" + +struct iscsi_scsi_cbdata { + struct iscsi_scsi_cbdata *prev, *next; + iscsi_command_cb callback; + void *private_data; + struct scsi_task *task; +}; + +void +iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata) +{ + if (scsi_cbdata == NULL) { + return; + } + if (scsi_cbdata->task != NULL) { + scsi_free_scsi_task(scsi_cbdata->task); + scsi_cbdata->task = NULL; + } + free(scsi_cbdata); +} + +void +iscsi_cbdata_steal_scsi_task(struct scsi_task *task) +{ + struct iscsi_scsi_cbdata *scsi_cbdata = + scsi_get_task_private_ptr(task); + + if (scsi_cbdata != NULL) { + scsi_cbdata->task = NULL; + } +} + +static void +iscsi_scsi_response_cb(struct iscsi_context *iscsi, int status, + void *command_data, void *private_data) +{ + struct iscsi_scsi_cbdata *scsi_cbdata = + (struct iscsi_scsi_cbdata *)private_data; + struct scsi_task *task = command_data; + + switch (status) { + case SCSI_STATUS_GOOD: + scsi_cbdata->callback(iscsi, SCSI_STATUS_GOOD, task, + scsi_cbdata->private_data); + return; + case SCSI_STATUS_CHECK_CONDITION: + scsi_cbdata->callback(iscsi, SCSI_STATUS_CHECK_CONDITION, task, + scsi_cbdata->private_data); + return; + default: + iscsi_set_error(iscsi, "Cant handle scsi status %d yet.", + status); + scsi_cbdata->callback(iscsi, SCSI_STATUS_ERROR, task, + scsi_cbdata->private_data); + } +} + + +int +iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, + struct scsi_task *task, iscsi_command_cb cb, + struct iscsi_data *data, void *private_data) +{ + struct iscsi_pdu *pdu; + struct iscsi_scsi_cbdata *scsi_cbdata; + int flags; + + if (iscsi->session_type != ISCSI_SESSION_NORMAL) { + iscsi_set_error(iscsi, "Trying to send command on " + "discovery session."); + scsi_free_scsi_task(task); + return -1; + } + + if (iscsi->is_loggedin == 0) { + iscsi_set_error(iscsi, "Trying to send command while " + "not logged in."); + scsi_free_scsi_task(task); + return -2; + } + + scsi_cbdata = malloc(sizeof(struct iscsi_scsi_cbdata)); + if (scsi_cbdata == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: failed to allocate " + "scsi cbdata."); + scsi_free_scsi_task(task); + return -3; + } + bzero(scsi_cbdata, sizeof(struct iscsi_scsi_cbdata)); + scsi_cbdata->task = task; + scsi_cbdata->callback = cb; + scsi_cbdata->private_data = private_data; + + scsi_set_task_private_ptr(task, scsi_cbdata); + + pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_SCSI_REQUEST, + ISCSI_PDU_SCSI_RESPONSE); + if (pdu == NULL) { + iscsi_set_error(iscsi, "Out-of-memory, Failed to allocate " + "scsi pdu."); + iscsi_free_scsi_cbdata(scsi_cbdata); + return -4; + } + pdu->scsi_cbdata = scsi_cbdata; + + /* flags */ + flags = ISCSI_PDU_SCSI_FINAL|ISCSI_PDU_SCSI_ATTR_SIMPLE; + switch (task->xfer_dir) { + case SCSI_XFER_NONE: + break; + case SCSI_XFER_READ: + flags |= ISCSI_PDU_SCSI_READ; + break; + case SCSI_XFER_WRITE: + flags |= ISCSI_PDU_SCSI_WRITE; + if (data == NULL) { + iscsi_set_error(iscsi, "DATA-OUT command but data " + "== NULL."); + iscsi_free_pdu(iscsi, pdu); + return -5; + } + if (data->size != task->expxferlen) { + iscsi_set_error(iscsi, "Data size:%d is not same as " + "expected data transfer " + "length:%d.", data->size, + task->expxferlen); + iscsi_free_pdu(iscsi, pdu); + return -6; + } + if (iscsi_pdu_add_data(iscsi, pdu, data->data, data->size) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to " + "add outdata to the pdu."); + iscsi_free_pdu(iscsi, pdu); + return -7; + } + + break; + } + iscsi_pdu_set_pduflags(pdu, flags); + + /* lun */ + iscsi_pdu_set_lun(pdu, lun); + + /* expxferlen */ + iscsi_pdu_set_expxferlen(pdu, task->expxferlen); + + /* cmdsn */ + iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn); + pdu->cmdsn = iscsi->cmdsn; + iscsi->cmdsn++; + + /* exp statsn */ + iscsi_pdu_set_expstatsn(pdu, iscsi->statsn+1); + + /* cdb */ + iscsi_pdu_set_cdb(pdu, task); + + pdu->callback = iscsi_scsi_response_cb; + pdu->private_data = scsi_cbdata; + + if (iscsi_queue_pdu(iscsi, pdu) != 0) { + iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi " + "scsi pdu."); + iscsi_free_pdu(iscsi, pdu); + return -8; + } + + return 0; +} + + +int +iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, + const unsigned char *hdr, int size) +{ + int statsn, flags, response, status; + struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata; + struct scsi_task *task = scsi_cbdata->task; + + statsn = ntohl(*(uint32_t *)&hdr[24]); + if (statsn > (int)iscsi->statsn) { + iscsi->statsn = statsn; + } + + flags = hdr[1]; + if ((flags&ISCSI_PDU_DATA_FINAL) == 0) { + iscsi_set_error(iscsi, "scsi response pdu but Final bit is " + "not set: 0x%02x.", flags); + pdu->callback(iscsi, SCSI_STATUS_ERROR, task, + pdu->private_data); + return -1; + } + if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) { + iscsi_set_error(iscsi, "scsi response asked for ACK " + "0x%02x.", flags); + pdu->callback(iscsi, SCSI_STATUS_ERROR, task, + pdu->private_data); + return -1; + } + + response = hdr[2]; + + status = hdr[3]; + + switch (status) { + case SCSI_STATUS_GOOD: + task->datain.data = pdu->indata.data; + task->datain.size = pdu->indata.size; + + pdu->indata.data = NULL; + pdu->indata.size = 0; + + pdu->callback(iscsi, SCSI_STATUS_GOOD, task, + pdu->private_data); + break; + case SCSI_STATUS_CHECK_CONDITION: + task->datain.size = size - ISCSI_HEADER_SIZE; + task->datain.data = malloc(task->datain.size); + if (task->datain.data == NULL) { + iscsi_set_error(iscsi, "failed to allocate blob for " + "sense data"); + } + memcpy(task->datain.data, hdr + ISCSI_HEADER_SIZE, + task->datain.size); + + task->sense.error_type = task->datain.data[2] & 0x7f; + task->sense.key = task->datain.data[4] & 0x0f; + task->sense.ascq = ntohs(*(uint16_t *) + &(task->datain.data[14])); + + iscsi_set_error(iscsi, "SENSE KEY:%s(%d) ASCQ:%s(0x%04x)", + scsi_sense_key_str(task->sense.key), + task->sense.key, + scsi_sense_ascq_str(task->sense.ascq), + task->sense.ascq); + pdu->callback(iscsi, SCSI_STATUS_CHECK_CONDITION, task, + pdu->private_data); + break; + default: + iscsi_set_error(iscsi, "Unknown SCSI status :%d.", status); + + pdu->callback(iscsi, SCSI_STATUS_ERROR, task, + pdu->private_data); + return -1; + } + + return 0; +} + +int +iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, + const unsigned char *hdr, int size, int *is_finished) +{ + int statsn, flags, status; + struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata; + struct scsi_task *task = scsi_cbdata->task; + int dsl; + + statsn = ntohl(*(uint32_t *)&hdr[24]); + if (statsn > (int)iscsi->statsn) { + iscsi->statsn = statsn; + } + + flags = hdr[1]; + if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) { + iscsi_set_error(iscsi, "scsi response asked for ACK " + "0x%02x.", flags); + pdu->callback(iscsi, SCSI_STATUS_ERROR, task, + pdu->private_data); + return -1; + } + dsl = ntohl(*(uint32_t *)&hdr[4])&0x00ffffff; + + if (iscsi_add_data(iscsi, &pdu->indata, + discard_const(hdr + ISCSI_HEADER_SIZE), dsl, 0) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: failed to add data " + "to pdu in buffer."); + return -2; + } + + + if ((flags&ISCSI_PDU_DATA_FINAL) == 0) { + *is_finished = 0; + } + if ((flags&ISCSI_PDU_DATA_CONTAINS_STATUS) == 0) { + *is_finished = 0; + } + + if (*is_finished == 0) { + return 0; + } + + + /* this was the final data-in packet in the sequence and it has + * the s-bit set, so invoke the callback. + */ + status = hdr[3]; + task->datain.data = pdu->indata.data; + task->datain.size = pdu->indata.size; + + pdu->indata.data = NULL; + pdu->indata.size = 0; + + pdu->callback(iscsi, status, task, pdu->private_data); + + return 0; +} + + + + +/* + * SCSI commands + */ + +int +iscsi_testunitready_async(struct iscsi_context *iscsi, int lun, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + int ret; + + task = scsi_cdb_testunitready(); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "testunitready cdb."); + return -1; + } + ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, + private_data); + + return ret; +} + + +int +iscsi_reportluns_async(struct iscsi_context *iscsi, int report_type, + int alloc_len, iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + int ret; + + if (alloc_len < 16) { + iscsi_set_error(iscsi, "Minimum allowed alloc len for " + "reportluns is 16. You specified %d.", + alloc_len); + return -1; + } + + task = scsi_reportluns_cdb(report_type, alloc_len); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "reportluns cdb."); + return -2; + } + /* report luns are always sent to lun 0 */ + ret = iscsi_scsi_command_async(iscsi, 0, task, cb, NULL, + private_data); + + return ret; +} + +int +iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, int evpd, + int page_code, int maxsize, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + int ret; + + task = scsi_cdb_inquiry(evpd, page_code, maxsize); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "inquiry cdb."); + return -1; + } + ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, + private_data); + + return ret; +} + +int +iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, int lba, + int pmi, iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + int ret; + + task = scsi_cdb_readcapacity10(lba, pmi); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "readcapacity10 cdb."); + return -1; + } + ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, + private_data); + + return ret; +} + +int +iscsi_read10_async(struct iscsi_context *iscsi, int lun, int lba, + int datalen, int blocksize, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + int ret; + + if (datalen % blocksize != 0) { + iscsi_set_error(iscsi, "Datalen:%d is not a multiple of " + "the blocksize:%d.", datalen, blocksize); + return -1; + } + + task = scsi_cdb_read10(lba, datalen, blocksize); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "read10 cdb."); + return -2; + } + ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, + private_data); + + return ret; +} + + +int +iscsi_write10_async(struct iscsi_context *iscsi, int lun, unsigned char *data, + int datalen, int lba, int fua, int fuanv, int blocksize, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + struct iscsi_data outdata; + int ret; + + if (datalen % blocksize != 0) { + iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " + "blocksize:%d.", datalen, blocksize); + return -1; + } + + task = scsi_cdb_write10(lba, datalen, fua, fuanv, blocksize); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "read10 cdb."); + return -2; + } + + outdata.data = data; + outdata.size = datalen; + + ret = iscsi_scsi_command_async(iscsi, lun, task, cb, &outdata, + private_data); + + return ret; +} + +int +iscsi_modesense6_async(struct iscsi_context *iscsi, int lun, int dbd, int pc, + int page_code, int sub_page_code, + unsigned char alloc_len, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + int ret; + + task = scsi_cdb_modesense6(dbd, pc, page_code, sub_page_code, + alloc_len); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "modesense6 cdb."); + return -2; + } + ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, + private_data); + + return ret; +} + +int +iscsi_synchronizecache10_async(struct iscsi_context *iscsi, int lun, int lba, + int num_blocks, int syncnv, int immed, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + int ret; + + task = scsi_cdb_synchronizecache10(lba, num_blocks, syncnv, + immed); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "synchronizecache10 cdb."); + return -1; + } + ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, + private_data); + + return ret; +} +