From patchwork Fri Dec 3 11:09:46 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: ronnie sahlberg X-Patchwork-Id: 74122 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 1ADBDB70E8 for ; Fri, 3 Dec 2010 22:45:52 +1100 (EST) Received: from localhost ([127.0.0.1]:40879 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1POTkp-00064l-8g for incoming@patchwork.ozlabs.org; Fri, 03 Dec 2010 06:24:43 -0500 Received: from [140.186.70.92] (port=58933 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1POTXw-0001ZZ-0k for qemu-devel@nongnu.org; Fri, 03 Dec 2010 06:11:26 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1POTXu-0002hd-FC for qemu-devel@nongnu.org; Fri, 03 Dec 2010 06:11:23 -0500 Received: from mail-yx0-f173.google.com ([209.85.213.173]:61819) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1POTXu-0002Wo-CS for qemu-devel@nongnu.org; Fri, 03 Dec 2010 06:11:22 -0500 Received: by mail-yx0-f173.google.com with SMTP id 31so5074889yxl.4 for ; Fri, 03 Dec 2010 03:11:22 -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=/E49PWPwCWgm/yqhexkNFmGXETXNscSZE1zrAZ+bvL0=; b=mMY0ZZLO2xIooDi9usfdOr9K9VVddlfPOr79Zd78qLJk0OIdonkunzBseyXbVc2Vt1 RLctHAtF4cEouQN1t2mSkeZNncUht0sjkd7j7EXlnL1Qh+uGGGGySVvOFaTdtYVMW8wr Ah/1a5KH3MrgaQPC0yB439K3zPLMtVRlvwInk= 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=LmoaTkOCnyWyPgcvm1Sj5VeKZXf22S917D6WfscZpzWufCYwgFf5+if3YwhsGpZNPW aVsDx8NATFGzU6bs4TVU9BvHfWloKI4V0sL1vfHRZxKOabmc4mzsKWA3YbIWTECmzlEN 2kBkiNykx6EiALKXcX+sPyqsamEu+E3zluUvc= Received: by 10.101.167.29 with SMTP id u29mr1382762ano.166.1291374682229; Fri, 03 Dec 2010 03:11:22 -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 b28sm1680470ana.28.2010.12.03.03.11.19 (version=TLSv1/SSLv3 cipher=RC4-MD5); Fri, 03 Dec 2010 03:11:21 -0800 (PST) Received: by ronniesahlberg@gmail.com (sSMTP sendmail emulation); Fri, 03 Dec 2010 22:10:54 +1100 From: ronniesahlberg@gmail.com To: qemu-devel@nongnu.org Date: Fri, 3 Dec 2010 22:09:46 +1100 Message-Id: <1291374593-17448-8-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 07/14] ./block/iscsi/pdu.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 : pdu.c This file contains functions for basic manipulation of iscsi PDUs and their flags as well as tracking and matching relations between requests, data-in, data-out and responses. ... ./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/pdu.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 339 insertions(+), 0 deletions(-) create mode 100644 block/iscsi/pdu.c diff --git a/block/iscsi/pdu.c b/block/iscsi/pdu.c new file mode 100644 index 0000000..ff3aecf --- /dev/null +++ b/block/iscsi/pdu.c @@ -0,0 +1,339 @@ +/* + 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 +#include "iscsi.h" +#include "iscsi-private.h" +#include "scsi-lowlevel.h" +#include "slist.h" + +struct iscsi_pdu * +iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode, + enum iscsi_opcode response_opcode) +{ + struct iscsi_pdu *pdu; + + pdu = malloc(sizeof(struct iscsi_pdu)); + if (pdu == NULL) { + iscsi_set_error(iscsi, "failed to allocate pdu\n"); + return NULL; + } + bzero(pdu, sizeof(struct iscsi_pdu)); + + pdu->outdata.size = ISCSI_HEADER_SIZE; + pdu->outdata.data = malloc(pdu->outdata.size); + + if (pdu->outdata.data == NULL) { + iscsi_set_error(iscsi, "failed to allocate pdu header\n"); + free(pdu); + pdu = NULL; + return NULL; + } + bzero(pdu->outdata.data, pdu->outdata.size); + + /* opcode */ + pdu->outdata.data[0] = opcode; + pdu->response_opcode = response_opcode; + + /* isid */ + if (opcode == ISCSI_PDU_LOGIN_REQUEST) { + memcpy(&pdu->outdata.data[8], &iscsi->isid[0], 6); + } + + /* itt */ + *(uint32_t *)&pdu->outdata.data[16] = htonl(iscsi->itt); + pdu->itt = iscsi->itt; + + iscsi->itt++; + + return pdu; +} + +void +iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) +{ + if (pdu == NULL) { + iscsi_set_error(iscsi, "trying to free NULL pdu"); + return; + } + if (pdu->outdata.data) { + free(pdu->outdata.data); + pdu->outdata.data = NULL; + } + if (pdu->indata.data) { + free(pdu->indata.data); + pdu->indata.data = NULL; + } + if (pdu->scsi_cbdata) { + iscsi_free_scsi_cbdata(pdu->scsi_cbdata); + pdu->scsi_cbdata = NULL; + } + + free(pdu); +} + + +int +iscsi_add_data(struct iscsi_context *iscsi, struct iscsi_data *data, + unsigned char *dptr, int dsize, int pdualignment) +{ + int len, aligned; + unsigned char *buf; + + if (dsize == 0) { + iscsi_set_error(iscsi, "Trying to append zero size data to " + "iscsi_data\n"); + return -1; + } + + len = data->size + dsize; + aligned = len; + if (pdualignment) { + aligned = (aligned+3)&0xfffffffc; + } + buf = malloc(aligned); + if (buf == NULL) { + iscsi_set_error(iscsi, "failed to allocate buffer for %d " + "bytes\n", len); + return -2; + } + + if (data->size > 0) { + memcpy(buf, data->data, data->size); + } + memcpy(buf + data->size, dptr, dsize); + if (len != aligned) { + /* zero out any padding at the end */ + bzero(buf+len, aligned-len); + } + + if (data->data != NULL) { + free(data->data); + } + data->data = buf; + data->size = len; + + return 0; +} + +int +iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, + unsigned char *dptr, int dsize) +{ + if (pdu == NULL) { + iscsi_set_error(iscsi, "trying to add data to NULL pdu\n"); + return -1; + } + if (dsize == 0) { + iscsi_set_error(iscsi, "Trying to append zero size data to " + "pdu\n"); + return -2; + } + + if (iscsi_add_data(iscsi, &pdu->outdata, dptr, dsize, 1) != 0) { + iscsi_set_error(iscsi, "failed to add data to pdu buffer\n"); + return -3; + } + + /* update data segment length */ + *(uint32_t *)&pdu->outdata.data[4] = htonl(pdu->outdata.size + - ISCSI_HEADER_SIZE); + + return 0; +} + +int +iscsi_get_pdu_size(struct iscsi_context *iscsi, const unsigned char *hdr) +{ + int size; + + size = (ntohl(*(uint32_t *)&hdr[4])&0x00ffffff) + ISCSI_HEADER_SIZE; + size = (size+3)&0xfffffffc; + + return size; +} + + +int +iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr, + int size) +{ + uint32_t itt; + enum iscsi_opcode opcode; + struct iscsi_pdu *pdu; + uint8_t ahslen; + + opcode = hdr[0] & 0x3f; + ahslen = hdr[4]; + itt = ntohl(*(uint32_t *)&hdr[16]); + + if (ahslen != 0) { + iscsi_set_error(iscsi, "cant handle expanded headers yet\n"); + return -1; + } + + for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) { + enum iscsi_opcode expected_response = pdu->response_opcode; + int is_finished = 1; + + if (pdu->itt != itt) { + continue; + } + + /* we have a special case with scsi-command opcodes, + * they are replied to by either a scsi-response + * or a data-in, or a combination of both. + */ + if (opcode == ISCSI_PDU_DATA_IN + && expected_response == ISCSI_PDU_SCSI_RESPONSE) { + expected_response = ISCSI_PDU_DATA_IN; + } + + if (opcode != expected_response) { + iscsi_set_error(iscsi, "Got wrong opcode back for " + "itt:%d got:%d expected %d\n", + itt, opcode, pdu->response_opcode); + return -1; + } + switch (opcode) { + case ISCSI_PDU_LOGIN_RESPONSE: + if (iscsi_process_login_reply(iscsi, pdu, hdr, size) + != 0) { + SLIST_REMOVE(&iscsi->waitpdu, pdu); + iscsi_free_pdu(iscsi, pdu); + iscsi_set_error(iscsi, "iscsi login reply " + "failed\n"); + return -2; + } + break; + case ISCSI_PDU_TEXT_RESPONSE: + if (iscsi_process_text_reply(iscsi, pdu, hdr, size) + != 0) { + SLIST_REMOVE(&iscsi->waitpdu, pdu); + iscsi_free_pdu(iscsi, pdu); + iscsi_set_error(iscsi, "iscsi text reply " + "failed\n"); + return -2; + } + break; + case ISCSI_PDU_LOGOUT_RESPONSE: + if (iscsi_process_logout_reply(iscsi, pdu, hdr, size) + != 0) { + SLIST_REMOVE(&iscsi->waitpdu, pdu); + iscsi_free_pdu(iscsi, pdu); + iscsi_set_error(iscsi, "iscsi logout reply " + "failed\n"); + return -3; + } + break; + case ISCSI_PDU_SCSI_RESPONSE: + if (iscsi_process_scsi_reply(iscsi, pdu, hdr, size) + != 0) { + SLIST_REMOVE(&iscsi->waitpdu, pdu); + iscsi_free_pdu(iscsi, pdu); + iscsi_set_error(iscsi, "iscsi response reply " + "failed\n"); + return -4; + } + break; + case ISCSI_PDU_DATA_IN: + if (iscsi_process_scsi_data_in(iscsi, pdu, hdr, size, + &is_finished) != 0) { + SLIST_REMOVE(&iscsi->waitpdu, pdu); + iscsi_free_pdu(iscsi, pdu); + iscsi_set_error(iscsi, "iscsi data in " + "failed\n"); + return -4; + } + break; + case ISCSI_PDU_NOP_IN: + if (iscsi_process_nop_out_reply(iscsi, pdu, hdr, size) + != 0) { + SLIST_REMOVE(&iscsi->waitpdu, pdu); + iscsi_free_pdu(iscsi, pdu); + iscsi_set_error(iscsi, "iscsi nop-in failed\n"); + return -5; + } + break; + default: + iscsi_set_error(iscsi, "Dont know how to handle " + "opcode %d\n", opcode); + return -2; + } + + if (is_finished) { + SLIST_REMOVE(&iscsi->waitpdu, pdu); + iscsi_free_pdu(iscsi, pdu); + } + return 0; + } + + return 0; +} + +void +iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags) +{ + pdu->outdata.data[1] = flags; +} + +void +iscsi_pdu_set_immediate(struct iscsi_pdu *pdu) +{ + pdu->outdata.data[0] |= ISCSI_PDU_IMMEDIATE; +} + +void +iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt) +{ + *(uint32_t *)&pdu->outdata.data[20] = htonl(ttt); +} + +void +iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn) +{ + *(uint32_t *)&pdu->outdata.data[24] = htonl(cmdsn); +} + +void +iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn) +{ + *(uint32_t *)&pdu->outdata.data[28] = htonl(expstatsnsn); +} + +void +iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task) +{ + bzero(&pdu->outdata.data[32], 16); + memcpy(&pdu->outdata.data[32], task->cdb, task->cdb_size); +} + +void +iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun) +{ + pdu->outdata.data[9] = lun; +} + +void +iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen) +{ + *(uint32_t *)&pdu->outdata.data[20] = htonl(expxferlen); +}