File longhorn.patch of Package tgt-longhorn

Index: tgt/usr/Makefile
===================================================================
--- tgt.orig/usr/Makefile
+++ tgt/usr/Makefile
@@ -50,7 +50,7 @@ CFLAGS += -Wall -Wstrict-prototypes -Wer
 CFLAGS += -DTGT_VERSION=\"$(VERSION)$(EXTRAVERSION)\"
 CFLAGS += -DBSDIR=\"$(DESTDIR)$(libdir)/backing-store\"
 
-LIBS += -lpthread -ldl
+LIBS += -lpthread -ldl -lrt -llonghorn $(EXTRA_LIBS)
 
 ifneq ($(SD_NOTIFY),)
 LIBS += -lsystemd
@@ -60,7 +60,8 @@ PROGRAMS += tgtd tgtadm tgtimg
 TGTD_OBJS += tgtd.o mgmt.o target.o scsi.o log.o driver.o util.o work.o \
 		concat_buf.o parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o \
 		ssc.o libssc.o bs_rdwr.o bs_ssc.o \
-		bs_null.o bs_sg.o bs.o libcrc32c.o bs_sheepdog.o
+		bs_null.o bs_sg.o bs.o libcrc32c.o bs_sheepdog.o \
+		bs_longhorn.o
 
 TGTD_DEP = $(TGTD_OBJS:.o=.d)
 
