From: Vladislav Bolkhovitin on
This patch contains file scst_lib.c.

Signed-off-by: Vladislav Bolkhovitin <vst(a)vlnb.net>
---
scst_lib.c | 6337 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 6337 insertions(+)

diff -uprN orig/linux-2.6.33/drivers/scst/scst_lib.c linux-2.6.33/drivers/scst/scst_lib.c
--- orig/linux-2.6.33/drivers/scst/scst_lib.c
+++ linux-2.6.33/drivers/scst/scst_lib.c
@@ -0,0 +1,6337 @@
+/*
+ * scst_lib.c
+ *
+ * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst(a)vlnb.net>
+ * Copyright (C) 2004 - 2005 Leonid Stoljar
+ * Copyright (C) 2007 - 2010 ID7 Ltd.
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/cdrom.h>
+#include <linux/unistd.h>
+#include <linux/string.h>
+#include <asm/kmap_types.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+
+#include "scst.h"
+#include "scst_priv.h"
+#include "scst_mem.h"
+
+struct scsi_io_context {
+ unsigned int full_cdb_used:1;
+ void *data;
+ void (*done)(void *data, char *sense, int result, int resid);
+ char sense[SCST_SENSE_BUFFERSIZE];
+ unsigned char full_cdb[0];
+};
+static struct kmem_cache *scsi_io_context_cache;
+
+/* get_trans_len_x extract x bytes from cdb as length starting from off */
+static int get_trans_len_1(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_len_1_256(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_len_2(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_len_3(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_len_4(struct scst_cmd *cmd, uint8_t off);
+
+/* for special commands */
+static int get_trans_len_block_limit(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_len_read_capacity(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_len_serv_act_in(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_len_single(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_len_none(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_len_read_pos(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_cdb_len_10(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_len_prevent_allow_medium_removal(struct scst_cmd *cmd,
+ uint8_t off);
+static int get_trans_len_3_read_elem_stat(struct scst_cmd *cmd, uint8_t off);
+static int get_trans_len_start_stop(struct scst_cmd *cmd, uint8_t off);
+
+/*
++=====================================-============-======-
+| Command name | Operation | Type |
+| | code | |
+|-------------------------------------+------------+------+
+
++=========================================================+
+|Key: M = command implementation is mandatory. |
+| O = command implementation is optional. |
+| V = Vendor-specific |
+| R = Reserved |
+| ' '= DON'T use for this device |
++=========================================================+
+*/
+
+#define SCST_CDB_MANDATORY 'M' /* mandatory */
+#define SCST_CDB_OPTIONAL 'O' /* optional */
+#define SCST_CDB_VENDOR 'V' /* vendor */
+#define SCST_CDB_RESERVED 'R' /* reserved */
+#define SCST_CDB_NOTSUPP ' ' /* don't use */
+
+struct scst_sdbops {
+ uint8_t ops; /* SCSI-2 op codes */
+ uint8_t devkey[16]; /* Key for every device type M,O,V,R
+ * type_disk devkey[0]
+ * type_tape devkey[1]
+ * type_printer devkey[2]
+ * type_proseccor devkey[3]
+ * type_worm devkey[4]
+ * type_cdrom devkey[5]
+ * type_scanner devkey[6]
+ * type_mod devkey[7]
+ * type_changer devkey[8]
+ * type_commdev devkey[9]
+ * type_reserv devkey[A]
+ * type_reserv devkey[B]
+ * type_raid devkey[C]
+ * type_enclosure devkey[D]
+ * type_reserv devkey[E]
+ * type_reserv devkey[F]
+ */
+ const char *op_name; /* SCSI-2 op codes full name */
+ uint8_t direction; /* init --> target: SCST_DATA_WRITE
+ * target --> init: SCST_DATA_READ
+ */
+ uint16_t flags; /* opcode -- various flags */
+ uint8_t off; /* length offset in cdb */
+ int (*get_trans_len)(struct scst_cmd *cmd, uint8_t off)
+ __attribute__ ((aligned));
+} __attribute__((packed));
+
+static int scst_scsi_op_list[256];
+
+#define FLAG_NONE 0
+
+static const struct scst_sdbops scst_scsi_op_table[] = {
+ /*
+ * +-------------------> TYPE_IS_DISK (0)
+ * |
+ * |+------------------> TYPE_IS_TAPE (1)
+ * ||
+ * || +----------------> TYPE_IS_PROCESSOR (3)
+ * || |
+ * || | +--------------> TYPE_IS_CDROM (5)
+ * || | |
+ * || | | +------------> TYPE_IS_MOD (7)
+ * || | | |
+ * || | | |+-----------> TYPE_IS_CHANGER (8)
+ * || | | ||
+ * || | | || +-------> TYPE_IS_RAID (C)
+ * || | | || |
+ * || | | || |
+ * 0123456789ABCDEF ---> TYPE_IS_???? */
+
+ /* 6-bytes length CDB */
+ {0x00, "MMMMMMMMMMMMMMMM", "TEST UNIT READY",
+ /* let's be HQ to don't look dead under high load */
+ SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_IMPLICIT_HQ|
+ SCST_REG_RESERVE_ALLOWED,
+ 0, get_trans_len_none},
+ {0x01, " M ", "REWIND",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0x01, "O V OO OO ", "REZERO UNIT",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x02, "VVVVVV V ", "REQUEST BLOCK ADDR",
+ SCST_DATA_NONE, SCST_SMALL_TIMEOUT, 0, get_trans_len_none},
+ {0x03, "MMMMMMMMMMMMMMMM", "REQUEST SENSE",
+ SCST_DATA_READ, SCST_SMALL_TIMEOUT|SCST_SKIP_UA|SCST_LOCAL_CMD|
+ SCST_REG_RESERVE_ALLOWED,
+ 4, get_trans_len_1},
+ {0x04, "M O O ", "FORMAT UNIT",
+ SCST_DATA_WRITE, SCST_LONG_TIMEOUT|SCST_UNKNOWN_LENGTH|SCST_WRITE_MEDIUM,
+ 0, get_trans_len_none},
+ {0x04, " O ", "FORMAT",
+ SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none},
+ {0x05, "VMVVVV V ", "READ BLOCK LIMITS",
+ SCST_DATA_READ, SCST_SMALL_TIMEOUT|SCST_REG_RESERVE_ALLOWED,
+ 0, get_trans_len_block_limit},
+ {0x07, " O ", "INITIALIZE ELEMENT STATUS",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0x07, "OVV O OV ", "REASSIGN BLOCKS",
+ SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none},
+ {0x08, "O ", "READ(6)",
+ SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 4, get_trans_len_1_256},
+ {0x08, " MV OO OV ", "READ(6)",
+ SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 2, get_trans_len_3},
+ {0x08, " M ", "GET MESSAGE(6)",
+ SCST_DATA_READ, FLAG_NONE, 2, get_trans_len_3},
+ {0x08, " O ", "RECEIVE",
+ SCST_DATA_READ, FLAG_NONE, 2, get_trans_len_3},
+ {0x0A, "O ", "WRITE(6)",
+ SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM,
+ 4, get_trans_len_1_256},
+ {0x0A, " M O OV ", "WRITE(6)",
+ SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM,
+ 2, get_trans_len_3},
+ {0x0A, " M ", "PRINT",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x0A, " M ", "SEND MESSAGE(6)",
+ SCST_DATA_WRITE, FLAG_NONE, 2, get_trans_len_3},
+ {0x0A, " M ", "SEND(6)",
+ SCST_DATA_WRITE, FLAG_NONE, 2, get_trans_len_3},
+ {0x0B, "O OO OV ", "SEEK(6)",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x0B, " ", "TRACK SELECT",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x0B, " O ", "SLEW AND PRINT",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x0C, "VVVVVV V ", "SEEK BLOCK",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0x0D, "VVVVVV V ", "PARTITION",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM,
+ 0, get_trans_len_none},
+ {0x0F, "VOVVVV V ", "READ REVERSE",
+ SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 2, get_trans_len_3},
+ {0x10, "VM V V ", "WRITE FILEMARKS",
+ SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none},
+ {0x10, " O O ", "SYNCHRONIZE BUFFER",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x11, "VMVVVV ", "SPACE",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0x12, "MMMMMMMMMMMMMMMM", "INQUIRY",
+ SCST_DATA_READ, SCST_SMALL_TIMEOUT|SCST_IMPLICIT_HQ|SCST_SKIP_UA|
+ SCST_REG_RESERVE_ALLOWED,
+ 4, get_trans_len_1},
+ {0x13, "VOVVVV ", "VERIFY(6)",
+ SCST_DATA_NONE, SCST_TRANSFER_LEN_TYPE_FIXED|
+ SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED,
+ 2, get_trans_len_3},
+ {0x14, "VOOVVV ", "RECOVER BUFFERED DATA",
+ SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 2, get_trans_len_3},
+ {0x15, "OMOOOOOOOOOOOOOO", "MODE SELECT(6)",
+ SCST_DATA_WRITE, SCST_LOCAL_CMD, 4, get_trans_len_1},
+ {0x16, "MMMMMMMMMMMMMMMM", "RESERVE",
+ SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD,
+ 0, get_trans_len_none},
+ {0x17, "MMMMMMMMMMMMMMMM", "RELEASE",
+ SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD|SCST_REG_RESERVE_ALLOWED,
+ 0, get_trans_len_none},
+ {0x18, "OOOOOOOO ", "COPY",
+ SCST_DATA_WRITE, SCST_LONG_TIMEOUT, 2, get_trans_len_3},
+ {0x19, "VMVVVV ", "ERASE",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM,
+ 0, get_trans_len_none},
+ {0x1A, "OMOOOOOOOOOOOOOO", "MODE SENSE(6)",
+ SCST_DATA_READ, SCST_SMALL_TIMEOUT, 4, get_trans_len_1},
+ {0x1B, " O ", "SCAN",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x1B, " O ", "LOAD UNLOAD",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0x1B, " O ", "STOP PRINT",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x1B, "O OO O O ", "START STOP UNIT",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_start_stop},
+ {0x1C, "OOOOOOOOOOOOOOOO", "RECEIVE DIAGNOSTIC RESULTS",
+ SCST_DATA_READ, FLAG_NONE, 3, get_trans_len_2},
+ {0x1D, "MMMMMMMMMMMMMMMM", "SEND DIAGNOSTIC",
+ SCST_DATA_WRITE, FLAG_NONE, 4, get_trans_len_1},
+ {0x1E, "OOOOOOOOOOOOOOOO", "PREVENT ALLOW MEDIUM REMOVAL",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0,
+ get_trans_len_prevent_allow_medium_removal},
+ {0x1F, " O ", "PORT STATUS",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+
+ /* 10-bytes length CDB */
+ {0x23, "V VV V ", "READ FORMAT CAPACITY",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x24, "V VVM ", "SET WINDOW",
+ SCST_DATA_WRITE, FLAG_NONE, 6, get_trans_len_3},
+ {0x25, "M MM M ", "READ CAPACITY",
+ SCST_DATA_READ, SCST_IMPLICIT_HQ|SCST_REG_RESERVE_ALLOWED,
+ 0, get_trans_len_read_capacity},
+ {0x25, " O ", "GET WINDOW",
+ SCST_DATA_READ, FLAG_NONE, 6, get_trans_len_3},
+ {0x28, "M MMMM ", "READ(10)",
+ SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 7, get_trans_len_2},
+ {0x28, " O ", "GET MESSAGE(10)",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x29, "V VV O ", "READ GENERATION",
+ SCST_DATA_READ, FLAG_NONE, 8, get_trans_len_1},
+ {0x2A, "O MO M ", "WRITE(10)",
+ SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM,
+ 7, get_trans_len_2},
+ {0x2A, " O ", "SEND MESSAGE(10)",
+ SCST_DATA_WRITE, FLAG_NONE, 7, get_trans_len_2},
+ {0x2A, " O ", "SEND(10)",
+ SCST_DATA_WRITE, FLAG_NONE, 7, get_trans_len_2},
+ {0x2B, " O ", "LOCATE",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0x2B, " O ", "POSITION TO ELEMENT",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0x2B, "O OO O ", "SEEK(10)",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x2C, "V O O ", "ERASE(10)",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM,
+ 0, get_trans_len_none},
+ {0x2D, "V O O ", "READ UPDATED BLOCK",
+ SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 0, get_trans_len_single},
+ {0x2E, "O OO O ", "WRITE AND VERIFY(10)",
+ SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM,
+ 7, get_trans_len_2},
+ {0x2F, "O OO O ", "VERIFY(10)",
+ SCST_DATA_NONE, SCST_TRANSFER_LEN_TYPE_FIXED|
+ SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED,
+ 7, get_trans_len_2},
+ {0x33, "O OO O ", "SET LIMITS(10)",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x34, " O ", "READ POSITION",
+ SCST_DATA_READ, SCST_SMALL_TIMEOUT, 7, get_trans_len_read_pos},
+ {0x34, " O ", "GET DATA BUFFER STATUS",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x34, "O OO O ", "PRE-FETCH",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x35, "O OO O ", "SYNCHRONIZE CACHE",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x36, "O OO O ", "LOCK UNLOCK CACHE",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x37, "O O ", "READ DEFECT DATA(10)",
+ SCST_DATA_READ, FLAG_NONE, 8, get_trans_len_1},
+ {0x37, " O ", "INIT ELEMENT STATUS WRANGE",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0x38, " O O ", "MEDIUM SCAN",
+ SCST_DATA_READ, FLAG_NONE, 8, get_trans_len_1},
+ {0x39, "OOOOOOOO ", "COMPARE",
+ SCST_DATA_WRITE, FLAG_NONE, 3, get_trans_len_3},
+ {0x3A, "OOOOOOOO ", "COPY AND VERIFY",
+ SCST_DATA_WRITE, FLAG_NONE, 3, get_trans_len_3},
+ {0x3B, "OOOOOOOOOOOOOOOO", "WRITE BUFFER",
+ SCST_DATA_WRITE, SCST_SMALL_TIMEOUT, 6, get_trans_len_3},
+ {0x3C, "OOOOOOOOOOOOOOOO", "READ BUFFER",
+ SCST_DATA_READ, SCST_SMALL_TIMEOUT, 6, get_trans_len_3},
+ {0x3D, " O O ", "UPDATE BLOCK",
+ SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED,
+ 0, get_trans_len_single},
+ {0x3E, "O OO O ", "READ LONG",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x3F, "O O O ", "WRITE LONG",
+ SCST_DATA_WRITE, SCST_WRITE_MEDIUM, 7, get_trans_len_2},
+ {0x40, "OOOOOOOOOO ", "CHANGE DEFINITION",
+ SCST_DATA_WRITE, SCST_SMALL_TIMEOUT, 8, get_trans_len_1},
+ {0x41, "O O ", "WRITE SAME",
+ SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM,
+ 0, get_trans_len_single},
+ {0x42, " O ", "READ SUB-CHANNEL",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x43, " O ", "READ TOC/PMA/ATIP",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x44, " M ", "REPORT DENSITY SUPPORT",
+ SCST_DATA_READ, SCST_REG_RESERVE_ALLOWED, 7, get_trans_len_2},
+ {0x44, " O ", "READ HEADER",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x45, " O ", "PLAY AUDIO(10)",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x46, " O ", "GET CONFIGURATION",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x47, " O ", "PLAY AUDIO MSF",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x48, " O ", "PLAY AUDIO TRACK INDEX",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x49, " O ", "PLAY TRACK RELATIVE(10)",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x4A, " O ", "GET EVENT STATUS NOTIFICATION",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x4B, " O ", "PAUSE/RESUME",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x4C, "OOOOOOOOOOOOOOOO", "LOG SELECT",
+ SCST_DATA_WRITE, SCST_SMALL_TIMEOUT, 7, get_trans_len_2},
+ {0x4D, "OOOOOOOOOOOOOOOO", "LOG SENSE",
+ SCST_DATA_READ, SCST_SMALL_TIMEOUT|SCST_REG_RESERVE_ALLOWED,
+ 7, get_trans_len_2},
+ {0x4E, " O ", "STOP PLAY/SCAN",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x50, " ", "XDWRITE",
+ SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none},
+ {0x51, " O ", "READ DISC INFORMATION",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x51, " ", "XPWRITE",
+ SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none},
+ {0x52, " O ", "READ TRACK INFORMATION",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x53, " O ", "RESERVE TRACK",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x54, " O ", "SEND OPC INFORMATION",
+ SCST_DATA_WRITE, FLAG_NONE, 7, get_trans_len_2},
+ {0x55, "OOOOOOOOOOOOOOOO", "MODE SELECT(10)",
+ SCST_DATA_WRITE, SCST_LOCAL_CMD, 7, get_trans_len_2},
+ {0x56, "OOOOOOOOOOOOOOOO", "RESERVE(10)",
+ SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD,
+ 0, get_trans_len_none},
+ {0x57, "OOOOOOOOOOOOOOOO", "RELEASE(10)",
+ SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD|SCST_REG_RESERVE_ALLOWED,
+ 0, get_trans_len_none},
+ {0x58, " O ", "REPAIR TRACK",
+ SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none},
+ {0x5A, "OOOOOOOOOOOOOOOO", "MODE SENSE(10)",
+ SCST_DATA_READ, SCST_SMALL_TIMEOUT, 7, get_trans_len_2},
+ {0x5B, " O ", "CLOSE TRACK/SESSION",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x5C, " O ", "READ BUFFER CAPACITY",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
+ {0x5D, " O ", "SEND CUE SHEET",
+ SCST_DATA_WRITE, FLAG_NONE, 6, get_trans_len_3},
+ {0x5E, "OOOOO OOOO ", "PERSISTENT RESERV IN",
+ SCST_DATA_READ, FLAG_NONE, 5, get_trans_len_4},
+ {0x5F, "OOOOO OOOO ", "PERSISTENT RESERV OUT",
+ SCST_DATA_WRITE, FLAG_NONE, 5, get_trans_len_4},
+
+ /* 16-bytes length CDB */
+ {0x80, "O OO O ", "XDWRITE EXTENDED",
+ SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none},
+ {0x80, " M ", "WRITE FILEMARKS",
+ SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none},
+ {0x81, "O OO O ", "REBUILD",
+ SCST_DATA_WRITE, SCST_WRITE_MEDIUM, 10, get_trans_len_4},
+ {0x82, "O OO O ", "REGENERATE",
+ SCST_DATA_WRITE, SCST_WRITE_MEDIUM, 10, get_trans_len_4},
+ {0x83, "OOOOOOOOOOOOOOOO", "EXTENDED COPY",
+ SCST_DATA_WRITE, SCST_WRITE_MEDIUM, 10, get_trans_len_4},
+ {0x84, "OOOOOOOOOOOOOOOO", "RECEIVE COPY RESULT",
+ SCST_DATA_WRITE, FLAG_NONE, 10, get_trans_len_4},
+ {0x86, "OOOOOOOOOO ", "ACCESS CONTROL IN",
+ SCST_DATA_NONE, SCST_REG_RESERVE_ALLOWED, 0, get_trans_len_none},
+ {0x87, "OOOOOOOOOO ", "ACCESS CONTROL OUT",
+ SCST_DATA_NONE, SCST_REG_RESERVE_ALLOWED, 0, get_trans_len_none},
+ {0x88, "M MMMM ", "READ(16)",
+ SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 10, get_trans_len_4},
+ {0x8A, "O OO O ", "WRITE(16)",
+ SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM,
+ 10, get_trans_len_4},
+ {0x8C, "OOOOOOOOOO ", "READ ATTRIBUTE",
+ SCST_DATA_READ, FLAG_NONE, 10, get_trans_len_4},
+ {0x8D, "OOOOOOOOOO ", "WRITE ATTRIBUTE",
+ SCST_DATA_WRITE, SCST_WRITE_MEDIUM, 10, get_trans_len_4},
+ {0x8E, "O OO O ", "WRITE AND VERIFY(16)",
+ SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM,
+ 10, get_trans_len_4},
+ {0x8F, "O OO O ", "VERIFY(16)",
+ SCST_DATA_NONE, SCST_TRANSFER_LEN_TYPE_FIXED|
+ SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED,
+ 10, get_trans_len_4},
+ {0x90, "O OO O ", "PRE-FETCH(16)",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x91, "O OO O ", "SYNCHRONIZE CACHE(16)",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x91, " M ", "SPACE(16)",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0x92, "O OO O ", "LOCK UNLOCK CACHE(16)",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0x92, " O ", "LOCATE(16)",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0x93, "O O ", "WRITE SAME(16)",
+ SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM,
+ 10, get_trans_len_4},
+ {0x93, " M ", "ERASE(16)",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM,
+ 0, get_trans_len_none},
+ {0x9E, "O ", "SERVICE ACTION IN",
+ SCST_DATA_READ, FLAG_NONE, 0, get_trans_len_serv_act_in},
+
+ /* 12-bytes length CDB */
+ {0xA0, "VVVVVVVVVV M ", "REPORT LUNS",
+ SCST_DATA_READ, SCST_SMALL_TIMEOUT|SCST_IMPLICIT_HQ|SCST_SKIP_UA|
+ SCST_FULLY_LOCAL_CMD|SCST_LOCAL_CMD|
+ SCST_REG_RESERVE_ALLOWED,
+ 6, get_trans_len_4},
+ {0xA1, " O ", "BLANK",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0xA3, " O ", "SEND KEY",
+ SCST_DATA_WRITE, FLAG_NONE, 8, get_trans_len_2},
+ {0xA3, "OOOOO OOOO ", "REPORT DEVICE IDENTIDIER",
+ SCST_DATA_READ, SCST_REG_RESERVE_ALLOWED, 6, get_trans_len_4},
+ {0xA3, " M ", "MAINTENANCE(IN)",
+ SCST_DATA_READ, FLAG_NONE, 6, get_trans_len_4},
+ {0xA4, " O ", "REPORT KEY",
+ SCST_DATA_READ, FLAG_NONE, 8, get_trans_len_2},
+ {0xA4, " O ", "MAINTENANCE(OUT)",
+ SCST_DATA_WRITE, FLAG_NONE, 6, get_trans_len_4},
+ {0xA5, " M ", "MOVE MEDIUM",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0xA5, " O ", "PLAY AUDIO(12)",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0xA6, " O O ", "EXCHANGE/LOAD/UNLOAD MEDIUM",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0xA7, " O ", "SET READ AHEAD",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0xA8, " O ", "GET MESSAGE(12)",
+ SCST_DATA_READ, FLAG_NONE, 6, get_trans_len_4},
+ {0xA8, "O OO O ", "READ(12)",
+ SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 6, get_trans_len_4},
+ {0xA9, " O ", "PLAY TRACK RELATIVE(12)",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0xAA, "O OO O ", "WRITE(12)",
+ SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM,
+ 6, get_trans_len_4},
+ {0xAA, " O ", "SEND MESSAGE(12)",
+ SCST_DATA_WRITE, FLAG_NONE, 6, get_trans_len_4},
+ {0xAC, " O ", "ERASE(12)",
+ SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none},
+ {0xAC, " M ", "GET PERFORMANCE",
+ SCST_DATA_READ, SCST_UNKNOWN_LENGTH, 0, get_trans_len_none},
+ {0xAD, " O ", "READ DVD STRUCTURE",
+ SCST_DATA_READ, FLAG_NONE, 8, get_trans_len_2},
+ {0xAE, "O OO O ", "WRITE AND VERIFY(12)",
+ SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM,
+ 6, get_trans_len_4},
+ {0xAF, "O OO O ", "VERIFY(12)",
+ SCST_DATA_NONE, SCST_TRANSFER_LEN_TYPE_FIXED|
+ SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED,
+ 6, get_trans_len_4},
+#if 0 /* No need to support at all */
+ {0xB0, " OO O ", "SEARCH DATA HIGH(12)",
+ SCST_DATA_WRITE, FLAG_NONE, 9, get_trans_len_1},
+ {0xB1, " OO O ", "SEARCH DATA EQUAL(12)",
+ SCST_DATA_WRITE, FLAG_NONE, 9, get_trans_len_1},
+ {0xB2, " OO O ", "SEARCH DATA LOW(12)",
+ SCST_DATA_WRITE, FLAG_NONE, 9, get_trans_len_1},
+#endif
+ {0xB3, " OO O ", "SET LIMITS(12)",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0xB5, " O ", "REQUEST VOLUME ELEMENT ADDRESS",
+ SCST_DATA_READ, FLAG_NONE, 9, get_trans_len_1},
+ {0xB6, " O ", "SEND VOLUME TAG",
+ SCST_DATA_WRITE, FLAG_NONE, 9, get_trans_len_1},
+ {0xB6, " M ", "SET STREAMING",
+ SCST_DATA_WRITE, FLAG_NONE, 9, get_trans_len_2},
+ {0xB7, " O ", "READ DEFECT DATA(12)",
+ SCST_DATA_READ, FLAG_NONE, 9, get_trans_len_1},
+ {0xB8, " O ", "READ ELEMENT STATUS",
+ SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_3_read_elem_stat},
+ {0xB9, " O ", "READ CD MSF",
+ SCST_DATA_READ, SCST_UNKNOWN_LENGTH, 0, get_trans_len_none},
+ {0xBA, " O ", "SCAN",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none},
+ {0xBA, " O ", "REDUNDANCY GROUP(IN)",
+ SCST_DATA_READ, FLAG_NONE, 6, get_trans_len_4},
+ {0xBB, " O ", "SET SPEED",
+ SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
+ {0xBB, " O ", "REDUNDANCY GROUP(OUT)",
+ SCST_DATA_WRITE, FLAG_NONE, 6, get_trans_len_4},
+ {0xBC, " O ", "SPARE(IN)",
+ SCST_DATA_READ, FLAG_NONE, 6, get_trans_len_4},
+ {0xBD, " O ", "MECHANISM STATUS",
+ SCST_DATA_READ, FLAG_NONE, 8, get_trans_len_2},
+ {0xBD, " O ", "SPARE(OUT)",
+ SCST_DATA_WRITE, FLAG_NONE, 6, get_trans_len_4},
+ {0xBE, " O ", "READ CD",
+ SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 6, get_trans_len_3},
+ {0xBE, " O ", "VOLUME SET(IN)",
+ SCST_DATA_READ, FLAG_NONE, 6, get_trans_len_4},
+ {0xBF, " O ", "SEND DVD STRUCTUE",
+ SCST_DATA_WRITE, FLAG_NONE, 8, get_trans_len_2},
+ {0xBF, " O ", "VOLUME SET(OUT)",
+ SCST_DATA_WRITE, FLAG_NONE, 6, get_trans_len_4},
+ {0xE7, " V ", "INIT ELEMENT STATUS WRANGE",
+ SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_cdb_len_10}
+};
+
+#define SCST_CDB_TBL_SIZE ((int)ARRAY_SIZE(scst_scsi_op_table))
+
+static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev);
+static void scst_check_internal_sense(struct scst_device *dev, int result,
+ uint8_t *sense, int sense_len);
+static void scst_queue_report_luns_changed_UA(struct scst_session *sess,
+ int flags);
+static void __scst_check_set_UA(struct scst_tgt_dev *tgt_dev,
+ const uint8_t *sense, int sense_len, int flags);
+static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev,
+ const uint8_t *sense, int sense_len, int flags);
+static void scst_free_all_UA(struct scst_tgt_dev *tgt_dev);
+static void scst_release_space(struct scst_cmd *cmd);
+static void scst_unblock_cmds(struct scst_device *dev);
+static void scst_clear_reservation(struct scst_tgt_dev *tgt_dev);
+static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess,
+ struct scst_acg_dev *acg_dev);
+static void scst_tgt_retry_timer_fn(unsigned long arg);
+
+#ifdef CONFIG_SCST_DEBUG_TM
+static void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev);
+static void tm_dbg_deinit_tgt_dev(struct scst_tgt_dev *tgt_dev);
+#else
+static inline void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev) {}
+static inline void tm_dbg_deinit_tgt_dev(struct scst_tgt_dev *tgt_dev) {}
+#endif /* CONFIG_SCST_DEBUG_TM */
+
+/**
+ * scst_alloc_sense() - allocate sense buffer for command
+ *
+ * Allocates, if necessary, sense buffer for command. Returns 0 on success
+ * and error code othrwise. Parameter "atomic" should be non-0 if the
+ * function called in atomic context.
+ */
+int scst_alloc_sense(struct scst_cmd *cmd, int atomic)
+{
+ int res = 0;
+ gfp_t gfp_mask = atomic ? GFP_ATOMIC : (GFP_KERNEL|__GFP_NOFAIL);
+
+ if (cmd->sense != NULL)
+ goto memzero;
+
+ cmd->sense = mempool_alloc(scst_sense_mempool, gfp_mask);
+ if (cmd->sense == NULL) {
+ PRINT_CRIT_ERROR("Sense memory allocation failed (op %x). "
+ "The sense data will be lost!!", cmd->cdb[0]);
+ res = -ENOMEM;
+ goto out;
+ }
+
+ cmd->sense_buflen = SCST_SENSE_BUFFERSIZE;
+
+memzero:
+ cmd->sense_valid_len = 0;
+ memset(cmd->sense, 0, cmd->sense_buflen);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(scst_alloc_sense);
+
+/**
+ * scst_alloc_set_sense() - allocate and fill sense buffer for command
+ *
+ * Allocates, if necessary, sense buffer for command and copies in
+ * it data from the supplied sense buffer. Returns 0 on success
+ * and error code othrwise.
+ */
+int scst_alloc_set_sense(struct scst_cmd *cmd, int atomic,
+ const uint8_t *sense, unsigned int len)
+{
+ int res;
+
+ /*
+ * We don't check here if the existing sense is valid or not, because
+ * we suppose the caller did it based on cmd->status.
+ */
+
+ res = scst_alloc_sense(cmd, atomic);
+ if (res != 0) {
+ PRINT_BUFFER("Lost sense", sense, len);
+ goto out;
+ }
+
+ cmd->sense_valid_len = len;
+ if (cmd->sense_buflen < len) {
+ PRINT_WARNING("Sense truncated (needed %d), shall you increase "
+ "SCST_SENSE_BUFFERSIZE? Op: %x", len, cmd->cdb[0]);
+ cmd->sense_valid_len = cmd->sense_buflen;
+ }
+
+ memcpy(cmd->sense, sense, cmd->sense_valid_len);
+ TRACE_BUFFER("Sense set", cmd->sense, cmd->sense_valid_len);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(scst_alloc_set_sense);
+
+/**
+ * scst_set_cmd_error_status() - set error SCSI status
+ * @cmd: SCST command
+ * @status: SCSI status to set
+ *
+ * Description:
+ * Sets error SCSI status in the command and prepares it for returning it.
+ * Returns 0 on success, error code otherwise.
+ */
+int scst_set_cmd_error_status(struct scst_cmd *cmd, int status)
+{
+ int res = 0;
+
+ if (cmd->status != 0) {
+ TRACE_MGMT_DBG("cmd %p already has status %x set", cmd,
+ cmd->status);
+ res = -EEXIST;
+ goto out;
+ }
+
+ cmd->status = status;
+ cmd->host_status = DID_OK;
+
+ cmd->dbl_ua_orig_resp_data_len = cmd->resp_data_len;
+ cmd->dbl_ua_orig_data_direction = cmd->data_direction;
+
+ cmd->data_direction = SCST_DATA_NONE;
+ cmd->resp_data_len = 0;
+ cmd->is_send_status = 1;
+
+ cmd->completed = 1;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(scst_set_cmd_error_status);
+
+static int scst_set_lun_not_supported_request_sense(struct scst_cmd *cmd,
+ int key, int asc, int ascq)
+{
+ int res;
+ int sense_len;
+
+ if (cmd->status != 0) {
+ TRACE_MGMT_DBG("cmd %p already has status %x set", cmd,
+ cmd->status);
+ res = -EEXIST;
+ goto out;
+ }
+
+ if ((cmd->sg != NULL) && SCST_SENSE_VALID(sg_virt(cmd->sg))) {
+ TRACE_MGMT_DBG("cmd %p already has sense set", cmd);
+ res = -EEXIST;
+ goto out;
+ }
+
+ if (cmd->sg == NULL) {
+ if (cmd->bufflen == 0)
+ cmd->bufflen = cmd->cdb[4];
+
+ cmd->sg = scst_alloc(cmd->bufflen, GFP_ATOMIC, &cmd->sg_cnt);
+ if (cmd->sg == NULL) {
+ PRINT_ERROR("Unable to alloc sg for REQUEST SENSE"
+ "(sense %x/%x/%x)", key, asc, ascq);
+ res = 1;
+ goto out;
+ }
+ }
+
+ TRACE_MEM("sg %p alloced for sense for cmd %p (cnt %d, "
+ "len %d)", cmd->sg, cmd, cmd->sg_cnt, cmd->bufflen);
+
+ sense_len = scst_set_sense(sg_virt(cmd->sg),
+ cmd->bufflen, cmd->cdb[1] & 1, key, asc, ascq);
+ scst_set_resp_data_len(cmd, sense_len);
+
+ TRACE_BUFFER("Sense set", sg_virt(cmd->sg), sense_len);
+
+ res = 0;
+ cmd->completed = 1;
+
+out:
+ return res;
+}
+
+static int scst_set_lun_not_supported_inquiry(struct scst_cmd *cmd)
+{
+ int res;
+ uint8_t *buf;
+ int len;
+
+ if (cmd->status != 0) {
+ TRACE_MGMT_DBG("cmd %p already has status %x set", cmd,
+ cmd->status);
+ res = -EEXIST;
+ goto out;
+ }
+
+ if (cmd->sg == NULL) {
+ if (cmd->bufflen == 0)
+ cmd->bufflen = min_t(int, 36, (cmd->cdb[3] << 8) | cmd->cdb[4]);
+
+ cmd->sg = scst_alloc(cmd->bufflen, GFP_ATOMIC, &cmd->sg_cnt);
+ if (cmd->sg == NULL) {
+ PRINT_ERROR("%s", "Unable to alloc sg for INQUIRY "
+ "for not supported LUN");
+ res = 1;
+ goto out;
+ }
+ }
+
+ TRACE_MEM("sg %p alloced INQUIRY for cmd %p (cnt %d, len %d)",
+ cmd->sg, cmd, cmd->sg_cnt, cmd->bufflen);
+
+ buf = sg_virt(cmd->sg);
+ len = min_t(int, 36, cmd->bufflen);
+
+ memset(buf, 0, len);
+ buf[0] = 0x7F; /* Peripheral qualifier 011b, Peripheral device type 1Fh */
+
+ TRACE_BUFFER("INQUIRY for not supported LUN set", buf, len);
+
+ res = 0;
+ cmd->completed = 1;
+
+out:
+ return res;
+}
+
+/**
+ * scst_set_cmd_error() - set error in the command and fill the sense buffer.
+ *
+ * Sets error in the command and fill the sense buffer. Returns 0 on success,
+ * error code otherwise.
+ */
+int scst_set_cmd_error(struct scst_cmd *cmd, int key, int asc, int ascq)
+{
+ int res;
+
+ /*
+ * We need for LOGICAL UNIT NOT SUPPORTED special handling for
+ * REQUEST SENSE and INQUIRY.
+ */
+ if ((key == ILLEGAL_REQUEST) && (asc == 0x25) && (ascq == 0)) {
+ if (cmd->cdb[0] == REQUEST_SENSE)
+ res = scst_set_lun_not_supported_request_sense(cmd,
+ key, asc, ascq);
+ else if (cmd->cdb[0] == INQUIRY)
+ res = scst_set_lun_not_supported_inquiry(cmd);
+ else
+ goto do_sense;
+
+ if (res > 0)
+ goto do_sense;
+ else
+ goto out;
+ }
+
+do_sense:
+ res = scst_set_cmd_error_status(cmd, SAM_STAT_CHECK_CONDITION);
+ if (res != 0)
+ goto out;
+
+ res = scst_alloc_sense(cmd, 1);
+ if (res != 0) {
+ PRINT_ERROR("Lost sense data (key %x, asc %x, ascq %x)",
+ key, asc, ascq);
+ goto out;
+ }
+
+ cmd->sense_valid_len = scst_set_sense(cmd->sense, cmd->sense_buflen,
+ scst_get_cmd_dev_d_sense(cmd), key, asc, ascq);
+ TRACE_BUFFER("Sense set", cmd->sense, cmd->sense_valid_len);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(scst_set_cmd_error);
+
+/**
+ * scst_set_sense() - set sense from KEY/ASC/ASCQ numbers
+ *
+ * Sets the corresponding fields in the sense buffer taking sense type
+ * into account. Returns resulting sense length.
+ */
+int scst_set_sense(uint8_t *buffer, int len, bool d_sense,
+ int key, int asc, int ascq)
+{
+ int res;
+
+ BUG_ON(len == 0);
+
+ memset(buffer, 0, len);
+
+ if (d_sense) {
+ /* Descriptor format */
+ if (len < 8) {
+ PRINT_ERROR("Length %d of sense buffer too small to "
+ "fit sense %x:%x:%x", len, key, asc, ascq);
+ }
+
+ buffer[0] = 0x72; /* Response Code */
+ if (len > 1)
+ buffer[1] = key; /* Sense Key */
+ if (len > 2)
+ buffer[2] = asc; /* ASC */
+ if (len > 3)
+ buffer[3] = ascq; /* ASCQ */
+ res = 8;
+ } else {
+ /* Fixed format */
+ if (len < 18) {
+ PRINT_ERROR("Length %d of sense buffer too small to "
+ "fit sense %x:%x:%x", len, key, asc, ascq);
+ }
+
+ buffer[0] = 0x70; /* Response Code */
+ if (len > 2)
+ buffer[2] = key; /* Sense Key */
+ if (len > 7)
+ buffer[7] = 0x0a; /* Additional Sense Length */
+ if (len > 12)
+ buffer[12] = asc; /* ASC */
+ if (len > 13)
+ buffer[13] = ascq; /* ASCQ */
+ res = 18;
+ }
+
+ TRACE_BUFFER("Sense set", buffer, res);
+ return res;
+}
+EXPORT_SYMBOL(scst_set_sense);
+
+/**
+ * scst_analyze_sense() - analyze sense
+ *
+ * Returns true if sense matches to (key, asc, ascq) and false otherwise.
+ * Valid_mask is one or several SCST_SENSE_*_VALID constants setting valid
+ * (key, asc, ascq) values.
+ */
+bool scst_analyze_sense(const uint8_t *sense, int len, unsigned int valid_mask,
+ int key, int asc, int ascq)
+{
+ bool res = false;
+
+ /* Response Code */
+ if ((sense[0] == 0x70) || (sense[0] == 0x71)) {
+ /* Fixed format */
+
+ /* Sense Key */
+ if (valid_mask & SCST_SENSE_KEY_VALID) {
+ if (len < 3)
+ goto out;
+ if (sense[2] != key)
+ goto out;
+ }
+
+ /* ASC */
+ if (valid_mask & SCST_SENSE_ASC_VALID) {
+ if (len < 13)
+ goto out;
+ if (sense[12] != asc)
+ goto out;
+ }
+
+ /* ASCQ */
+ if (valid_mask & SCST_SENSE_ASCQ_VALID) {
+ if (len < 14)
+ goto out;
+ if (sense[13] != ascq)
+ goto out;
+ }
+ } else if ((sense[0] == 0x72) || (sense[0] == 0x73)) {
+ /* Descriptor format */
+
+ /* Sense Key */
+ if (valid_mask & SCST_SENSE_KEY_VALID) {
+ if (len < 2)
+ goto out;
+ if (sense[1] != key)
+ goto out;
+ }
+
+ /* ASC */
+ if (valid_mask & SCST_SENSE_ASC_VALID) {
+ if (len < 3)
+ goto out;
+ if (sense[2] != asc)
+ goto out;
+ }
+
+ /* ASCQ */
+ if (valid_mask & SCST_SENSE_ASCQ_VALID) {
+ if (len < 4)
+ goto out;
+ if (sense[3] != ascq)
+ goto out;
+ }
+ } else
+ goto out;
+
+ res = true;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(scst_analyze_sense);
+
+/**
+ * scst_is_ua_sense() - determine if the sense is UA sense
+ *
+ * Returns true if the sense is valid and carrying a Unit
+ * Attention or false otherwise.
+ */
+bool scst_is_ua_sense(const uint8_t *sense, int len)
+{
+ if (SCST_SENSE_VALID(sense))
+ return scst_analyze_sense(sense, len,
+ SCST_SENSE_KEY_VALID, UNIT_ATTENTION, 0, 0);
+ else
+ return false;
+}
+EXPORT_SYMBOL(scst_is_ua_sense);
+
+bool scst_is_ua_global(const uint8_t *sense, int len)
+{
+ bool res;
+
+ /* Changing it don't forget to change scst_requeue_ua() as well!! */
+
+ if (scst_analyze_sense(sense, len, SCST_SENSE_ALL_VALID,
+ SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed)))
+ res = true;
+ else
+ res = false;
+
+ return res;
+}
+
+/**
+ * scst_check_convert_sense() - check sense type and convert it if needed
+ *
+ * Checks if sense in the sense buffer, if any, is in the correct format.
+ * If not, converts it in the correct format.
+ */
+void scst_check_convert_sense(struct scst_cmd *cmd)
+{
+ bool d_sense;
+
+ if ((cmd->sense == NULL) || (cmd->status != SAM_STAT_CHECK_CONDITION))
+ goto out;
+
+ d_sense = scst_get_cmd_dev_d_sense(cmd);
+ if (d_sense && ((cmd->sense[0] == 0x70) || (cmd->sense[0] == 0x71))) {
+ TRACE_MGMT_DBG("Converting fixed sense to descriptor (cmd %p)",
+ cmd);
+ if ((cmd->sense_valid_len < 18)) {
+ PRINT_ERROR("Sense too small to convert (%d, "
+ "type: fixed)", cmd->sense_buflen);
+ goto out;
+ }
+ cmd->sense_valid_len = scst_set_sense(cmd->sense, cmd->sense_buflen,
+ d_sense, cmd->sense[2], cmd->sense[12], cmd->sense[13]);
+ } else if (!d_sense && ((cmd->sense[0] == 0x72) ||
+ (cmd->sense[0] == 0x73))) {
+ TRACE_MGMT_DBG("Converting descriptor sense to fixed (cmd %p)",
+ cmd);
+ if ((cmd->sense_buflen < 18) || (cmd->sense_valid_len < 8)) {
+ PRINT_ERROR("Sense too small to convert (%d, "
+ "type: descryptor, valid %d)",
+ cmd->sense_buflen, cmd->sense_valid_len);
+ goto out;
+ }
+ cmd->sense_valid_len = scst_set_sense(cmd->sense,
+ cmd->sense_buflen, d_sense,
+ cmd->sense[1], cmd->sense[2], cmd->sense[3]);
+ }
+
+out:
+ return;
+}
+EXPORT_SYMBOL(scst_check_convert_sense);
+
+static int scst_set_cmd_error_sense(struct scst_cmd *cmd, uint8_t *sense,
+ unsigned int len)
+{
+ int res;
+
+ res = scst_set_cmd_error_status(cmd, SAM_STAT_CHECK_CONDITION);
+ if (res != 0)
+ goto out;
+
+ res = scst_alloc_set_sense(cmd, 1, sense, len);
+
+out:
+ return res;
+}
+
+/**
+ * scst_set_busy() - set BUSY or TASK QUEUE FULL status
+ *
+ * Sets BUSY or TASK QUEUE FULL status depending on if this session has other
+ * outstanding commands or not.
+ */
+void scst_set_busy(struct scst_cmd *cmd)
+{
+ int c = atomic_read(&cmd->sess->sess_cmd_count);
+
+ if ((c <= 1) || (cmd->sess->init_phase != SCST_SESS_IPH_READY)) {
+ scst_set_cmd_error_status(cmd, SAM_STAT_BUSY);
+ TRACE(TRACE_FLOW_CONTROL, "Sending BUSY status to initiator %s "
+ "(cmds count %d, queue_type %x, sess->init_phase %d)",
+ cmd->sess->initiator_name, c,
+ cmd->queue_type, cmd->sess->init_phase);
+ } else {
+ scst_set_cmd_error_status(cmd, SAM_STAT_TASK_SET_FULL);
+ TRACE(TRACE_FLOW_CONTROL, "Sending QUEUE_FULL status to "
+ "initiator %s (cmds count %d, queue_type %x, "
+ "sess->init_phase %d)", cmd->sess->initiator_name, c,
+ cmd->queue_type, cmd->sess->init_phase);
+ }
+ return;
+}
+EXPORT_SYMBOL(scst_set_busy);
+
+/**
+ * scst_set_initial_UA() - set initial Unit Attention
+ *
+ * Sets initial Unit Attention on all devices of the session,
+ * replacing default scst_sense_reset_UA
+ */
+void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq)
+{
+ int i;
+
+ TRACE_MGMT_DBG("Setting for sess %p initial UA %x/%x/%x", sess, key,
+ asc, ascq);
+
+ /* Protect sess_tgt_dev_list_hash */
+ mutex_lock(&scst_mutex);
+
+ for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
+ struct list_head *sess_tgt_dev_list_head =
+ &sess->sess_tgt_dev_list_hash[i];
+ struct scst_tgt_dev *tgt_dev;
+
+ list_for_each_entry(tgt_dev, sess_tgt_dev_list_head,
+ sess_tgt_dev_list_entry) {
+ spin_lock_bh(&tgt_dev->tgt_dev_lock);
+ if (!list_empty(&tgt_dev->UA_list)) {
+ struct scst_tgt_dev_UA *ua;
+
+ ua = list_entry(tgt_dev->UA_list.next,
+ typeof(*ua), UA_list_entry);
+ if (scst_analyze_sense(ua->UA_sense_buffer,
+ ua->UA_valid_sense_len,
+ SCST_SENSE_ALL_VALID,
+ SCST_LOAD_SENSE(scst_sense_reset_UA))) {
+ ua->UA_valid_sense_len = scst_set_sense(
+ ua->UA_sense_buffer,
+ sizeof(ua->UA_sense_buffer),
+ tgt_dev->dev->d_sense,
+ key, asc, ascq);
+ } else
+ PRINT_ERROR("%s",
+ "The first UA isn't RESET UA");
+ } else
+ PRINT_ERROR("%s", "There's no RESET UA to "
+ "replace");
+ spin_unlock_bh(&tgt_dev->tgt_dev_lock);
+ }
+ }
+
+ mutex_unlock(&scst_mutex);
+ return;
+}
+EXPORT_SYMBOL(scst_set_initial_UA);
+
+static struct scst_aen *scst_alloc_aen(struct scst_session *sess,
+ uint64_t unpacked_lun)
+{
+ struct scst_aen *aen;
+
+ aen = mempool_alloc(scst_aen_mempool, GFP_KERNEL);
+ if (aen == NULL) {
+ PRINT_ERROR("AEN memory allocation failed. Corresponding "
+ "event notification will not be performed (initiator "
+ "%s)", sess->initiator_name);
+ goto out;
+ }
+ memset(aen, 0, sizeof(*aen));
+
+ aen->sess = sess;
+ scst_sess_get(sess);
+
+ aen->lun = scst_pack_lun(unpacked_lun, sess->acg->addr_method);
+
+out:
+ return aen;
+};
+
+static void scst_free_aen(struct scst_aen *aen)
+{
+
+ scst_sess_put(aen->sess);
+ mempool_free(aen, scst_aen_mempool);
+ return;
+};
+
+/* Must be called under scst_mutex */
+void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
+ int key, int asc, int ascq)
+{
+ struct scst_tgt_template *tgtt = tgt_dev->sess->tgt->tgtt;
+ uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
+ int sl;
+
+ if (tgtt->report_aen != NULL) {
+ struct scst_aen *aen;
+ int rc;
+
+ aen = scst_alloc_aen(tgt_dev->sess, tgt_dev->lun);
+ if (aen == NULL)
+ goto queue_ua;
+
+ aen->event_fn = SCST_AEN_SCSI;
+ aen->aen_sense_len = scst_set_sense(aen->aen_sense,
+ sizeof(aen->aen_sense), tgt_dev->dev->d_sense,
+ key, asc, ascq);
+
+ TRACE_DBG("Calling target's %s report_aen(%p)",
+ tgtt->name, aen);
+ rc = tgtt->report_aen(aen);
+ TRACE_DBG("Target's %s report_aen(%p) returned %d",
+ tgtt->name, aen, rc);
+ if (rc == SCST_AEN_RES_SUCCESS)
+ goto out;
+
+ scst_free_aen(aen);
+ }
+
+queue_ua:
+ TRACE_MGMT_DBG("AEN not supported, queuing plain UA (tgt_dev %p)",
+ tgt_dev);
+ sl = scst_set_sense(sense_buffer, sizeof(sense_buffer),
+ tgt_dev->dev->d_sense, key, asc, ascq);
+ scst_check_set_UA(tgt_dev, sense_buffer, sl, 0);
+
+out:
+ return;
+}
+
+/**
+ * scst_capacity_data_changed() - notify SCST about device capacity change
+ *
+ * Notifies SCST core that dev has changed its capacity. Called under no locks.
+ */
+void scst_capacity_data_changed(struct scst_device *dev)
+{
+ struct scst_tgt_dev *tgt_dev;
+
+ if (dev->type != TYPE_DISK) {
+ TRACE_MGMT_DBG("Device type %d isn't for CAPACITY DATA "
+ "CHANGED UA", dev->type);
+ goto out;
+ }
+
+ TRACE_MGMT_DBG("CAPACITY DATA CHANGED (dev %p)", dev);
+
+ mutex_lock(&scst_mutex);
+
+ list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
+ dev_tgt_dev_list_entry) {
+ scst_gen_aen_or_ua(tgt_dev,
+ SCST_LOAD_SENSE(scst_sense_capacity_data_changed));
+ }
+
+ mutex_unlock(&scst_mutex);
+
+out:
+ return;
+}
+EXPORT_SYMBOL_GPL(scst_capacity_data_changed);
+
+static inline bool scst_is_report_luns_changed_type(int type)
+{
+ switch (type) {
+ case TYPE_DISK:
+ case TYPE_TAPE:
+ case TYPE_PRINTER:
+ case TYPE_PROCESSOR:
+ case TYPE_WORM:
+ case TYPE_ROM:
+ case TYPE_SCANNER:
+ case TYPE_MOD:
+ case TYPE_MEDIUM_CHANGER:
+ case TYPE_RAID:
+ case TYPE_ENCLOSURE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* scst_mutex supposed to be held */
+static void scst_queue_report_luns_changed_UA(struct scst_session *sess,
+ int flags)
+{
+ uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
+ struct list_head *shead;
+ struct scst_tgt_dev *tgt_dev;
+ int i;
+
+ TRACE_MGMT_DBG("Queuing REPORTED LUNS DATA CHANGED UA "
+ "(sess %p)", sess);
+
+ local_bh_disable();
+
+ for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
+ shead = &sess->sess_tgt_dev_list_hash[i];
+
+ list_for_each_entry(tgt_dev, shead,
+ sess_tgt_dev_list_entry) {
+ /* Lockdep triggers here a false positive.. */
+ spin_lock(&tgt_dev->tgt_dev_lock);
+ }
+ }
+
+ for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
+ shead = &sess->sess_tgt_dev_list_hash[i];
+
+ list_for_each_entry(tgt_dev, shead,
+ sess_tgt_dev_list_entry) {
+ int sl;
+
+ if (!scst_is_report_luns_changed_type(
+ tgt_dev->dev->type))
+ continue;
+
+ sl = scst_set_sense(sense_buffer, sizeof(sense_buffer),
+ tgt_dev->dev->d_sense,
+ SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed));
+
+ __scst_check_set_UA(tgt_dev, sense_buffer,
+ sl, flags | SCST_SET_UA_FLAG_GLOBAL);
+ }
+ }
+
+ for (i = TGT_DEV_HASH_SIZE-1; i >= 0; i--) {
+ shead = &sess->sess_tgt_dev_list_hash[i];
+
+ list_for_each_entry_reverse(tgt_dev,
+ shead, sess_tgt_dev_list_entry) {
+ spin_unlock(&tgt_dev->tgt_dev_lock);
+ }
+ }
+
+ local_bh_enable();
+ return;
+}
+
+/* The activity supposed to be suspended and scst_mutex held */
+static void scst_report_luns_changed_sess(struct scst_session *sess)
+{
+ int i;
+ struct scst_tgt_template *tgtt = sess->tgt->tgtt;
+ int d_sense = 0;
+ uint64_t lun = 0;
+
+ TRACE_DBG("REPORTED LUNS DATA CHANGED (sess %p)", sess);
+
+ for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
+ struct list_head *shead;
+ struct scst_tgt_dev *tgt_dev;
+
+ shead = &sess->sess_tgt_dev_list_hash[i];
+
+ list_for_each_entry(tgt_dev, shead,
+ sess_tgt_dev_list_entry) {
+ if (scst_is_report_luns_changed_type(
+ tgt_dev->dev->type)) {
+ lun = tgt_dev->lun;
+ d_sense = tgt_dev->dev->d_sense;
+ goto found;
+ }
+ }
+ }
+
+found:
+ if (tgtt->report_aen != NULL) {
+ struct scst_aen *aen;
+ int rc;
+
+ aen = scst_alloc_aen(sess, lun);
+ if (aen == NULL)
+ goto queue_ua;
+
+ aen->event_fn = SCST_AEN_SCSI;
+ aen->aen_sense_len = scst_set_sense(aen->aen_sense,
+ sizeof(aen->aen_sense), d_sense,
+ SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed));
+
+ TRACE_DBG("Calling target's %s report_aen(%p)",
+ tgtt->name, aen);
+ rc = tgtt->report_aen(aen);
+ TRACE_DBG("Target's %s report_aen(%p) returned %d",
+ tgtt->name, aen, rc);
+ if (rc == SCST_AEN_RES_SUCCESS)
+ goto out;
+
+ scst_free_aen(aen);
+ }
+
+queue_ua:
+ scst_queue_report_luns_changed_UA(sess, 0);
+
+out:
+ return;
+}
+
+/* The activity supposed to be suspended and scst_mutex held */
+void scst_report_luns_changed(struct scst_acg *acg)
+{
+ struct scst_session *sess;
+
+ TRACE_MGMT_DBG("REPORTED LUNS DATA CHANGED (acg %s)", acg->acg_name);
+
+ list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) {
+ scst_report_luns_changed_sess(sess);
+ }
+ return;
+}
+
+/**
+ * scst_aen_done() - AEN processing done
+ *
+ * Notifies SCST that the driver has sent the AEN and it
+ * can be freed now. Don't forget to set the delivery status, if it
+ * isn't success, using scst_set_aen_delivery_status() before calling
+ * this function.
+ */
+void scst_aen_done(struct scst_aen *aen)
+{
+
+ TRACE_MGMT_DBG("AEN %p (fn %d) done (initiator %s)", aen,
+ aen->event_fn, aen->sess->initiator_name);
+
+ if (aen->delivery_status == SCST_AEN_RES_SUCCESS)
+ goto out_free;
+
+ if (aen->event_fn != SCST_AEN_SCSI)
+ goto out_free;
+
+ TRACE_MGMT_DBG("Delivery of SCSI AEN failed (initiator %s)",
+ aen->sess->initiator_name);
+
+ if (scst_analyze_sense(aen->aen_sense, aen->aen_sense_len,
+ SCST_SENSE_ALL_VALID, SCST_LOAD_SENSE(
+ scst_sense_reported_luns_data_changed))) {
+ mutex_lock(&scst_mutex);
+ scst_queue_report_luns_changed_UA(aen->sess,
+ SCST_SET_UA_FLAG_AT_HEAD);
+ mutex_unlock(&scst_mutex);
+ } else {
+ struct list_head *shead;
+ struct scst_tgt_dev *tgt_dev;
+ uint64_t lun;
+
+ lun = scst_unpack_lun((uint8_t *)&aen->lun, sizeof(aen->lun));
+
+ mutex_lock(&scst_mutex);
+
+ /* tgt_dev might get dead, so we need to reseek it */
+ shead = &aen->sess->sess_tgt_dev_list_hash[HASH_VAL(lun)];
+ list_for_each_entry(tgt_dev, shead,
+ sess_tgt_dev_list_entry) {
+ if (tgt_dev->lun == lun) {
+ TRACE_MGMT_DBG("Requeuing failed AEN UA for "
+ "tgt_dev %p", tgt_dev);
+ scst_check_set_UA(tgt_dev, aen->aen_sense,
+ aen->aen_sense_len,
+ SCST_SET_UA_FLAG_AT_HEAD);
+ break;
+ }
+ }
+
+ mutex_unlock(&scst_mutex);
+ }
+
+out_free:
+ scst_free_aen(aen);
+ return;
+}
+EXPORT_SYMBOL(scst_aen_done);
+
+void scst_requeue_ua(struct scst_cmd *cmd)
+{
+
+ if (scst_analyze_sense(cmd->sense, cmd->sense_valid_len,
+ SCST_SENSE_ALL_VALID,
+ SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed))) {
+ TRACE_MGMT_DBG("Requeuing REPORTED LUNS DATA CHANGED UA "
+ "for delivery failed cmd %p", cmd);
+ mutex_lock(&scst_mutex);
+ scst_queue_report_luns_changed_UA(cmd->sess,
+ SCST_SET_UA_FLAG_AT_HEAD);
+ mutex_unlock(&scst_mutex);
+ } else {
+ TRACE_MGMT_DBG("Requeuing UA for delivery failed cmd %p", cmd);
+ scst_check_set_UA(cmd->tgt_dev, cmd->sense,
+ cmd->sense_valid_len, SCST_SET_UA_FLAG_AT_HEAD);
+ }
+ return;
+}
+
+/* The activity supposed to be suspended and scst_mutex held */
+static void scst_check_reassign_sess(struct scst_session *sess)
+{
+ struct scst_acg *acg, *old_acg;
+ struct scst_acg_dev *acg_dev;
+ int i;
+ struct list_head *shead;
+ struct scst_tgt_dev *tgt_dev;
+ bool luns_changed = false;
+ bool add_failed, something_freed, not_needed_freed = false;
+
+ TRACE_MGMT_DBG("Checking reassignment for sess %p (initiator %s)",
+ sess, sess->initiator_name);
+
+ acg = scst_find_acg(sess);
+ if (acg == sess->acg) {
+ TRACE_MGMT_DBG("No reassignment for sess %p", sess);
+ goto out;
+ }
+
+ TRACE_MGMT_DBG("sess %p will be reassigned from acg %s to acg %s",
+ sess, sess->acg->acg_name, acg->acg_name);
+
+ old_acg = sess->acg;
+ sess->acg = NULL; /* to catch implicit dependencies earlier */
+
+retry_add:
+ add_failed = false;
+ list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
+ unsigned int inq_changed_ua_needed = 0;
+
+ for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
+ shead = &sess->sess_tgt_dev_list_hash[i];
+
+ list_for_each_entry(tgt_dev, shead,
+ sess_tgt_dev_list_entry) {
+ if ((tgt_dev->dev == acg_dev->dev) &&
+ (tgt_dev->lun == acg_dev->lun) &&
+ (tgt_dev->acg_dev->rd_only == acg_dev->rd_only)) {
+ TRACE_MGMT_DBG("sess %p: tgt_dev %p for "
+ "LUN %lld stays the same",
+ sess, tgt_dev,
+ (unsigned long long)tgt_dev->lun);
+ tgt_dev->acg_dev = acg_dev;
+ goto next;
+ } else if (tgt_dev->lun == acg_dev->lun)
+ inq_changed_ua_needed = 1;
+ }
+ }
+
+ luns_changed = true;
+
+ TRACE_MGMT_DBG("sess %p: Allocing new tgt_dev for LUN %lld",
+ sess, (unsigned long long)acg_dev->lun);
+
+ tgt_dev = scst_alloc_add_tgt_dev(sess, acg_dev);
+ if (tgt_dev == NULL) {
+ add_failed = true;
+ break;
+ }
+
+ tgt_dev->inq_changed_ua_needed = inq_changed_ua_needed ||
+ not_needed_freed;
+next:
+ continue;
+ }
+
+ something_freed = false;
+ not_needed_freed = true;
+ for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
+ struct scst_tgt_dev *t;
+ shead = &sess->sess_tgt_dev_list_hash[i];
+
+ list_for_each_entry_safe(tgt_dev, t, shead,
+ sess_tgt_dev_list_entry) {
+ if (tgt_dev->acg_dev->acg != acg) {
+ TRACE_MGMT_DBG("sess %p: Deleting not used "
+ "tgt_dev %p for LUN %lld",
+ sess, tgt_dev,
+ (unsigned long long)tgt_dev->lun);
+ luns_changed = true;
+ something_freed = true;
+ scst_free_tgt_dev(tgt_dev);
+ }
+ }
+ }
+
+ if (add_failed && something_freed) {
+ TRACE_MGMT_DBG("sess %p: Retrying adding new tgt_devs", sess);
+ goto retry_add;
+ }
+
+ sess->acg = acg;
+
+ TRACE_DBG("Moving sess %p from acg %s to acg %s", sess,
+ old_acg->acg_name, acg->acg_name);
+ list_move_tail(&sess->acg_sess_list_entry, &acg->acg_sess_list);
+
+ if (luns_changed) {
+ scst_report_luns_changed_sess(sess);
+
+ for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
+ shead = &sess->sess_tgt_dev_list_hash[i];
+
+ list_for_each_entry(tgt_dev, shead,
+ sess_tgt_dev_list_entry) {
+ if (tgt_dev->inq_changed_ua_needed) {
+ TRACE_MGMT_DBG("sess %p: Setting "
+ "INQUIRY DATA HAS CHANGED UA "
+ "(tgt_dev %p)", sess, tgt_dev);
+
+ tgt_dev->inq_changed_ua_needed = 0;
+
+ scst_gen_aen_or_ua(tgt_dev,
+ SCST_LOAD_SENSE(scst_sense_inquery_data_changed));
+ }
+ }
+ }
+ }
+
+out:
+ return;
+}
+
+/* The activity supposed to be suspended and scst_mutex held */
+void scst_check_reassign_sessions(void)
+{
+ struct scst_tgt_template *tgtt;
+
+ list_for_each_entry(tgtt, &scst_template_list, scst_template_list_entry) {
+ struct scst_tgt *tgt;
+ list_for_each_entry(tgt, &tgtt->tgt_list, tgt_list_entry) {
+ struct scst_session *sess;
+ list_for_each_entry(sess, &tgt->sess_list,
+ sess_list_entry) {
+ scst_check_reassign_sess(sess);
+ }
+ }
+ }
+ return;
+}
+
+/**
+ * scst_get_cmd_abnormal_done_state() - get command's next abnormal done state
+ *
+ * Returns the next state of the SCSI target state machine in case if command's
+ * completed abnormally.
+ */
+int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd)
+{
+ int res;
+
+ switch (cmd->state) {
+ case SCST_CMD_STATE_INIT_WAIT:
+ case SCST_CMD_STATE_INIT:
+ case SCST_CMD_STATE_PRE_PARSE:
+ case SCST_CMD_STATE_DEV_PARSE:
+ if (cmd->preprocessing_only) {
+ res = SCST_CMD_STATE_PREPROCESSING_DONE;
+ break;
+ } /* else go through */
+ case SCST_CMD_STATE_DEV_DONE:
+ if (cmd->internal)
+ res = SCST_CMD_STATE_FINISHED_INTERNAL;
+ else
+ res = SCST_CMD_STATE_PRE_XMIT_RESP;
+ break;
+
+ case SCST_CMD_STATE_PRE_DEV_DONE:
+ case SCST_CMD_STATE_MODE_SELECT_CHECKS:
+ res = SCST_CMD_STATE_DEV_DONE;
+ break;
+
+ case SCST_CMD_STATE_PRE_XMIT_RESP:
+ res = SCST_CMD_STATE_XMIT_RESP;
+ break;
+
+ case SCST_CMD_STATE_PREPROCESSING_DONE:
+ case SCST_CMD_STATE_PREPROCESSING_DONE_CALLED:
+ if (cmd->tgt_dev == NULL)
+ res = SCST_CMD_STATE_PRE_XMIT_RESP;
+ else
+ res = SCST_CMD_STATE_PRE_DEV_DONE;
+ break;
+
+ case SCST_CMD_STATE_PREPARE_SPACE:
+ if (cmd->preprocessing_only) {
+ res = SCST_CMD_STATE_PREPROCESSING_DONE;
+ break;
+ } /* else go through */
+ case SCST_CMD_STATE_RDY_TO_XFER:
+ case SCST_CMD_STATE_DATA_WAIT:
+ case SCST_CMD_STATE_TGT_PRE_EXEC:
+ case SCST_CMD_STATE_SEND_FOR_EXEC:
+ case SCST_CMD_STATE_LOCAL_EXEC:
+ case SCST_CMD_STATE_REAL_EXEC:
+ case SCST_CMD_STATE_REAL_EXECUTING:
+ res = SCST_CMD_STATE_PRE_DEV_DONE;
+ break;
+
+ default:
+ PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %x)",
+ cmd->state, cmd, cmd->cdb[0]);
+ BUG();
+ /* Invalid state to supress compiler's warning */
+ res = SCST_CMD_STATE_LAST_ACTIVE;
+ }
+ return res;
+}
+EXPORT_SYMBOL_GPL(scst_get_cmd_abnormal_done_state);
+
+/**
+ * scst_set_cmd_abnormal_done_state() - set command's next abnormal done state
+ *
+ * Sets state of the SCSI target state machine in case if command's completed
+ * abnormally.
+ */
+void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd)
+{
+
+#ifdef CONFIG_SCST_EXTRACHECKS
+ switch (cmd->state) {
+ case SCST_CMD_STATE_XMIT_RESP:
+ case SCST_CMD_STATE_FINISHED:
+ case SCST_CMD_STATE_FINISHED_INTERNAL:
+ case SCST_CMD_STATE_XMIT_WAIT:
+ PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %x)",
+ cmd->state, cmd, cmd->cdb[0]);
+ BUG();
+ }
+#endif
+
+ cmd->state = scst_get_cmd_abnormal_done_state(cmd);
+
+#ifdef CONFIG_SCST_EXTRACHECKS
+ if (((cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP) &&
+ (cmd->state != SCST_CMD_STATE_PREPROCESSING_DONE)) &&
+ (cmd->tgt_dev == NULL) && !cmd->internal) {
+ PRINT_CRIT_ERROR("Wrong not inited cmd state %d (cmd %p, "
+ "op %x)", cmd->state, cmd, cmd->cdb[0]);
+ BUG();
+ }
+#endif
+ return;
+}
+EXPORT_SYMBOL_GPL(scst_set_cmd_abnormal_done_state);
+
+/**
+ * scst_set_resp_data_len() - set response data length
+ *
+ * Sets response data length for cmd and truncates its SG vector accordingly.
+ *
+ * The cmd->resp_data_len must not be set directly, it must be set only
+ * using this function. Value of resp_data_len must be <= cmd->bufflen.
+ */
+void scst_set_resp_data_len(struct scst_cmd *cmd, int resp_data_len)
+{
+ int i, l;
+
+ scst_check_restore_sg_buff(cmd);
+ cmd->resp_data_len = resp_data_len;
+
+ if (resp_data_len == cmd->bufflen)
+ goto out;
+
+ l = 0;
+ for (i = 0; i < cmd->sg_cnt; i++) {
+ l += cmd->sg[i].length;
+ if (l >= resp_data_len) {
+ int left = resp_data_len - (l - cmd->sg[i].length);
+#ifdef CONFIG_SCST_DEBUG
+ TRACE(TRACE_SG_OP|TRACE_MEMORY, "cmd %p (tag %llu), "
+ "resp_data_len %d, i %d, cmd->sg[i].length %d, "
+ "left %d",
+ cmd, (long long unsigned int)cmd->tag,
+ resp_data_len, i,
+ cmd->sg[i].length, left);
+#endif
+ cmd->orig_sg_cnt = cmd->sg_cnt;
+ cmd->orig_sg_entry = i;
+ cmd->orig_entry_len = cmd->sg[i].length;
+ cmd->sg_cnt = (left > 0) ? i+1 : i;
+ cmd->sg[i].length = left;
+ cmd->sg_buff_modified = 1;
+ break;
+ }
+ }
+
+out:
+ return;
+}
+EXPORT_SYMBOL_GPL(scst_set_resp_data_len);
+
+/* No locks */
+int scst_queue_retry_cmd(struct scst_cmd *cmd, int finished_cmds)
+{
+ struct scst_tgt *tgt = cmd->tgt;
+ int res = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tgt->tgt_lock, flags);
+ tgt->retry_cmds++;
+ /*
+ * Memory barrier is needed here, because we need the exact order
+ * between the read and write between retry_cmds and finished_cmds to
+ * not miss the case when a command finished while we queuing it for
+ * retry after the finished_cmds check.
+ */
+ smp_mb();
+ TRACE_RETRY("TGT QUEUE FULL: incrementing retry_cmds %d",
+ tgt->retry_cmds);
+ if (finished_cmds != atomic_read(&tgt->finished_cmds)) {
+ /* At least one cmd finished, so try again */
+ tgt->retry_cmds--;
+ TRACE_RETRY("Some command(s) finished, direct retry "
+ "(finished_cmds=%d, tgt->finished_cmds=%d, "
+ "retry_cmds=%d)", finished_cmds,
+ atomic_read(&tgt->finished_cmds), tgt->retry_cmds);
+ res = -1;
+ goto out_unlock_tgt;
+ }
+
+ TRACE_RETRY("Adding cmd %p to retry cmd list", cmd);
+ list_add_tail(&cmd->cmd_list_entry, &tgt->retry_cmd_list);
+
+ if (!tgt->retry_timer_active) {
+ tgt->retry_timer.expires = jiffies + SCST_TGT_RETRY_TIMEOUT;
+ add_timer(&tgt->retry_timer);
+ tgt->retry_timer_active = 1;
+ }
+
+out_unlock_tgt:
+ spin_unlock_irqrestore(&tgt->tgt_lock, flags);
+ return res;
+}
+
+/* Returns 0 to continue, >0 to restart, <0 to break */
+static int scst_check_hw_pending_cmd(struct scst_cmd *cmd,
+ unsigned long cur_time, unsigned long max_time,
+ struct scst_session *sess, unsigned long *flags,
+ struct scst_tgt_template *tgtt)
+{
+ int res = -1; /* break */
+
+ TRACE_DBG("cmd %p, hw_pending %d, proc time %ld, "
+ "pending time %ld", cmd, cmd->cmd_hw_pending,
+ (long)(cur_time - cmd->start_time) / HZ,
+ (long)(cur_time - cmd->hw_pending_start) / HZ);
+
+ if (time_before_eq(cur_time, cmd->start_time + max_time)) {
+ /* Cmds are ordered, so no need to check more */
+ goto out;
+ }
+
+ if (!cmd->cmd_hw_pending) {
+ res = 0; /* continue */
+ goto out;
+ }
+
+ if (time_before(cur_time, cmd->hw_pending_start + max_time)) {
+ /* Cmds are ordered, so no need to check more */
+ goto out;
+ }
+
+ TRACE_MGMT_DBG("Cmd %p HW pending for too long %ld (state %x)",
+ cmd, (cur_time - cmd->hw_pending_start) / HZ,
+ cmd->state);
+
+ cmd->cmd_hw_pending = 0;
+
+ spin_unlock_irqrestore(&sess->sess_list_lock, *flags);
+ tgtt->on_hw_pending_cmd_timeout(cmd);
+ spin_lock_irqsave(&sess->sess_list_lock, *flags);
+
+ res = 1; /* restart */
+
+out:
+ return res;
+}
+
+static void scst_hw_pending_work_fn(struct delayed_work *work)
+{
+ struct scst_session *sess = container_of(work, struct scst_session,
+ hw_pending_work);
+ struct scst_tgt_template *tgtt = sess->tgt->tgtt;
+ struct scst_cmd *cmd;
+ unsigned long cur_time = jiffies;
+ unsigned long flags;
+ unsigned long max_time = tgtt->max_hw_pending_time * HZ;
+
+ TRACE_DBG("HW pending work (sess %p, max time %ld)", sess, max_time/HZ);
+
+ clear_bit(SCST_SESS_HW_PENDING_WORK_SCHEDULED, &sess->sess_aflags);
+
+ spin_lock_irqsave(&sess->sess_list_lock, flags);
+
+restart:
+ list_for_each_entry(cmd, &sess->sess_cmd_list, sess_cmd_list_entry) {
+ int rc;
+
+ rc = scst_check_hw_pending_cmd(cmd, cur_time, max_time, sess,
+ &flags, tgtt);
+ if (rc < 0)
+ break;
+ else if (rc == 0)
+ continue;
+ else
+ goto restart;
+ }
+
+ if (!list_empty(&sess->sess_cmd_list)) {
+ /*
+ * For stuck cmds if there is no activity we might need to have
+ * one more run to release them, so reschedule once again.
+ */
+ TRACE_DBG("Sched HW pending work for sess %p (max time %d)",
+ sess, tgtt->max_hw_pending_time);
+ set_bit(SCST_SESS_HW_PENDING_WORK_SCHEDULED, &sess->sess_aflags);
+ schedule_delayed_work(&sess->hw_pending_work,
+ tgtt->max_hw_pending_time * HZ);
+ }
+
+ spin_unlock_irqrestore(&sess->sess_list_lock, flags);
+ return;
+}
+
+bool scst_is_relative_target_port_id_unique(uint16_t id, struct scst_tgt *t)
+{
+ bool res = true;
+ struct scst_tgt_template *tgtt;
+
+ mutex_lock(&scst_mutex);
+ list_for_each_entry(tgtt, &scst_template_list,
+ scst_template_list_entry) {
+ struct scst_tgt *tgt;
+ list_for_each_entry(tgt, &tgtt->tgt_list, tgt_list_entry) {
+ if (tgt == t)
+ continue;
+ if (id == tgt->rel_tgt_id) {
+ res = false;
+ break;
+ }
+ }
+ }
+ mutex_unlock(&scst_mutex);
+ return res;
+}
+
+int gen_relative_target_port_id(uint16_t *id)
+{
+ int res = -EOVERFLOW;
+ static unsigned long rti = SCST_MIN_REL_TGT_ID, rti_prev;
+
+ rti_prev = rti;
+ do {
+ if (scst_is_relative_target_port_id_unique(rti, NULL)) {
+ *id = (uint16_t)rti++;
+ res = 0;
+ goto out;
+ }
+ rti++;
+ if (rti > SCST_MAX_REL_TGT_ID)
+ rti = SCST_MIN_REL_TGT_ID;
+ } while (rti != rti_prev);
+
+ PRINT_ERROR("%s", "Unable to create unique relative target port id");
+
+out:
+ return res;
+}
+
+int scst_alloc_tgt(struct scst_tgt_template *tgtt, struct scst_tgt **tgt)
+{
+ struct scst_tgt *t;
+ int res = 0;
+
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL) {
+ TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of tgt failed");
+ res = -ENOMEM;
+ goto out;
+ }
+
+ INIT_LIST_HEAD(&t->sess_list);
+ init_waitqueue_head(&t->unreg_waitQ);
+ t->tgtt = tgtt;
+ t->sg_tablesize = tgtt->sg_tablesize;
+ spin_lock_init(&t->tgt_lock);
+ INIT_LIST_HEAD(&t->retry_cmd_list);
+ atomic_set(&t->finished_cmds, 0);
+ init_timer(&t->retry_timer);
+ t->retry_timer.data = (unsigned long)t;
+ t->retry_timer.function = scst_tgt_retry_timer_fn;
+
+ *tgt = t;
+
+out:
+ return res;
+}
+
+void scst_free_tgt(struct scst_tgt *tgt)
+{
+
+ if (tgt->default_acg != NULL)
+ scst_free_acg(tgt->default_acg);
+
+ kfree(tgt->tgt_name);
+
+ kfree(tgt);
+ return;
+}
+
+/* Called under scst_mutex and suspended activity */
+int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev)
+{
+ struct scst_device *dev;
+ int res = 0;
+
+ dev = kzalloc(sizeof(*dev), gfp_mask);
+ if (dev == NULL) {
+ TRACE(TRACE_OUT_OF_MEM, "%s",
+ "Allocation of scst_device failed");
+ res = -ENOMEM;
+ goto out;
+ }
+
+ dev->handler = &scst_null_devtype;
+ atomic_set(&dev->dev_cmd_count, 0);
+ atomic_set(&dev->write_cmd_count, 0);
+ scst_init_mem_lim