Files
scst/usr/fileio/common.c
T
Vladislav Bolkhovitin bb4d276a54 Merged revisions 7096,7101,7104,7106-7111,7115-7118,7125,7134-7137,7139,7141,7143,7152-7154,7158,7162-7163,7168-7171,7173,7175-7179,7185,7190,7193,7203,7208 via svnmerge from
svn+ssh://svn.code.sf.net/p/scst/svn/trunk

........
  r7096 | bvassche | 2017-02-23 18:08:17 -0800 (Thu, 23 Feb 2017) | 4 lines
  
  scst_copy_mgr: Avoid that LUN removal triggers a BUG()
  
  Reported-by: Jinpu Wang <jinpu.wang@profitbricks.com>
........
  r7101 | bvassche | 2017-03-01 07:31:59 -0800 (Wed, 01 Mar 2017) | 20 lines
  
  scst_vdisk: Avoid that LUN refresh triggers a general protection fault
  
  Avoid that triggering LUN referesh concurrently with device deletion
  triggers the following:
  
  general protection fault: 0000 [#1]
  Workqueue: events vdev_inq_changed_fn [scst_vdisk]
  Call Trace:
   _raw_spin_lock_bh+0x2b/0x30
   scst_cm_update_dev+0x87/0x190 [scst]
   scst_dev_inquiry_data_changed+0xfb/0x1b0 [scst]
   vdev_inq_changed_fn+0x60/0x120 [scst_vdisk]
   process_one_work+0x14d/0x410
   worker_thread+0x66/0x460
   kthread+0xdb/0x100
   ret_from_fork+0x3f/0x70
  
  Reported-by: Jinpu Wang <jinpu.wang@profitbricks.com>
  Tested-by: Jinpu Wang <jinpu.wang@profitbricks.com>
........
  r7104 | vlnb | 2017-03-07 20:36:45 -0800 (Tue, 07 Mar 2017) | 5 lines
  
  Linux kernel v4.10 build fix.
  
  Signed-off-by: Sebastian Herbszt <herbszt@gmx.de>
........
  r7106 | bvassche | 2017-04-11 11:48:36 -0700 (Tue, 11 Apr 2017) | 4 lines
  
  ib_srpt: Ensure that the BUG_ON() argument has no side effects
  
  Reported-by: David Butterfield <dab21774@gmail.com>
........
  r7107 | vlnb | 2017-04-13 15:04:07 -0700 (Thu, 13 Apr 2017) | 7 lines
  
  The argument to sleep() would get "promoted" to an integer with value zero.
  -               sleep(0.1);
  +               usleep(100*1000);
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7108 | vlnb | 2017-04-13 15:30:25 -0700 (Thu, 13 Apr 2017) | 6 lines
  
  create_and_open_dev() returns a (-errno), so the "if (iser_fd...)" check should
  detect *any* negative return value as a case when fd should be set to -1.
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7109 | vlnb | 2017-04-13 15:38:38 -0700 (Thu, 13 Apr 2017) | 8 lines
  
  Change memcpy() to strncpy() because the source name string is not guaranteed
  to exist as valid addressable memory beyond the NULL byte.
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
  
  with small addition to force set last byte NULL
........
  r7110 | vlnb | 2017-04-13 16:02:18 -0700 (Thu, 13 Apr 2017) | 7 lines
  
  Thre is potential buffer overflow in iscsi_session_alloc() due to
  short computation of needed string size.  Notice the "%s@%s" in the first call
  to sprintf().
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7111 | vlnb | 2017-04-13 16:12:46 -0700 (Thu, 13 Apr 2017) | 8 lines
  
  iscsi: avoid a crash in iscsi_extracheck_is_rd_thread()
  
  Add an extra check in iscsi_extracheck_is_rd_thread() to avoid a crash when
  conn->rd_task is NULL.
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7115 | vlnb | 2017-04-13 16:37:13 -0700 (Thu, 13 Apr 2017) | 12 lines
  
  scst: fix memory leak in scst_proc_group_add()
  
  Valgrind noticed that the "name" allocated in scst_proc_group_add() was
  leaking.  It turns out that scst_alloc_add_acg makes its own copy of the name
  passed to it from this code, making the string duplication done here redundant
  (and leaky).  The change eliminates the string duplication (along with all its
  associated error handling logic) and simply passes the (unowned) incoming
  string down for duplication below.
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7116 | vlnb | 2017-04-13 16:39:14 -0700 (Thu, 13 Apr 2017) | 7 lines
  
  scst_vdisk: fix memory leak in vdisk_write_proc()
  
  Another leak valgrind popped out, this one in vdisk_write_proc().
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7117 | vlnb | 2017-04-13 16:41:14 -0700 (Thu, 13 Apr 2017) | 9 lines
  
  scst: take scst_mutex before calling scst_del_free_acg() in exit_scst()
  
  scst_del_free_acg() does lockdep_assert_held(&scst_mutex), so we'd better take
  the lock before calling it.
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7118 | vlnb | 2017-04-13 16:42:51 -0700 (Thu, 13 Apr 2017) | 8 lines
  
  scst: take scst_mutex before calling scst_del_free_acg() in scst_proc_cleanup_module()
  
  Take lock before a call that ends up at the lockdep_assert_held() in
  scst_del_free_acg() without locking.
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7125 | vlnb | 2017-04-13 18:17:45 -0700 (Thu, 13 Apr 2017) | 6 lines
  
  extraclean does "rm tags cscope.out"
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7134 | vlnb | 2017-04-17 20:57:12 -0700 (Mon, 17 Apr 2017) | 5 lines
  
  iscsi-scst: replace strncpy() by strlcpy()
  
  Follow up for r7109: strlcpy() is more appropriate in this place.
........
  r7135 | vlnb | 2017-04-17 21:02:44 -0700 (Mon, 17 Apr 2017) | 5 lines
  
  fcst: Linux kernel v4.10 build fix
  
  Signed-off-by: Sebastian Herbszt <herbszt@gmx.de>
........
  r7136 | vlnb | 2017-04-17 21:06:18 -0700 (Mon, 17 Apr 2017) | 5 lines
  
  scst: avoid possible side effect with WARN_ON_ONCE()
  
  Reported-By: David Butterfield <dab21774@gmail.com>
........
  r7137 | vlnb | 2017-04-18 20:44:20 -0700 (Tue, 18 Apr 2017) | 5 lines
  
  fileio_tgt: change "#if DEBUG_TM_FN_IGNORE" to "#ifdef ..."
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7139 | vlnb | 2017-04-20 18:02:25 -0700 (Thu, 20 Apr 2017) | 7 lines
  
  iscsi-scstd: replace signal() with sigaction()
  
  Replace signal() with sigaction() for validity in a multithreaded process
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7141 | vlnb | 2017-04-20 18:11:37 -0700 (Thu, 20 Apr 2017) | 7 lines
  
  scst: set file size for NULLIO in PROCFS build
  
  The file size wasn't getting set for NULLIO with /proc support
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7143 | vlnb | 2017-04-20 18:32:07 -0700 (Thu, 20 Apr 2017) | 9 lines
  
  iscsi-scst: fix ENOMEM path
  
  In an error path in iscsi_threads_pool_get(), when a new pool cannot be
  allocated, if there is a pool on iscsi_thread_pools_list, it passes that back as
  an alternative, so return zero in that case.
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7152 | bvassche | 2017-04-26 16:53:11 -0700 (Wed, 26 Apr 2017) | 5 lines
  
  scst: Introduce scst_scsi_execute()
  
  This patch does not change any functionality but makes it easier
  to port SCST to Linux kernel v4.11.
........
  r7153 | bvassche | 2017-04-26 17:17:22 -0700 (Wed, 26 Apr 2017) | 1 line
  
  scst: Port to Linux kernel v4.11
........
  r7154 | vlnb | 2017-04-28 17:58:55 -0700 (Fri, 28 Apr 2017) | 5 lines
  
  scst: create proc/scst_threads with mode S_IRUGO, not 0
  
  Signed-off-by: David Butterfield <dab21774@gmail.com>
........
  r7158 | bvassche | 2017-05-01 14:01:33 -0700 (Mon, 01 May 2017) | 1 line
  
  scst: Kernel v4.12 build fixes
........
  r7162 | bvassche | 2017-05-02 07:13:15 -0700 (Tue, 02 May 2017) | 1 line
  
  scst_lib: Fix kernel 2.6.30 build
........
  r7163 | bvassche | 2017-05-02 07:23:00 -0700 (Tue, 02 May 2017) | 1 line
  
  scst: Fix build for kernels before v2.6.39
........
  r7168 | bvassche | 2017-05-03 19:56:27 -0700 (Wed, 03 May 2017) | 1 line
  
  scst: Fix build for kernels before v2.6.39
........
  r7169 | vlnb | 2017-05-05 18:31:59 -0700 (Fri, 05 May 2017) | 3 lines
  
  scst: nolockdep patch for kernel 4.9
........
  r7170 | vlnb | 2017-05-10 20:51:32 -0700 (Wed, 10 May 2017) | 3 lines
  
  qla2x00t: update to kernel 4.10
........
  r7171 | vlnb | 2017-05-10 20:52:51 -0700 (Wed, 10 May 2017) | 3 lines
  
  scst: nolockdep patch for kernel 4.10
........
  r7173 | vlnb | 2017-05-10 21:00:29 -0700 (Wed, 10 May 2017) | 7 lines
  
  fcst: Linux kernel v4.11 build fix
  
  Linux kernel v4.11 build fix.
  
  Signed-off-by: Sebastian Herbszt <herbszt@gmx.de>
........
  r7175 | bvassche | 2017-05-13 17:42:24 -0700 (Sat, 13 May 2017) | 4 lines
  
  scst/include/backport.h: Remove duplicate definition of kthread_create_on_node()
  
  This patch reverts most of r7168.
........
  r7176 | bvassche | 2017-05-13 17:55:39 -0700 (Sat, 13 May 2017) | 1 line
  
  scst/include/backport.h: Add a comment
........
  r7177 | bvassche | 2017-05-13 19:55:24 -0700 (Sat, 13 May 2017) | 1 line
  
  scst/include/backport.h: Fix kthread_create_on_node() definition
........
  r7178 | bvassche | 2017-05-13 20:06:54 -0700 (Sat, 13 May 2017) | 4 lines
  
  scst/include/backport.h: Add a kref_read() backport
  
  This patch does not change any functionality.
........
  r7179 | bvassche | 2017-05-13 20:13:56 -0700 (Sat, 13 May 2017) | 4 lines
  
  scst/include/backport.h: Add a backport of rcu_dereference_protected()
  
  This patch does not change any functionality.
........
  r7185 | bvassche | 2017-05-14 11:56:09 -0700 (Sun, 14 May 2017) | 14 lines
  
  scst_local: Fix a race condition
  
  Avoid that the following crash can occur:
  
  general protection fault: 0000 [#1] PREEMPT SMP
  RIP: 0010:scsi_is_host_device+0x7/0x20 [scsi_mod]
  Call Trace:
   scst_process_aens+0x95/0x1d0 [scst_local]
   scst_aen_work_fn+0x6f/0x120 [scst_local]
   process_one_work+0x20b/0x6c0
   worker_thread+0x4e/0x4a0
   kthread+0x113/0x150
   ret_from_fork+0x31/0x40
........
  r7190 | vlnb | 2017-05-19 20:00:28 -0700 (Fri, 19 May 2017) | 8 lines
  
  scst: fix possible NULL dereference in TM code
  
  TM command accessing a non-existing LUN might lead NULL dereference in
  scst_call_dev_task_mgmt_fn_done().
  
  Reported-By: <Ilan Steinberg <ilan.steinberg@kaminario.com>>
........
  r7193 | vlnb | 2017-05-22 19:23:38 -0700 (Mon, 22 May 2017) | 5 lines
  
  qla2x00t: fix broken 4.9 kernels build
  
  Reported-By: Marc Smith <marc.smith@parodyne.com>
........
  r7203 | vlnb | 2017-06-02 19:38:51 -0700 (Fri, 02 Jun 2017) | 3 lines
  
  Update to 4.11 kernels
........
  r7208 | vlnb | 2017-06-12 20:58:26 -0700 (Mon, 12 Jun 2017) | 5 lines
  
  scst: Linux kernel v4.12 warning fix.
  
  Signed-off-by: Sebastian Herbszt <herbszt@gmx.de>
........


git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/3.2.x@7209 d57e44dd-8a1f-0410-8b47-8ef2f437770f
2017-07-14 00:17:31 +00:00

1929 lines
48 KiB
C

/*
* common.c
*
* Copyright (C) 2007 - 2016 Vladislav Bolkhovitin <vst@vlnb.net>
* Copyright (C) 2007 - 2016 SanDisk Corporation
*
* 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 <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <getopt.h>
#include <inttypes.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "common.h"
static void exec_inquiry(struct vdisk_cmd *vcmd);
static void exec_request_sense(struct vdisk_cmd *vcmd);
static void exec_mode_sense(struct vdisk_cmd *vcmd);
static void exec_mode_select(struct vdisk_cmd *vcmd);
static void exec_read_capacity(struct vdisk_cmd *vcmd);
static void exec_read_capacity16(struct vdisk_cmd *vcmd);
static void exec_read_toc(struct vdisk_cmd *vcmd);
static void exec_prevent_allow_medium_removal(struct vdisk_cmd *vcmd);
static int exec_fsync(struct vdisk_cmd *vcmd);
static void exec_read(struct vdisk_cmd *vcmd, loff_t loff);
static void exec_write(struct vdisk_cmd *vcmd, loff_t loff);
static void exec_verify(struct vdisk_cmd *vcmd, loff_t loff);
static void exec_write_same(struct vdisk_cmd *vcmd);
static int open_dev_fd(struct vdisk_dev *dev)
{
int res;
int open_flags = O_LARGEFILE;
if (dev->rd_only_flag)
open_flags |= O_RDONLY;
else
open_flags |= O_RDWR;
if (dev->o_direct_flag)
open_flags |= O_DIRECT;
if (dev->wt_flag)
open_flags |= O_DSYNC;
TRACE_DBG("Opening file %s, flags 0x%x", dev->file_name, open_flags);
res = open(dev->file_name, open_flags);
return res;
}
static void set_resp_data_len(struct vdisk_cmd *vcmd, int32_t resp_data_len)
{
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
TRACE_ENTRY();
if (vcmd->may_need_to_free_pbuf && (resp_data_len == 0)) {
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
free((void *)(unsigned long)cmd->pbuf);
cmd->pbuf = 0;
reply->pbuf = 0;
}
reply->resp_data_len = resp_data_len;
TRACE_EXIT();
return;
}
static inline void set_cmd_error_status(struct vdisk_cmd *vcmd,
int status)
{
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
reply->status = status;
set_resp_data_len(vcmd, 0);
return;
}
static int set_sense(uint8_t *buffer, int len, int key, int asc, int ascq)
{
int res = 18;
EXTRACHECKS_BUG_ON(len < res);
memset(buffer, 0, res);
buffer[0] = 0x70; /* Error Code */
buffer[2] = key; /* Sense Key */
buffer[7] = 0x0a; /* Additional Sense Length */
buffer[12] = asc; /* ASC */
buffer[13] = ascq; /* ASCQ */
TRACE_BUFFER("Sense set", buffer, res);
return res;
}
/*
* ToDo: implement analogs of scst_set_invalid_field_in_cdb() and
* scst_set_invalid_field_in_parm_list()
*/
void set_cmd_error(struct vdisk_cmd *vcmd, int key, int asc, int ascq)
{
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
TRACE_ENTRY();
EXTRACHECKS_BUG_ON(vcmd->cmd->subcode != SCST_USER_EXEC);
set_cmd_error_status(vcmd, SAM_STAT_CHECK_CONDITION);
reply->sense_len = set_sense(vcmd->sense, sizeof(vcmd->sense), key,
asc, ascq);
reply->psense_buffer = (unsigned long)vcmd->sense;
TRACE_EXIT();
return;
}
void set_busy(struct vdisk_cmd *vcmd)
{
TRACE_ENTRY();
set_cmd_error_status(vcmd, SAM_STAT_TASK_SET_FULL);
TRACE_MGMT_DBG("%s", "Sending QUEUE_FULL status");
TRACE_EXIT();
return;
}
static int do_parse(struct vdisk_cmd *vcmd)
{
int res = 0;
struct scst_user_scsi_cmd_parse *cmd = &vcmd->cmd->parse_cmd;
struct scst_user_scsi_cmd_reply_parse *reply = &vcmd->reply->parse_reply;
TRACE_ENTRY();
memset(reply, 0, sizeof(*reply));
vcmd->reply->cmd_h = vcmd->cmd->cmd_h;
vcmd->reply->subcode = vcmd->cmd->subcode;
if (cmd->expected_values_set == 0) {
PRINT_ERROR("%s", "Oops, expected values are not set");
reply->bufflen = -1; /* invalid value */
goto out;
}
reply->queue_type = cmd->queue_type;
reply->data_direction = cmd->expected_data_direction;
reply->lba = cmd->lba;
reply->data_len = cmd->expected_transfer_len;
reply->bufflen = cmd->expected_transfer_len;
reply->out_bufflen = cmd->expected_out_transfer_len;
reply->cdb_len = cmd->cdb_len;
if (cmd->op_flags & SCST_INFO_VALID)
reply->op_flags = cmd->op_flags;
else {
TRACE_DBG("Extra parse (op %x)", cmd->cdb[0]);
if (reply->data_direction & SCST_DATA_WRITE)
reply->op_flags |= SCST_WRITE_MEDIUM;
reply->op_flags |= SCST_INFO_VALID;
}
out:
TRACE_EXIT_RES(res);
return res;
}
struct vdisk_tgt_dev *find_tgt_dev(struct vdisk_dev *dev, uint64_t sess_h)
{
unsigned int i;
struct vdisk_tgt_dev *res = NULL;
for(i = 0; i < ARRAY_SIZE(dev->tgt_devs); i++) {
if (dev->tgt_devs[i].sess_h == sess_h) {
res = &dev->tgt_devs[i];
break;
}
}
return res;
}
struct vdisk_tgt_dev *find_empty_tgt_dev(struct vdisk_dev *dev)
{
unsigned int i;
struct vdisk_tgt_dev *res = NULL;
for(i = 0; i < ARRAY_SIZE(dev->tgt_devs); i++) {
if (dev->tgt_devs[i].sess_h == 0) {
res = &dev->tgt_devs[i];
break;
}
}
return res;
}
static int do_exec(struct vdisk_cmd *vcmd)
{
int res = 0;
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
uint64_t lba_start = cmd->lba;
int64_t data_len = cmd->data_len;
uint8_t *cdb = cmd->cdb;
int opcode = cdb[0];
loff_t loff;
int fua = 0;
TRACE_ENTRY();
/* Must be reinitialized each time to avoid crash on stale value */
vcmd->may_need_to_free_pbuf = 0;
switch(cmd->queue_type) {
case SCST_CMD_QUEUE_ORDERED:
TRACE(TRACE_ORDER, "ORDERED cmd_h %d", vcmd->cmd->cmd_h);
break;
case SCST_CMD_QUEUE_HEAD_OF_QUEUE:
TRACE(TRACE_ORDER, "HQ cmd_h %d", vcmd->cmd->cmd_h);
break;
default:
break;
}
memset(reply, 0, sizeof(*reply));
vcmd->reply->cmd_h = vcmd->cmd->cmd_h;
vcmd->reply->subcode = vcmd->cmd->subcode;
reply->reply_type = SCST_EXEC_REPLY_COMPLETED;
#ifdef DEBUG_SENSE
if ((random() % 100000) == 75) {
set_cmd_error(vcmd, SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out;
}
#endif
#ifdef DEBUG_TM_IGNORE
if (dev->debug_tm_ignore && (random() % 10000) == 75) {
TRACE_MGMT_DBG("Ignore cmd op %x (h=%d)", cdb[0],
vcmd->cmd->cmd_h);
res = 150;
goto out;
}
#endif
if ((cmd->pbuf == 0) && (cmd->alloc_len != 0)) {
#ifdef DEBUG_NOMEM
if ((random() % 100) == 75)
cmd->pbuf = 0;
else
#endif
cmd->pbuf = (unsigned long)dev->alloc_fn(cmd->alloc_len);
vcmd->may_need_to_free_pbuf = 1;
TRACE_MEM("Buf %"PRIx64" alloced, len %d", cmd->pbuf,
cmd->alloc_len);
reply->pbuf = cmd->pbuf;
if (cmd->pbuf == 0) {
TRACE(TRACE_OUT_OF_MEM, "Unable to allocate buffer "
"(len %d)", cmd->alloc_len);
#ifndef DEBUG_NOMEM
set_busy(vcmd);
#endif
goto out;
}
}
if (cmd->data_direction & SCST_DATA_READ)
reply->resp_data_len = cmd->bufflen;
loff = (loff_t)lba_start << dev->block_shift;
TRACE_DBG("cmd %d, buf %"PRIx64", lba_start %"PRId64", loff %"PRId64
", data_len %"PRId64, vcmd->cmd->cmd_h, cmd->pbuf, lba_start,
(uint64_t)loff, data_len);
if ((loff < 0) || (data_len < 0) || ((loff + data_len) > dev->file_size)) {
PRINT_INFO("Access beyond the end of the device "
"(%"PRId64" of %"PRId64", len %"PRId64")", (uint64_t)loff,
(uint64_t)dev->file_size, data_len);
set_cmd_error(vcmd, SCST_LOAD_SENSE(
scst_sense_block_out_range_error));
goto out;
}
switch (opcode) {
case WRITE_10:
case WRITE_12:
case WRITE_16:
fua = (cdb[1] & 0x8);
if (cdb[1] & 0x8) {
TRACE(TRACE_ORDER, "FUA(%d): loff=%"PRId64", "
"data_len=%"PRId64, fua, (uint64_t)loff,
data_len);
}
break;
}
switch (opcode) {
case READ_6:
case READ_10:
case READ_12:
case READ_16:
exec_read(vcmd, loff);
break;
case WRITE_6:
case WRITE_10:
case WRITE_12:
case WRITE_16:
if (!dev->rd_only_flag) {
struct vdisk_tgt_dev *tgt_dev;
tgt_dev = find_tgt_dev(dev, cmd->sess_h);
if (tgt_dev == NULL) {
PRINT_ERROR("Session %"PRIx64" not found",
cmd->sess_h);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
}
exec_write(vcmd, loff);
/* O_DSYNC flag is used for WT devices */
if (fua)
exec_fsync(vcmd);
} else {
PRINT_WARNING("Attempt to write to read-only "
"device %s", dev->name);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_data_protect));
}
break;
case WRITE_VERIFY:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
if (!dev->rd_only_flag) {
struct vdisk_tgt_dev *tgt_dev;
tgt_dev = find_tgt_dev(dev, cmd->sess_h);
if (tgt_dev == NULL) {
PRINT_ERROR("Session %"PRIx64" not found",
cmd->sess_h);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
}
exec_write(vcmd, loff);
/* O_DSYNC flag is used for WT devices */
if (reply->status == 0)
exec_verify(vcmd, loff);
} else {
PRINT_WARNING("Attempt to write to read-only "
"device %s", dev->name);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_data_protect));
}
break;
case SYNCHRONIZE_CACHE:
case SYNCHRONIZE_CACHE_16:
{
int immed = cdb[1] & 0x2;
if (data_len == 0)
data_len = dev->file_size -
((loff_t)lba_start << dev->block_shift);
TRACE(TRACE_ORDER, "SYNCHRONIZE_CACHE: "
"loff=%"PRId64", data_len=%"PRId64", immed=%d",
(uint64_t)loff, data_len, immed);
if (immed) {
/* ToDo: backgroung exec */
exec_fsync(vcmd);
break;
} else {
exec_fsync(vcmd);
break;
}
}
case VERIFY:
case VERIFY_12:
case VERIFY_16:
exec_verify(vcmd, loff);
break;
case MODE_SENSE:
case MODE_SENSE_10:
exec_mode_sense(vcmd);
break;
case MODE_SELECT:
case MODE_SELECT_10:
exec_mode_select(vcmd);
break;
case ALLOW_MEDIUM_REMOVAL:
exec_prevent_allow_medium_removal(vcmd);
break;
case READ_TOC:
exec_read_toc(vcmd);
break;
case START_STOP:
exec_fsync(vcmd/*, 0, dev->file_size*/);
break;
case RESERVE:
case RESERVE_10:
case RELEASE:
case RELEASE_10:
case TEST_UNIT_READY:
break;
case INQUIRY:
exec_inquiry(vcmd);
break;
case REQUEST_SENSE:
exec_request_sense(vcmd);
break;
case READ_CAPACITY:
exec_read_capacity(vcmd);
break;
case WRITE_SAME_10:
case WRITE_SAME_16:
exec_write_same(vcmd);
break;
case SERVICE_ACTION_IN_16:
if ((cmd->cdb[1] & 0x1f) == SAI_READ_CAPACITY_16)
exec_read_capacity16(vcmd);
else {
TRACE_DBG("Invalid service action %d for SERVICE "
"ACTION IN", cmd->cdb[1] & 0x1f);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
}
break;
case REPORT_LUNS:
default:
TRACE_DBG("Invalid opcode 0x%x", opcode);
set_cmd_error(vcmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode));
break;
}
out:
TRACE_EXIT();
return res;
}
static int do_alloc_mem(struct vdisk_cmd *vcmd)
{
struct scst_user_get_cmd *cmd = vcmd->cmd;
struct scst_user_reply_cmd *reply = vcmd->reply;
int res = 0;
TRACE_ENTRY();
TRACE_MEM("Alloc mem (cmd %d, sess_h %"PRIx64", cdb_len %d, "
"alloc_len %d, queue_type %d, data_direction %d)",
cmd->cmd_h, cmd->alloc_cmd.sess_h,
cmd->alloc_cmd.cdb_len, cmd->alloc_cmd.alloc_len,
cmd->alloc_cmd.queue_type, cmd->alloc_cmd.data_direction);
TRACE_BUFF_FLAG(TRACE_MEMORY, "CDB", cmd->alloc_cmd.cdb,
cmd->alloc_cmd.cdb_len);
memset(reply, 0, sizeof(*reply));
reply->cmd_h = cmd->cmd_h;
reply->subcode = cmd->subcode;
#ifdef DEBUG_NOMEM
if ((random() % 100) == 75)
reply->alloc_reply.pbuf = 0;
else
#endif
reply->alloc_reply.pbuf = (unsigned long)vcmd->dev->alloc_fn(
cmd->alloc_cmd.alloc_len);
TRACE_MEM("Buf %"PRIx64" alloced, len %d", reply->alloc_reply.pbuf,
cmd->alloc_cmd.alloc_len);
if (reply->alloc_reply.pbuf == 0) {
TRACE(TRACE_OUT_OF_MEM, "Unable to allocate buffer (len %d)",
cmd->alloc_cmd.alloc_len);
}
TRACE_EXIT_RES(res);
return res;
}
static int do_cached_mem_free(struct vdisk_cmd *vcmd)
{
struct scst_user_get_cmd *cmd = vcmd->cmd;
struct scst_user_reply_cmd *reply = vcmd->reply;
int res = 0;
TRACE_ENTRY();
TRACE_MEM("Cached mem free (cmd %x, buf %"PRIx64")", cmd->cmd_h,
cmd->on_cached_mem_free.pbuf);
free((void *)(unsigned long)cmd->on_cached_mem_free.pbuf);
memset(reply, 0, sizeof(*reply));
reply->cmd_h = cmd->cmd_h;
reply->subcode = cmd->subcode;
TRACE_EXIT_RES(res);
return res;
}
static int do_on_free_cmd(struct vdisk_cmd *vcmd)
{
struct scst_user_get_cmd *cmd = vcmd->cmd;
struct scst_user_reply_cmd *reply = vcmd->reply;
int res = 0;
TRACE_ENTRY();
TRACE_DBG("On free cmd (cmd %d, resp_data_len %d, aborted %d, "
"status %d, delivery_status %d)", cmd->cmd_h,
cmd->on_free_cmd.resp_data_len, cmd->on_free_cmd.aborted,
cmd->on_free_cmd.status, cmd->on_free_cmd.delivery_status);
TRACE_MEM("On free cmd (cmd %d, buf %"PRIx64", buffer_cached %d)",
cmd->cmd_h, cmd->on_free_cmd.pbuf,
cmd->on_free_cmd.buffer_cached);
if (!cmd->on_free_cmd.buffer_cached && (cmd->on_free_cmd.pbuf != 0)) {
TRACE_MEM("Freeing buf %"PRIx64, cmd->on_free_cmd.pbuf);
free((void *)(unsigned long)cmd->on_free_cmd.pbuf);
}
memset(reply, 0, sizeof(*reply));
reply->cmd_h = cmd->cmd_h;
reply->subcode = cmd->subcode;
TRACE_EXIT_RES(res);
return res;
}
#ifdef DEBUG_EXT_COPY_REMAP
static int do_ext_copy_remap(struct vdisk_cmd *vcmd)
{
struct scst_user_get_cmd *cmd = vcmd->cmd;
struct scst_user_ext_copy_remap *rcmd = &cmd->remap_cmd;
struct scst_user_reply_cmd *reply = vcmd->reply;
struct scst_user_ext_copy_reply_remap *rreply = &reply->remap_reply;
static struct scst_user_ext_copy_data_descr d[1];
int res = 0;
TRACE_ENTRY();
TRACE_DBG("Ext copy remap cmd %d", cmd->cmd_h);
memset(reply, 0, sizeof(*reply));
reply->cmd_h = cmd->cmd_h;
reply->subcode = cmd->subcode;
memset(rreply, 0, sizeof(*rreply));
/* It's only a debug code, so it's OK to use static descr */
memset(d, 0, sizeof(d));
d[0].data_len = rcmd->data_descr.data_len;
d[0].src_lba = rcmd->data_descr.src_lba;
d[0].dst_lba = rcmd->data_descr.dst_lba;
#if 1
rreply->remap_descriptors = (unsigned long)d;
rreply->remap_descriptors_len = sizeof(d);
#else
rreply->status = SAM_STAT_CHECK_CONDITION;
rreply->sense_len = set_sense(vcmd->sense, sizeof(vcmd->sense),
SCST_LOAD_SENSE(scst_sense_data_protect));
rreply->psense_buffer = (unsigned long)vcmd->sense;
#endif
TRACE_EXIT_RES(res);
return res;
}
#endif
static int do_tm(struct vdisk_cmd *vcmd, int done)
{
struct scst_user_get_cmd *cmd = vcmd->cmd;
struct scst_user_reply_cmd *reply = vcmd->reply;
struct vdisk_dev *dev = vcmd->dev;
int res = 0;
TRACE_ENTRY();
if (cmd->tm_cmd.fn <= SCST_TARGET_RESET)
TRACE(TRACE_MGMT, "%s TM fn %d (sess_h %"PRIx64", "
"cmd_h_to_abort %d)", done ? "Done" : "Received",
cmd->tm_cmd.fn, cmd->tm_cmd.sess_h,
cmd->tm_cmd.cmd_h_to_abort);
else
TRACE_MGMT_DBG("%s TM fn %d (sess_h %"PRIx64", "
"cmd_h_to_abort %d)", done ? "Done" : "Received",
cmd->tm_cmd.fn, cmd->tm_cmd.sess_h,
cmd->tm_cmd.cmd_h_to_abort);
if (done) {
switch (cmd->tm_cmd.fn) {
case SCST_LUN_RESET:
case SCST_TARGET_RESET:
case SCST_PR_ABORT_ALL:
pthread_mutex_lock(&dev->dev_mutex);
dev->prevent_allow_medium_removal = 0;
pthread_mutex_unlock(&dev->dev_mutex);
break;
}
}
memset(reply, 0, sizeof(*reply));
reply->cmd_h = cmd->cmd_h;
reply->subcode = cmd->subcode;
reply->result = 0;
TRACE_EXIT_RES(res);
return res;
}
static int do_sess(struct vdisk_cmd *vcmd)
{
struct scst_user_get_cmd *cmd = vcmd->cmd;
struct scst_user_reply_cmd *reply = vcmd->reply;
int res = 0;
struct vdisk_tgt_dev *tgt_dev;
TRACE_ENTRY();
/*
* We are guaranteed to have one and only one command at this point,
* which is ATTACH_SESS/DETACH_SESS, so no protection is needed
*/
tgt_dev = find_tgt_dev(vcmd->dev, cmd->sess.sess_h);
if (cmd->subcode == SCST_USER_ATTACH_SESS) {
if (tgt_dev != NULL) {
PRINT_ERROR("Session %"PRIx64" already exists)",
cmd->sess.sess_h);
res = EEXIST;
goto reply;
}
tgt_dev = find_empty_tgt_dev(vcmd->dev);
if (tgt_dev == NULL) {
PRINT_ERROR("Too many initiators, session %"PRIx64
" refused)", cmd->sess.sess_h);
res = ENOMEM;
goto reply;
}
tgt_dev->sess_h = cmd->sess.sess_h;
PRINT_INFO("Session from initiator %s (target %s) attached "
"(LUN %"PRIx64", threads_num %d, rd_only %d, sess_h "
"%"PRIx64")", cmd->sess.initiator_name,
cmd->sess.target_name, cmd->sess.lun,
cmd->sess.threads_num, cmd->sess.rd_only,
cmd->sess.sess_h);
} else {
if (tgt_dev == NULL) {
PRINT_ERROR("Session %"PRIx64" not found)",
cmd->sess.sess_h);
res = ESRCH;
goto reply;
}
tgt_dev->sess_h = 0;
PRINT_INFO("Session detached (sess_h %"PRIx64")",
cmd->sess.sess_h);
}
reply:
memset(reply, 0, sizeof(*reply));
reply->cmd_h = cmd->cmd_h;
reply->subcode = cmd->subcode;
reply->result = res;
TRACE_EXIT_RES(res);
return res;
}
static int process_cmd(struct vdisk_cmd *vcmd)
{
struct scst_user_get_cmd *cmd = vcmd->cmd;
struct scst_user_reply_cmd *reply = vcmd->reply;
int res = 0;
TRACE_ENTRY();
TRACE_BUFFER("Received cmd", cmd, sizeof(*cmd));
switch(cmd->subcode) {
case SCST_USER_EXEC:
if (cmd->exec_cmd.data_direction & SCST_DATA_WRITE) {
TRACE_BUFFER("Received cmd data",
(void *)(unsigned long)cmd->exec_cmd.pbuf,
cmd->exec_cmd.bufflen);
}
res = do_exec(vcmd);
if ((reply->exec_reply.resp_data_len != 0) && (res != 150)) {
TRACE_BUFFER("Reply data",
(void *)(unsigned long)reply->exec_reply.pbuf,
reply->exec_reply.resp_data_len);
}
break;
case SCST_USER_ALLOC_MEM:
res = do_alloc_mem(vcmd);
break;
case SCST_USER_PARSE:
res = do_parse(vcmd);
break;
case SCST_USER_ON_CACHED_MEM_FREE:
res = do_cached_mem_free(vcmd);
break;
case SCST_USER_ON_FREE_CMD:
res = do_on_free_cmd(vcmd);
break;
#ifdef DEBUG_EXT_COPY_REMAP
case SCST_USER_EXT_COPY_REMAP:
res = do_ext_copy_remap(vcmd);
break;
#endif
case SCST_USER_TASK_MGMT_RECEIVED:
res = do_tm(vcmd, 0);
break;
case SCST_USER_TASK_MGMT_DONE:
res = do_tm(vcmd, 1);
#ifdef DEBUG_TM_FN_IGNORE
if (dev->debug_tm_ignore) {
sleep(15);
}
#endif
break;
case SCST_USER_ATTACH_SESS:
case SCST_USER_DETACH_SESS:
res = do_sess(vcmd);
break;
default:
PRINT_ERROR("Unknown or wrong cmd subcode %x",
cmd->subcode);
res = -EINVAL;
goto out;
}
out:
TRACE_EXIT_RES(res);
return res;
}
void *main_loop(void *arg)
{
int res = 0, i, j;
struct vdisk_dev *dev = (struct vdisk_dev *)arg;
struct scst_user_get_cmd cmd;
struct scst_user_reply_cmd reply;
struct vdisk_cmd vcmd = {
.fd = -1,
.cmd = &cmd,
.dev = dev,
.may_need_to_free_pbuf = 0,
.reply = &reply,
.sense = {0}
};
int scst_usr_fd = dev->scst_usr_fd;
struct pollfd pl;
#define MULTI_CMDS_CNT 2
struct {
struct scst_user_reply_cmd replies[MULTI_CMDS_CNT];
struct scst_user_get_multi multi_cmd;
struct scst_user_get_cmd cmds[MULTI_CMDS_CNT];
} multi;
TRACE_ENTRY();
vcmd.fd = open_dev_fd(dev);
if (vcmd.fd < 0) {
res = -errno;
PRINT_ERROR("Unable to open file %s (%s)", dev->file_name,
strerror(-res));
goto out;
}
memset(&pl, 0, sizeof(pl));
pl.fd = scst_usr_fd;
pl.events = POLLIN;
cmd.preply = 0;
multi.multi_cmd.preplies = (uint64_t)&multi.replies[0];
multi.multi_cmd.replies_cnt = 0;
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
while(1) {
#ifdef DEBUG_TM_IGNORE_ALL
if (dev->debug_tm_ignore && (random() % 50000) == 55) {
TRACE_MGMT_DBG("%s", "Ignore ALL");
dev->debug_tm_ignore_all = 1;
}
if (dev->debug_tm_ignore_all) {
/* Go Astral */
while(1) {
sleep(60);
}
}
#endif
if (use_multi) {
TRACE_DBG("preplies %p (first: %p), replies_cnt %d, "
"replies_done %d, cmds_cnt %d", (void *)multi.multi_cmd.preplies,
&multi.replies[0], multi.multi_cmd.replies_cnt,
multi.multi_cmd.replies_done, multi.multi_cmd.cmds_cnt);
res = ioctl(scst_usr_fd, SCST_USER_REPLY_AND_GET_MULTI, &multi.multi_cmd);
} else
res = ioctl(scst_usr_fd, SCST_USER_REPLY_AND_GET_CMD, &cmd);
if (res != 0) {
res = errno;
switch(res) {
case ESRCH:
case EBUSY:
TRACE_MGMT_DBG("SCST_USER returned %d (%s)", res, strerror(res));
cmd.preply = 0;
multi.multi_cmd.preplies = (uint64_t)&multi.replies[0];
multi.multi_cmd.replies_cnt = 0;
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
case EINTR:
continue;
case EAGAIN:
TRACE_DBG("SCST_USER returned EAGAIN (%d)", res);
cmd.preply = 0;
multi.multi_cmd.preplies = (uint64_t)&multi.replies[0];
multi.multi_cmd.replies_cnt = 0;
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
if (dev->non_blocking)
break;
else
continue;
default:
PRINT_ERROR("SCST_USER failed: %s (%d)", strerror(res), res);
#if 1
cmd.preply = 0;
multi.multi_cmd.preplies = (uint64_t)&multi.replies[0];
multi.multi_cmd.replies_cnt = 0;
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
continue;
#else
goto out_close;
#endif
}
again_poll:
res = poll(&pl, 1, -1);
if (res > 0)
continue;
else if (res == 0)
goto again_poll;
else {
res = errno;
switch(res) {
case ESRCH:
case EBUSY:
case EAGAIN:
TRACE_MGMT_DBG("poll() returned %d "
"(%s)", res, strerror(res));
case EINTR:
goto again_poll;
default:
PRINT_ERROR("poll() failed: %s", strerror(res));
#if 1
goto again_poll;
#else
goto out_close;
#endif
}
}
}
if (use_multi) {
if (multi.multi_cmd.replies_done < multi.multi_cmd.replies_cnt) {
TRACE_MGMT_DBG("replies_done %d < replies_cnt %d (dev %s)",
multi.multi_cmd.replies_done, multi.multi_cmd.replies_cnt, dev->name);
multi.multi_cmd.preplies = (uint64_t)&multi.replies[multi.multi_cmd.replies_done];
multi.multi_cmd.replies_cnt = multi.multi_cmd.replies_cnt - multi.multi_cmd.replies_done;
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
continue;
}
TRACE_DBG("cmds_cnt %d", multi.multi_cmd.cmds_cnt);
multi.multi_cmd.preplies = (uint64_t)&multi.replies[0];
for (i = 0, j = 0; i < multi.multi_cmd.cmds_cnt; i++, j++) {
vcmd.cmd = &multi.cmds[i];
vcmd.reply = &multi.replies[j];
res = process_cmd(&vcmd);
#ifdef DEBUG_TM_IGNORE
if (res == 150) {
j--;
continue;
}
#endif
if (res != 0)
goto out_close;
TRACE_BUFFER("Sending reply", vcmd.reply, sizeof(reply));
}
multi.multi_cmd.replies_cnt = j;
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
} else {
res = process_cmd(&vcmd);
#ifdef DEBUG_TM_IGNORE
if (res == 150) {
cmd.preply = 0;
continue;
}
#endif
if (res != 0)
goto out_close;
cmd.preply = (unsigned long)&reply;
TRACE_BUFFER("Sending reply", &reply, sizeof(reply));
}
}
out_close:
close(vcmd.fd);
out:
PRINT_INFO("Thread %d exiting (res=%d)", gettid(), res);
TRACE_EXIT_RES(res);
return (void *)(long)res;
}
uint64_t gen_dev_id_num(const struct vdisk_dev *dev)
{
uint32_t dev_id_num;
dev_id_num = crc32buf(dev->name, strlen(dev->name)+1);
return ((uint64_t)vdisk_ID << 32) | dev_id_num;
}
static void exec_inquiry(struct vdisk_cmd *vcmd)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
int resp_len = 0;
int length = cmd->bufflen;
unsigned int i;
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
uint8_t buf[INQ_BUF_SZ];
TRACE_ENTRY();
if (cmd->cdb[1] & CMDDT) {
TRACE_DBG("%s", "INQUIRY: CMDDT is unsupported");
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out;
}
memset(buf, 0, sizeof(buf));
buf[0] = dev->type; /* type dev */
if (buf[0] == TYPE_ROM)
buf[1] = 0x80; /* removable */
/* Vital Product */
if (cmd->cdb[1] & EVPD) {
uint64_t dev_id_num = gen_dev_id_num(dev);
if (0 == cmd->cdb[2]) { /* supported vital product data pages */
buf[3] = 5;
buf[4] = 0x0; /* this page */
buf[5] = 0x80; /* unit serial number */
buf[6] = 0x83; /* device identification */
buf[7] = 0xB0; /* block limits */
buf[8] = 0xB1; /* block device characteristics */
resp_len = buf[3] + 6;
} else if (0x80 == cmd->cdb[2]) { /* unit serial number */
int usn_len = strlen(dev->usn);
buf[1] = 0x80;
buf[3] = usn_len;
strncpy((char *)&buf[4], dev->usn, usn_len);
resp_len = buf[3] + 4;
} else if (0x83 == cmd->cdb[2]) { /* device identification */
int num = 4;
char *t10_id = (char *)&buf[num + 12];
buf[1] = 0x83;
/* Two identification descriptors: */
/* T10 vendor identifier field format (faked) */
buf[num + 0] = 0x2; /* ASCII */
buf[num + 1] = 0x1; /* Vendor ID */
memcpy(&buf[num + 4], VENDOR, 8);
snprintf(t10_id, sizeof(buf) - num - 12,
"%"PRIx64"-%s", dev_id_num, dev->name);
i = strlen(t10_id) + 1;
TRACE_DBG("t10_dev_id %s", t10_id);
buf[num + 3] = 8 + i;
num += buf[num + 3];
#if 0 /* This isn't required and can be misleading, so let's disable it */
num += 4;
/* NAA IEEE registered identifier (faked) */
buf[num] = 0x1; /* binary */
buf[num + 1] = 0x3;
buf[num + 2] = 0x0;
buf[num + 3] = 0x8;
buf[num + 4] = 0x51; /* ieee company id=0x123456 (faked) */
buf[num + 5] = 0x23;
buf[num + 6] = 0x45;
buf[num + 7] = 0x60;
buf[num + 8] = (dev_id_num >> 24);
buf[num + 9] = (dev_id_num >> 16) & 0xff;
buf[num + 10] = (dev_id_num >> 8) & 0xff;
buf[num + 11] = dev_id_num & 0xff;
num = num + 12 - 4;
#endif
resp_len = num;
buf[2] = (resp_len >> 8) & 0xFF;
buf[3] = resp_len & 0xFF;
resp_len += 4;
} else if (0xB0 == cmd->cdb[2]) {
/* Block Limits */
int max_transfer;
buf[1] = 0xB0;
buf[3] = 0x3C;
buf[5] = 0xFF; /* No MAXIMUM COMPARE AND WRITE LENGTH limit */
/* Optimal transfer granuality is PAGE_SIZE */
max_transfer = max((int)(4096/dev->block_size), (int)1);
buf[6] = (max_transfer >> 8) & 0xff;
buf[7] = max_transfer & 0xff;
/*
* Max transfer len is min of sg limit and 8M, but we
* don't have access to them here, so let's use 1M.
*/
max_transfer = 1*1024*1024;
buf[8] = (max_transfer >> 24) & 0xff;
buf[9] = (max_transfer >> 16) & 0xff;
buf[10] = (max_transfer >> 8) & 0xff;
buf[11] = max_transfer & 0xff;
/*
* Let's have optimal transfer len 512KB. Better to not
* set it at all, because we don't have such limit,
* but some initiators may not understand that (?).
* From other side, too big transfers are not optimal,
* because SGV cache supports only <4M buffers.
*/
max_transfer = min((int)max_transfer, (int)(512*1024 / dev->block_size));
buf[12] = (max_transfer >> 24) & 0xff;
buf[13] = (max_transfer >> 16) & 0xff;
buf[14] = (max_transfer >> 8) & 0xff;
buf[15] = max_transfer & 0xff;
resp_len = buf[3] + 4;
} else if (0xB1 == cmd->cdb[2]) {
int r;
/* Block Device Characteristics */
buf[1] = 0xB1;
buf[3] = 0x3C;
#if 0
if (virt_dev->rotational) {
#endif
/* 15K RPM */
r = 0x3A98;
#if 0
} else
r = 1;
#endif
buf[4] = (r >> 8) & 0xff;
buf[5] = r & 0xff;
resp_len = buf[3] + 4;
} else {
TRACE_DBG("INQUIRY: Unsupported EVPD page %x",
cmd->cdb[2]);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out;
}
} else {
int len;
if (cmd->cdb[2] != 0) {
TRACE_DBG("INQUIRY: Unsupported page %x", cmd->cdb[2]);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out;
}
buf[2] = 6; /* Device complies to SPC-4 */
buf[3] = 0x12; /* HiSup + data in format specified in SPC */
buf[4] = 31;/* n - 4 = 35 - 4 = 31 for full 36 byte data */
buf[6] = 1; /* MultiP 1 */
buf[7] = 2; /* CMDQUE 1, BQue 0 => commands queuing supported */
/* 8 byte ASCII Vendor Identification of the target - left aligned */
memcpy(&buf[8], VENDOR, 8);
/* 16 byte ASCII Product Identification of the target - left aligned */
memset(&buf[16], ' ', 16);
len = min(strlen(dev->name), (size_t)16);
memcpy(&buf[16], dev->name, len);
/* 4 byte ASCII Product Revision Level of the target - left aligned */
memcpy(&buf[32], FIO_REV, 4);
resp_len = buf[4] + 5;
}
sBUG_ON(resp_len >= (int)sizeof(buf));
if (length > resp_len)
length = resp_len;
memcpy(address, buf, length);
if (length < reply->resp_data_len)
set_resp_data_len(vcmd, length);
out:
TRACE_EXIT();
return;
}
static void exec_request_sense(struct vdisk_cmd *vcmd)
{
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
int length = cmd->bufflen, l;
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
uint8_t b[SCST_STANDARD_SENSE_LEN];
TRACE_ENTRY();
l = set_sense(b, sizeof(b), SCST_LOAD_SENSE(scst_sense_no_sense));
length = min(l, length);
memcpy(address, b, length);
if (length < reply->resp_data_len)
set_resp_data_len(vcmd, length);
TRACE_EXIT();
return;
}
/*
* <<Following mode pages info copied from ST318451LW with some corrections>>
*
* ToDo: revise them
*/
static int err_recov_pg(unsigned char *p, int pcontrol)
{ /* Read-Write Error Recovery page for mode_sense */
const unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
5, 0, 0xff, 0xff};
memcpy(p, err_recov_pg, sizeof(err_recov_pg));
if (1 == pcontrol)
memset(p + 2, 0, sizeof(err_recov_pg) - 2);
return sizeof(err_recov_pg);
}
static int disconnect_pg(unsigned char *p, int pcontrol)
{ /* Disconnect-Reconnect page for mode_sense */
const unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
memcpy(p, disconnect_pg, sizeof(disconnect_pg));
if (1 == pcontrol)
memset(p + 2, 0, sizeof(disconnect_pg) - 2);
return sizeof(disconnect_pg);
}
static int rigid_geo_pg(unsigned char *p, int pcontrol,
struct vdisk_dev *dev)
{
unsigned char geo_m_pg[] = {0x04, 0x16, 0, 0, 0, DEF_HEADS, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0x3a, 0x98/* 15K RPM */, 0, 0};
int32_t ncyl, n;
memcpy(p, geo_m_pg, sizeof(geo_m_pg));
ncyl = dev->nblocks / (DEF_HEADS * DEF_SECTORS);
if ((dev->nblocks % (DEF_HEADS * DEF_SECTORS)) != 0)
ncyl++;
memcpy(&n, p + 2, sizeof(uint32_t));
n = n | (htonl(ncyl) >> 8);
memcpy(p + 2, &n, sizeof(uint32_t));
if (1 == pcontrol)
memset(p + 2, 0, sizeof(geo_m_pg) - 2);
return sizeof(geo_m_pg);
}
static int format_pg(unsigned char *p, int pcontrol,
struct vdisk_dev *dev)
{ /* Format device page for mode_sense */
const unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x40, 0, 0, 0};
memcpy(p, format_pg, sizeof(format_pg));
p[10] = (DEF_SECTORS >> 8) & 0xff;
p[11] = DEF_SECTORS & 0xff;
p[12] = (dev->block_size >> 8) & 0xff;
p[13] = dev->block_size & 0xff;
if (1 == pcontrol)
memset(p + 2, 0, sizeof(format_pg) - 2);
return sizeof(format_pg);
}
static int caching_pg(unsigned char *p, int pcontrol,
struct vdisk_dev *dev)
{ /* Caching page for mode_sense */
const unsigned char caching_pg[] = {0x8, 18, 0x10, 0, 0xff, 0xff, 0, 0,
0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0};
memcpy(p, caching_pg, sizeof(caching_pg));
p[2] |= !(dev->wt_flag) ? WCE : 0;
if (1 == pcontrol)
memset(p + 2, 0, sizeof(caching_pg) - 2);
return sizeof(caching_pg);
}
static int ctrl_m_pg(unsigned char *p, int pcontrol,
struct vdisk_dev *dev)
{ /* Control mode page for mode_sense */
const unsigned char ctrl_m_pg[] = {0xa, 0xa, 0x20, 0, 0, 0x40, 0, 0,
0, 0, 0x2, 0x4b};
memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
if (!dev->wt_flag && !dev->nv_cache)
p[3] |= 0x10; /* Enable unrestricted reordering */
if (1 == pcontrol)
memset(p + 2, 0, sizeof(ctrl_m_pg) - 2);
return sizeof(ctrl_m_pg);
}
static int iec_m_pg(unsigned char *p, int pcontrol)
{ /* Informational Exceptions control mode page for mode_sense */
const unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
0, 0, 0x0, 0x0};
memcpy(p, iec_m_pg, sizeof(iec_m_pg));
if (1 == pcontrol)
memset(p + 2, 0, sizeof(iec_m_pg) - 2);
return sizeof(iec_m_pg);
}
static void exec_mode_sense(struct vdisk_cmd *vcmd)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
int length = cmd->bufflen;
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
uint8_t buf[MSENSE_BUF_SZ];
int blocksize;
uint64_t nblocks;
unsigned char dbd, type;
int pcontrol, pcode, subpcode;
unsigned char dev_spec;
int msense_6, offset = 0, len;
unsigned char *bp;
TRACE_ENTRY();
blocksize = dev->block_size;
nblocks = dev->nblocks;
type = dev->type; /* type dev */
dbd = cmd->cdb[1] & DBD;
pcontrol = (cmd->cdb[2] & 0xc0) >> 6;
pcode = cmd->cdb[2] & 0x3f;
subpcode = cmd->cdb[3];
msense_6 = (MODE_SENSE == cmd->cdb[0]);
dev_spec = (dev->rd_only_flag ? WP : 0) | DPOFUA;
memset(buf, 0, sizeof(buf));
if (0x3 == pcontrol) {
TRACE_DBG("%s", "MODE SENSE: Saving values not supported");
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_saving_params_unsup));
goto out;
}
if (msense_6) {
buf[1] = type;
buf[2] = dev_spec;
offset = 4;
} else {
buf[2] = type;
buf[3] = dev_spec;
offset = 8;
}
if (0 != subpcode) { /* TODO: Control Extension page */
TRACE_DBG("%s", "MODE SENSE: Only subpage 0 is supported");
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out;
}
if (!dbd) {
/* Create block descriptor */
buf[offset - 1] = 0x08; /* block descriptor length */
if (nblocks >> 32) {
buf[offset + 0] = 0xFF;
buf[offset + 1] = 0xFF;
buf[offset + 2] = 0xFF;
buf[offset + 3] = 0xFF;
} else {
buf[offset + 0] = (nblocks >> (BYTE * 3)) & 0xFF;/* num blks */
buf[offset + 1] = (nblocks >> (BYTE * 2)) & 0xFF;
buf[offset + 2] = (nblocks >> (BYTE * 1)) & 0xFF;
buf[offset + 3] = (nblocks >> (BYTE * 0)) & 0xFF;
}
buf[offset + 4] = 0; /* density code */
buf[offset + 5] = (blocksize >> (BYTE * 2)) & 0xFF;/* blklen */
buf[offset + 6] = (blocksize >> (BYTE * 1)) & 0xFF;
buf[offset + 7] = (blocksize >> (BYTE * 0)) & 0xFF;
offset += 8; /* increment offset */
}
bp = buf + offset;
switch (pcode) {
case 0x1: /* Read-Write error recovery page, direct access */
len = err_recov_pg(bp, pcontrol);
break;
case 0x2: /* Disconnect-Reconnect page, all devices */
len = disconnect_pg(bp, pcontrol);
break;
case 0x3: /* Format device page, direct access */
len = format_pg(bp, pcontrol, dev);
break;
case 0x4: /* Rigid disk geometry */
len = rigid_geo_pg(bp, pcontrol, dev);
break;
case 0x8: /* Caching page, direct access */
len = caching_pg(bp, pcontrol, dev);
break;
case 0xa: /* Control Mode page, all devices */
len = ctrl_m_pg(bp, pcontrol, dev);
break;
case 0x1c: /* Informational Exceptions Mode page, all devices */
len = iec_m_pg(bp, pcontrol);
break;
case 0x3f: /* Read all Mode pages */
len = err_recov_pg(bp, pcontrol);
len += disconnect_pg(bp + len, pcontrol);
len += format_pg(bp + len, pcontrol, dev);
len += caching_pg(bp + len, pcontrol, dev);
len += ctrl_m_pg(bp + len, pcontrol, dev);
len += iec_m_pg(bp + len, pcontrol);
len += rigid_geo_pg(bp + len, pcontrol, dev);
break;
default:
TRACE_DBG("MODE SENSE: Unsupported page %x", pcode);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out;
}
offset += len;
if (msense_6)
buf[0] = offset - 1;
else {
buf[0] = ((offset - 2) >> 8) & 0xff;
buf[1] = (offset - 2) & 0xff;
}
sBUG_ON(offset >= (int)sizeof(buf));
if (offset > length)
offset = length;
memcpy(address, buf, offset);
if (offset < reply->resp_data_len)
set_resp_data_len(vcmd, offset);
out:
TRACE_EXIT();
return;
}
static int set_wt(struct vdisk_dev *dev, int wt)
{
int res = 0;
TRACE_ENTRY();
if ((dev->wt_flag == wt) || dev->nullio)
goto out;
pthread_mutex_lock(&dev->dev_mutex);
dev->wt_flag = wt;
pthread_mutex_unlock(&dev->dev_mutex);
out:
TRACE_EXIT_RES(res);
return res;
}
static void exec_mode_select(struct vdisk_cmd *vcmd)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
int length = cmd->bufflen;
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
int mselect_6, offset;
TRACE_ENTRY();
mselect_6 = (MODE_SELECT == cmd->cdb[0]);
if (!(cmd->cdb[1] & PF) || (cmd->cdb[1] & SP)) {
PRINT_ERROR("MODE SELECT: PF and/or SP are wrongly set "
"(cdb[1]=%x)", cmd->cdb[1]);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out;
}
if (mselect_6) {
offset = 4;
} else {
offset = 8;
}
if (address[offset - 1] == 8) {
offset += 8;
} else if (address[offset - 1] != 0) {
PRINT_ERROR("%s", "MODE SELECT: Wrong parameters list "
"length");
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list));
goto out;
}
while (length > offset + 2) {
if (address[offset] & PS) {
PRINT_ERROR("%s", "MODE SELECT: Illegal PS bit");
set_cmd_error(vcmd, SCST_LOAD_SENSE(
scst_sense_invalid_field_in_parm_list));
goto out;
}
if ((address[offset] & 0x3f) == 0x8) { /* Caching page */
if (address[offset + 1] != 18) {
PRINT_ERROR("%s", "MODE SELECT: Invalid "
"caching page request");
set_cmd_error(vcmd, SCST_LOAD_SENSE(
scst_sense_invalid_field_in_parm_list));
goto out;
}
if (set_wt(dev,
(address[offset + 2] & WCE) ? 0 : 1) != 0) {
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
}
break;
}
offset += address[offset + 1];
}
out:
TRACE_EXIT();
return;
}
static void exec_read_capacity(struct vdisk_cmd *vcmd)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
int length = cmd->bufflen;
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
uint32_t blocksize;
uint64_t nblocks;
uint8_t buffer[8];
TRACE_ENTRY();
blocksize = dev->block_size;
nblocks = dev->nblocks;
/* last block on the dev is (nblocks-1) */
memset(buffer, 0, sizeof(buffer));
if (nblocks >> 32) {
buffer[0] = 0xFF;
buffer[1] = 0xFF;
buffer[2] = 0xFF;
buffer[3] = 0xFF;
} else {
buffer[0] = ((nblocks - 1) >> (BYTE * 3)) & 0xFF;
buffer[1] = ((nblocks - 1) >> (BYTE * 2)) & 0xFF;
buffer[2] = ((nblocks - 1) >> (BYTE * 1)) & 0xFF;
buffer[3] = ((nblocks - 1) >> (BYTE * 0)) & 0xFF;
}
buffer[4] = (blocksize >> (BYTE * 3)) & 0xFF;
buffer[5] = (blocksize >> (BYTE * 2)) & 0xFF;
buffer[6] = (blocksize >> (BYTE * 1)) & 0xFF;
buffer[7] = (blocksize >> (BYTE * 0)) & 0xFF;
length = min(length, (int)sizeof(buffer));
memcpy(address, buffer, length);
if (length < reply->resp_data_len)
set_resp_data_len(vcmd, length);
TRACE_EXIT();
return;
}
static void exec_read_capacity16(struct vdisk_cmd *vcmd)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
int length = cmd->bufflen;
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
uint32_t blocksize;
uint64_t nblocks;
uint8_t buffer[32];
TRACE_ENTRY();
blocksize = dev->block_size;
nblocks = dev->nblocks - 1;
memset(buffer, 0, sizeof(buffer));
buffer[0] = nblocks >> 56;
buffer[1] = (nblocks >> 48) & 0xFF;
buffer[2] = (nblocks >> 40) & 0xFF;
buffer[3] = (nblocks >> 32) & 0xFF;
buffer[4] = (nblocks >> 24) & 0xFF;
buffer[5] = (nblocks >> 16) & 0xFF;
buffer[6] = (nblocks >> 8) & 0xFF;
buffer[7] = nblocks& 0xFF;
buffer[8] = (blocksize >> (BYTE * 3)) & 0xFF;
buffer[9] = (blocksize >> (BYTE * 2)) & 0xFF;
buffer[10] = (blocksize >> (BYTE * 1)) & 0xFF;
buffer[11] = (blocksize >> (BYTE * 0)) & 0xFF;
switch (blocksize) {
case 512:
buffer[13] = 3;
break;
case 1024:
buffer[13] = 2;
break;
case 2048:
buffer[13] = 1;
break;
case 4096:
default:
buffer[13] = 0;
break;
}
length = min(length, (int)sizeof(buffer));
memcpy(address, buffer, length);
if (length < reply->resp_data_len)
set_resp_data_len(vcmd, length);
TRACE_EXIT();
return;
}
static void exec_read_toc(struct vdisk_cmd *vcmd)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
int32_t off = 0;
int length = cmd->bufflen;
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
uint32_t nblocks;
uint8_t buffer[4+8+8] = { 0x00, 0x0a, 0x01, 0x01, 0x00, 0x14,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
TRACE_ENTRY();
if (dev->type != TYPE_ROM) {
PRINT_ERROR("%s", "READ TOC for non-CDROM device");
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
goto out;
}
if (cmd->cdb[2] & 0x0e/*Format*/) {
PRINT_ERROR("%s", "READ TOC: invalid requested data format");
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out;
}
if ((cmd->cdb[6] != 0 && (cmd->cdb[2] & 0x01)) ||
(cmd->cdb[6] > 1 && cmd->cdb[6] != 0xAA)) {
PRINT_ERROR("READ TOC: invalid requested track number %x",
cmd->cdb[6]);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out;
}
/* ToDo when you have > 8TB ROM device. */
nblocks = (uint32_t)dev->nblocks;
/* Header */
memset(buffer, 0, sizeof(buffer));
buffer[2] = 0x01; /* First Track/Session */
buffer[3] = 0x01; /* Last Track/Session */
off = 4;
if (cmd->cdb[6] <= 1)
{
/* Fistr TOC Track Descriptor */
buffer[off+1] = 0x14; /* ADDR 0x10 - Q Sub-channel encodes current position data
CONTROL 0x04 - Data track, recoreded uninterrupted */
buffer[off+2] = 0x01; /* Track Number */
off += 8;
}
if (!(cmd->cdb[2] & 0x01))
{
/* Lead-out area TOC Track Descriptor */
buffer[off+1] = 0x14;
buffer[off+2] = 0xAA; /* Track Number */
buffer[off+4] = (nblocks >> (BYTE * 3)) & 0xFF; /* Track Start Address */
buffer[off+5] = (nblocks >> (BYTE * 2)) & 0xFF;
buffer[off+6] = (nblocks >> (BYTE * 1)) & 0xFF;
buffer[off+7] = (nblocks >> (BYTE * 0)) & 0xFF;
off += 8;
}
buffer[1] = off - 2; /* Data Length */
if (off > length)
off = length;
memcpy(address, buffer, off);
if (off < reply->resp_data_len)
set_resp_data_len(vcmd, off);
out:
TRACE_EXIT();
return;
}
static void exec_prevent_allow_medium_removal(struct vdisk_cmd *vcmd)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
TRACE_DBG("PERSIST/PREVENT 0x%02x", cmd->cdb[4]);
pthread_mutex_lock(&dev->dev_mutex);
dev->prevent_allow_medium_removal = cmd->cdb[4] & 0x01 ? 1 : 0;
pthread_mutex_unlock(&dev->dev_mutex);
return;
}
static int exec_fsync(struct vdisk_cmd *vcmd)
{
int res = 0;
struct vdisk_dev *dev = vcmd->dev;
/* Hopefully, the compiler will generate the single comparison */
if (dev->nv_cache || dev->wt_flag || dev->rd_only_flag ||
dev->o_direct_flag || dev->nullio)
goto out;
/* ToDo: use sync_file_range() instead */
fsync(vcmd->fd);
out:
TRACE_EXIT_RES(res);
return res;
}
static void exec_read(struct vdisk_cmd *vcmd, loff_t loff)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
int length = cmd->bufflen;
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
int fd = vcmd->fd;
loff_t err;
TRACE_ENTRY();
TRACE_DBG("reading off %"PRId64", len %d", loff, length);
if (dev->nullio)
err = length;
else {
/* SEEK */
err = lseek64(fd, loff, 0/*SEEK_SET*/);
if (err != loff) {
PRINT_ERROR("lseek trouble %"PRId64" != %"PRId64
" (errno %d)", (uint64_t)err, (uint64_t)loff,
errno);
set_cmd_error(vcmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
}
/* READ */
err = read(fd, address, length);
}
if ((err < 0) || (err < length)) {
PRINT_ERROR("read() returned %"PRId64" from %d (errno %d)",
(uint64_t)err, length, errno);
if (err == -EAGAIN)
set_busy(vcmd);
else {
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_read_error));
}
goto out;
}
set_resp_data_len(vcmd, cmd->bufflen);
out:
TRACE_EXIT();
return;
}
static void exec_write(struct vdisk_cmd *vcmd, loff_t loff)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
loff_t err;
int length = cmd->bufflen;
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
int fd = vcmd->fd;
TRACE_ENTRY();
restart:
TRACE_DBG("writing off %"PRId64", len %d", loff, length);
if (dev->nullio)
err = length;
else {
/* SEEK */
err = lseek64(fd, loff, 0/*SEEK_SET*/);
if (err != loff) {
PRINT_ERROR("lseek trouble %"PRId64" != %"PRId64
" (errno %d)", (uint64_t)err, (uint64_t)loff,
errno);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
}
/* WRITE */
err = write(fd, address, length);
}
if (err < 0) {
PRINT_ERROR("write() returned %"PRId64" from %d (errno %d, "
"cmd_h %x)", err, length, errno, vcmd->cmd->cmd_h);
if (err == -EAGAIN)
set_busy(vcmd);
else {
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_write_error));
}
goto out;
} else if (err < length) {
/*
* Probably that's wrong, but sometimes write() returns
* value less, than requested. Let's restart.
*/
TRACE_MGMT_DBG("write() returned %d from %d", (int)err, length);
if (err == 0) {
PRINT_INFO("Suspicious: write() returned 0 from "
"%d", length);
}
length -= err;
goto restart;
}
out:
TRACE_EXIT();
return;
}
static void exec_verify(struct vdisk_cmd *vcmd, loff_t loff)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
loff_t err;
int64_t length = cmd->bufflen;
uint8_t *address = (uint8_t *)(unsigned long)cmd->pbuf;
int compare;
int fd = vcmd->fd;
uint8_t mem_verify[128*1024];
TRACE_ENTRY();
if (exec_fsync(vcmd) != 0)
goto out;
/*
* Until the cache is cleared prior the verifying, there is not
* much point in this code. ToDo.
*
* Nevertherless, this code is valuable if the data have not read
* from the file/disk yet.
*/
/* SEEK */
if (!dev->nullio) {
err = lseek64(fd, loff, 0/*SEEK_SET*/);
if (err != loff) {
PRINT_ERROR("lseek trouble %"PRId64" != %"PRId64
" (errno %d)", (uint64_t)err,
(uint64_t)loff, errno);
set_cmd_error(vcmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
}
}
if ((length == 0) && (cmd->data_len != 0)) {
length = cmd->data_len;
compare = 0;
} else
compare = 1;
while (length > 0) {
int64_t len_mem = (length > (int)sizeof(mem_verify)) ?
(int)sizeof(mem_verify) : length;
TRACE_DBG("Verify: length %"PRId64" - len_mem %"PRId64,
length, len_mem);
if (!dev->nullio)
err = read(fd, (char *)mem_verify, len_mem);
else
err = len_mem;
if ((err < 0) || (err < len_mem)) {
PRINT_ERROR("read() returned %"PRId64" from %"PRId64" "
"(errno %d)", (uint64_t)err, len_mem, errno);
if (err == -EAGAIN)
set_busy(vcmd);
else {
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_read_error));
}
goto out;
}
if (compare && memcmp(address, mem_verify, len_mem) != 0) {
TRACE_DBG("Verify: error memcmp length %"PRId64, length);
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_miscompare_error));
goto out;
}
length -= len_mem;
address += len_mem;
}
if (length < 0) {
PRINT_ERROR("Failure: %"PRId64, length);
set_cmd_error(vcmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
}
out:
TRACE_EXIT();
return;
}
static void exec_write_same(struct vdisk_cmd *vcmd)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
uint64_t blocks = cmd->data_len >> dev->block_shift;
static struct scst_user_data_descriptor uwhere[3];
TRACE_ENTRY();
if (blocks < 2)
goto out;
/* It's only a debug code, so it's OK to use static descr */
memset(uwhere, 0, sizeof(uwhere));
uwhere[0].usdd_lba = cmd->lba;
uwhere[0].usdd_blocks = 1;
uwhere[1].usdd_lba = cmd->lba+1;
uwhere[1].usdd_blocks = blocks-1;
reply->reply_type = SCST_EXEC_REPLY_DO_WRITE_SAME;
reply->ws_descriptors_len = sizeof(uwhere);
reply->ws_descriptors = (unsigned long)uwhere;
out:
TRACE_EXIT();
return;
}