Index: tgt/usr/bs_longhorn.c
===================================================================
--- /dev/null
+++ tgt/usr/bs_longhorn.c
@@ -0,0 +1,384 @@
+/*
+ * Longhorn backing store routine
+ *
+ * Copyright (C) 2016 Sheng Yang <sheng.yang@rancher.com>
+ *
+ * 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, version 2 of the
+ * License.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <pthread.h>
+#include <limits.h>
+#include <ctype.h>
+#include <sys/un.h>
+
+#include "list.h"
+#include "tgtd.h"
+#include "util.h"
+#include "log.h"
+#include "scsi.h"
+#include "bs_thread.h"
+
+#include "liblonghorn.h"
+
+#define DEFAULT_REQUEST_TIMEOUT 15
+
+struct longhorn_info {
+	struct lh_client_conn *conn;
+	size_t size;
+	int request_timeout;
+	char *path;
+
+	pthread_rwlock_t rwlock;
+};
+
+#define LHP(lu)	((struct longhorn_info *) \
+			((char *)lu + \
+			 sizeof(struct scsi_lu) + \
+			 sizeof(struct bs_thread_info)) \
+                )
+
+static void set_medium_error(int *result, uint8_t *key, uint16_t *asc)
+{
+	*result = SAM_STAT_CHECK_CONDITION;
+	*key = MEDIUM_ERROR;
+	*asc = ASC_READ_ERROR;
+}
+
+static void bs_longhorn_request(struct scsi_cmd *cmd)
+{
+	int ret = 0;
+	uint32_t length = 0;
+	int result = SAM_STAT_GOOD;
+	uint8_t key = 0;
+	uint16_t asc = 0;
+	char *tmpbuf;
+	uint64_t offset;
+	uint32_t tl;
+	struct longhorn_info *lh = LHP(cmd->dev);
+	struct lh_client_conn *old_conn, *new_conn;
+
+	switch (cmd->scb[0]) {
+	case WRITE_6:
+	case WRITE_10:
+	case WRITE_12:
+	case WRITE_16:
+		length = scsi_get_out_length(cmd);
+		pthread_rwlock_rdlock(&lh->rwlock);
+		ret = lh_client_write_at(lh->conn, scsi_get_out_buffer(cmd),
+			    length, cmd->offset);
+		pthread_rwlock_unlock(&lh->rwlock);
+		if (ret) {
+            eprintf("fail to write at %" PRIu64 " for %u\n", cmd->offset, length);
+			set_medium_error(&result, &key, &asc);
+        }
+		break;
+	case READ_6:
+	case READ_10:
+	case READ_12:
+	case READ_16:
+		length = scsi_get_in_length(cmd);
+		pthread_rwlock_rdlock(&lh->rwlock);
+		ret = lh_client_read_at(lh->conn, scsi_get_in_buffer(cmd),
+			    length, cmd->offset);
+		pthread_rwlock_unlock(&lh->rwlock);
+		if (ret) {
+            eprintf("fail to read at %" PRIu64 " for %u\n", cmd->offset, length);
+			set_medium_error(&result, &key, &asc);
+        }
+		break;
+	case EXCHANGE_MEDIUM:
+		old_conn = lh->conn;
+		new_conn = lh_client_allocate_conn(lh->request_timeout);
+		if (new_conn == NULL) {
+			eprintf("cannot allocate new connection\n");
+			set_medium_error(&result, &key, &asc);
+			break;
+		}
+
+		ret = lh_client_open_conn(new_conn, lh->path);
+		if (ret < 0) {
+			eprintf("cannot refresh connection to %s: %d\n", lh->path, ret);
+			set_medium_error(&result, &key, &asc);
+			break;
+		}
+		eprintf("reconnected to %s\n", lh->path);
+
+		pthread_rwlock_wrlock(&lh->rwlock);
+		// no on-the-fly request after this point due to the lock
+		lh->conn = new_conn;
+		pthread_rwlock_unlock(&lh->rwlock);
+
+		eprintf("connection updated, close old longhorn connection\n");
+		lh_client_close_conn(old_conn);
+		lh_client_free_conn(old_conn);
+		break;
+	case UNMAP:
+        /*
+         * Reference: Section "3.54 UNMAP command" in doc
+         *   https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
+         */
+		if (!cmd->dev->attrs.thinprovisioning) {
+			eprintf("invalid cmd->dev->attrs.thinprovisioning == false\n");
+			result = SAM_STAT_CHECK_CONDITION;
+			key = ILLEGAL_REQUEST;
+			asc = ASC_INVALID_FIELD_IN_CDB;
+			break;
+		}
+
+		length = scsi_get_out_length(cmd);
+		tmpbuf = scsi_get_out_buffer(cmd);
+
+		if (length < 8)
+			break;
+
+		length -= 8;
+		tmpbuf += 8;
+
+		while (length >= 16) {
+			offset = get_unaligned_be64(&tmpbuf[0]);
+			offset = offset << cmd->dev->blk_shift;
+
+			tl = get_unaligned_be32(&tmpbuf[8]);
+			tl = tl << cmd->dev->blk_shift;
+
+			if (offset + tl > cmd->dev->size) {
+				eprintf("UNMAP beyond EOF\n");
+				result = SAM_STAT_CHECK_CONDITION;
+				key = ILLEGAL_REQUEST;
+				asc = ASC_LBA_OUT_OF_RANGE;
+				break;
+			}
+
+			if (tl > 0) {
+				if (lh_client_unmap(lh->conn, NULL, tl, offset) != 0) {
+					eprintf("Failed to punch hole for"
+						" UNMAP at offset:%" PRIu64
+						" length:%d\n",
+						offset, tl);
+					result = SAM_STAT_CHECK_CONDITION;
+					key = HARDWARE_ERROR;
+					asc = ASC_INTERNAL_TGT_FAILURE;
+					break;
+				}
+			}
+
+			length -= 16;
+			tmpbuf += 16;
+		}
+		break;
+	case SYNCHRONIZE_CACHE:
+	case SYNCHRONIZE_CACHE_16:
+		// Ignore sync since it's synchronized by default
+		break;
+	default:
+		eprintf("unsupported cmd->scb[0]: %x\n", cmd->scb[0]);
+		break;
+	}
+
+	dprintf("io done %p %x %d %u\n", cmd, cmd->scb[0], ret, length);
+
+	scsi_set_result(cmd, result);
+
+	if (result != SAM_STAT_GOOD) {
+		eprintf("io error %p %x %d %d %" PRIu64 ", %m\n",
+			cmd, cmd->scb[0], ret, length, cmd->offset);
+		sense_data_build(cmd, key, asc);
+	}
+}
+
+static int bs_longhorn_open(struct scsi_lu *lu, char *path,
+			    int *fd, uint64_t *size)
+{
+	struct longhorn_info *lh = LHP(lu);
+	int rc;
+	int len;
+
+	rc = lh_client_open_conn(lh->conn, path);
+	if (rc < 0) {
+		eprintf("Cannot establish connection\n");
+		return rc;
+	}
+
+	*size = lh->size;
+
+	len = strlen(path);
+	lh->path = malloc(len + 1); // ending '\0'
+	strcpy(lh->path, path);
+
+	return 0;
+}
+
+static void bs_longhorn_close(struct scsi_lu *lu)
+{
+	if (LHP(lu)->conn) {
+		dprintf("close longhorn connection\n");
+		lh_client_close_conn(LHP(lu)->conn);
+	}
+}
+
+static char *slurp_to_semi(char **p)
+{
+	char *end = index(*p, ';');
+	char *ret;
+	int len;
+
+	if (end == NULL)
+		end = *p + strlen(*p);
+	len = end - *p;
+	ret = malloc(len + 1);
+	strncpy(ret, *p, len);
+	ret[len] = '\0';
+	*p = end;
+	/* Jump past the semicolon, if we stopped at one */
+	if (**p == ';')
+		*p = end + 1;
+	return ret;
+}
+
+static char *slurp_value(char **p)
+{
+	char *equal = index(*p, '=');
+	if (equal) {
+		*p = equal + 1;
+		return slurp_to_semi(p);
+	} else {
+		return NULL;
+	}
+}
+
+static int is_opt(const char *opt, char *p)
+{
+	int ret = 0;
+	if ((strncmp(p, opt, strlen(opt)) == 0) &&
+		(p[strlen(opt)] == '=')) {
+		ret = 1;
+	}
+	return ret;
+}
+
+static tgtadm_err bs_longhorn_init(struct scsi_lu *lu, char *bsopts)
+{
+	struct bs_thread_info *info = BS_THREAD_I(lu);
+	char *value = NULL;
+	size_t size = 0;
+	int request_timeout = DEFAULT_REQUEST_TIMEOUT;
+    struct longhorn_info *lh = LHP(lu);
+	int rc = 0;
+
+	while (bsopts && strlen(bsopts)) {
+		if (is_opt("size", bsopts)) {
+			value = slurp_value(&bsopts);
+			size = atoll(value);
+		} else if (is_opt("request_timeout", bsopts)) {
+			value = slurp_value(&bsopts);
+			request_timeout = atoi(value);
+		}
+	}
+
+	lh->conn = lh_client_allocate_conn(request_timeout);
+	if (lh->conn == NULL) {
+		perror("Cannot allocate connection\n");
+		return TGTADM_NOMEM;
+	}
+
+	rc = pthread_rwlock_init(&lh->rwlock, NULL);
+	if (rc < 0) {
+		perror("Cannot init rwlock for connection\n");
+		return TGTADM_NOMEM;
+	}
+	lh->size = size;
+	lh->request_timeout = request_timeout;
+	return bs_thread_open(info, bs_longhorn_request, nr_iothreads);
+}
+
+static void bs_longhorn_exit(struct scsi_lu *lu)
+{
+	struct bs_thread_info *info = BS_THREAD_I(lu);
+	struct longhorn_info *lh = LHP(lu);
+
+	bs_thread_close(info);
+
+	lh_client_free_conn(lh->conn);
+	lh->conn = NULL;
+	free(lh->path);
+	pthread_rwlock_destroy(&lh->rwlock);
+}
+
+static struct backingstore_template longhorn_bst = {
+	.bs_name				= "longhorn",
+	.bs_datasize			= sizeof(struct bs_thread_info) + sizeof(struct longhorn_info),
+	.bs_open				= bs_longhorn_open,
+	.bs_close				= bs_longhorn_close,
+	.bs_init				= bs_longhorn_init,
+	.bs_exit				= bs_longhorn_exit,
+	.bs_cmd_submit			= bs_thread_cmd_submit,
+	.bs_oflags_supported	= O_SYNC | O_DIRECT | O_RDWR,
+};
+
+__attribute__((constructor)) void register_bs_module(void)
+{
+	unsigned char opcodes[] = {
+		ALLOW_MEDIUM_REMOVAL,
+		FORMAT_UNIT,
+		INQUIRY,
+		MAINT_PROTOCOL_IN,
+		MODE_SELECT,
+		MODE_SELECT_10,
+		MODE_SENSE,
+		MODE_SENSE_10,
+		PERSISTENT_RESERVE_IN,
+		PERSISTENT_RESERVE_OUT,
+		READ_10,
+		READ_12,
+		READ_16,
+		READ_6,
+		READ_CAPACITY,
+		RELEASE,
+		REPORT_LUNS,
+		REQUEST_SENSE,
+		RESERVE,
+		SEND_DIAGNOSTIC,
+		SERVICE_ACTION_IN,
+		START_STOP,
+		SYNCHRONIZE_CACHE,
+		SYNCHRONIZE_CACHE_16,
+		TEST_UNIT_READY,
+		WRITE_10,
+		WRITE_12,
+		WRITE_16,
+		WRITE_6,
+		EXCHANGE_MEDIUM,
+		UNMAP,
+	};
+
+	bs_create_opcode_map(&longhorn_bst, opcodes, ARRAY_SIZE(opcodes));
+
+	register_backingstore_template(&longhorn_bst);
+}
+
Index: tgt/usr/sbc.c
===================================================================
--- tgt.orig/usr/sbc.c
+++ tgt/usr/sbc.c
@@ -952,7 +952,7 @@ static struct device_type_template sbc_t
 		{spc_service_action, maint_in_service_actions,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{spc_illegal_op,},
+		{sbc_rw,},
 		{spc_illegal_op,},
 
 		{sbc_rw, NULL, PR_EA_FA|PR_EA_FN},
Index: tgt/usr/spc.c
===================================================================
--- tgt.orig/usr/spc.c
+++ tgt/usr/spc.c
@@ -1972,6 +1972,47 @@ tgtadm_err spc_lu_offline(struct scsi_lu
 	return TGTADM_SUCCESS;
 }
 
+
+static char *slurp_to_semi(char **p)
+{
+	char *end = index(*p, ';');
+	char *ret;
+	int len;
+
+	if (end == NULL)
+		end = *p + strlen(*p);
+	len = end - *p;
+	ret = malloc(len + 1);
+	strncpy(ret, *p, len);
+	ret[len] = '\0';
+	*p = end;
+	/* Jump past the semicolon, if we stopped at one */
+	if (**p == ';')
+		*p = end + 1;
+	return ret;
+}
+
+static char *slurp_value(char **p)
+{
+	char *equal = index(*p, '=');
+	if (equal) {
+		*p = equal + 1;
+		return slurp_to_semi(p);
+	} else {
+		return NULL;
+	}
+}
+
+static int is_opt(const char *opt, char *p)
+{
+	int ret = 0;
+	if ((strncmp(p, opt, strlen(opt)) == 0) &&
+		(p[strlen(opt)] == '=')) {
+		ret = 1;
+	}
+	return ret;
+}
+
 tgtadm_err lu_config(struct scsi_lu *lu, char *params, match_fn_t *fn)
 {
 	tgtadm_err adm_err = TGTADM_SUCCESS;
@@ -2073,6 +2114,24 @@ tgtadm_err lu_config(struct scsi_lu *lu,
 			match_strncpy(buf, &args[0], sizeof(buf));
 			adm_err = tgt_device_path_update(lu->tgt, lu, buf);
 			break;
+		case Opt_bsopts:
+			match_strncpy(buf, &args[0], sizeof(buf));
+			char *lh_opt_ptr = buf;
+			char *lh_opt_val = NULL;
+			size_t size = 0;
+			if (is_opt("size", lh_opt_ptr)) {
+				lh_opt_val = slurp_value(&lh_opt_ptr);
+				size = atoll(lh_opt_val);
+				if (lu->size > size) {
+					eprintf("Invalid bsopts %s, the input size %zu is smaller than the current size %lu\n", buf, size, lu->size);
+					adm_err = TGTADM_INVALID_REQUEST;
+					break;
+				}
+				lu->size = size;
+			}else {
+				adm_err = TGTADM_INVALID_REQUEST;
+			}
+			break;
 		default:
 			adm_err = fn ? fn(lu, p) : TGTADM_INVALID_REQUEST;
 		}
openSUSE Build Service is sponsored by