mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-21 20:51:27 +00:00
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
921 lines
24 KiB
C
921 lines
24 KiB
C
/*
|
|
* Copyright (c) 2010 Cisco Systems, Inc.
|
|
*
|
|
* This program is free software; you may 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.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
#include <scsi/libfc.h>
|
|
#include <scsi/fc_encode.h>
|
|
#include "fcst.h"
|
|
|
|
/*
|
|
* Append string to buffer safely.
|
|
* Also prepends a space if there's already something the buf.
|
|
*/
|
|
static void ft_cmd_flag(char *buf, size_t len, const char *desc)
|
|
{
|
|
if (buf[0])
|
|
strlcat(buf, " ", len);
|
|
strlcat(buf, desc, len);
|
|
}
|
|
|
|
/*
|
|
* Debug: dump command.
|
|
*/
|
|
void ft_cmd_dump(struct scst_cmd *cmd, const char *caller)
|
|
{
|
|
static atomic_t serial;
|
|
struct ft_cmd *fcmd;
|
|
struct fc_frame_header *fh;
|
|
char prefix[30];
|
|
char buf[150];
|
|
|
|
if (!(ft_debug_logging & FT_DEBUG_IO))
|
|
return;
|
|
|
|
fcmd = scst_cmd_get_tgt_priv(cmd);
|
|
fh = fc_frame_header_get(fcmd->req_frame);
|
|
snprintf(prefix, sizeof(prefix), FT_MODULE ": cmd %2x",
|
|
atomic_inc_return(&serial) & 0xff);
|
|
|
|
pr_info("%s %s oid %x oxid %x resp_len %u\n",
|
|
prefix, caller, ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id),
|
|
scst_cmd_get_resp_data_len(cmd));
|
|
pr_info("%s scst_cmd %p wlen %u rlen %u\n",
|
|
prefix, cmd, fcmd->write_data_len, fcmd->read_data_len);
|
|
pr_info("%s exp_dir %x exp_xfer_len %d exp_in_len %d\n",
|
|
prefix, cmd->expected_data_direction,
|
|
cmd->expected_transfer_len_full,
|
|
cmd->expected_out_transfer_len);
|
|
pr_info("%s dir %x data_len %lld bufflen %d out_bufflen %d\n",
|
|
prefix, cmd->data_direction, cmd->data_len,
|
|
cmd->bufflen, cmd->out_bufflen);
|
|
pr_info("%s sg_cnt reg %d in %d tgt %d tgt_in %d\n",
|
|
prefix, cmd->sg_cnt, cmd->out_sg_cnt,
|
|
cmd->tgt_i_sg_cnt, cmd->tgt_out_sg_cnt);
|
|
|
|
buf[0] = '\0';
|
|
if (cmd->sent_for_exec)
|
|
ft_cmd_flag(buf, sizeof(buf), "sent");
|
|
if (cmd->completed)
|
|
ft_cmd_flag(buf, sizeof(buf), "comp");
|
|
if (cmd->ua_ignore)
|
|
ft_cmd_flag(buf, sizeof(buf), "ua_ign");
|
|
if (cmd->atomic)
|
|
ft_cmd_flag(buf, sizeof(buf), "atom");
|
|
if (cmd->double_ua_possible)
|
|
ft_cmd_flag(buf, sizeof(buf), "dbl_ua_poss");
|
|
if (cmd->is_send_status)
|
|
ft_cmd_flag(buf, sizeof(buf), "send_stat");
|
|
if (cmd->retry)
|
|
ft_cmd_flag(buf, sizeof(buf), "retry");
|
|
if (cmd->internal)
|
|
ft_cmd_flag(buf, sizeof(buf), "internal");
|
|
if (cmd->unblock_dev)
|
|
ft_cmd_flag(buf, sizeof(buf), "unblock_dev");
|
|
if (cmd->cmd_hw_pending)
|
|
ft_cmd_flag(buf, sizeof(buf), "hw_pend");
|
|
if (cmd->tgt_need_alloc_data_buf)
|
|
ft_cmd_flag(buf, sizeof(buf), "tgt_need_alloc");
|
|
if (cmd->tgt_i_data_buf_alloced)
|
|
ft_cmd_flag(buf, sizeof(buf), "tgt_i_alloced");
|
|
if (cmd->dh_data_buf_alloced)
|
|
ft_cmd_flag(buf, sizeof(buf), "dh_alloced");
|
|
if (cmd->expected_values_set)
|
|
ft_cmd_flag(buf, sizeof(buf), "exp_val");
|
|
if (cmd->sg_buff_modified)
|
|
ft_cmd_flag(buf, sizeof(buf), "sg_buf_mod");
|
|
if (cmd->preprocessing_only)
|
|
ft_cmd_flag(buf, sizeof(buf), "pre_only");
|
|
if (cmd->sn_set)
|
|
ft_cmd_flag(buf, sizeof(buf), "sn_set");
|
|
if (cmd->hq_cmd_inced)
|
|
ft_cmd_flag(buf, sizeof(buf), "hq_cmd_inc");
|
|
if (cmd->set_sn_on_restart_cmd)
|
|
ft_cmd_flag(buf, sizeof(buf), "set_sn_on_restart");
|
|
if (cmd->no_sgv)
|
|
ft_cmd_flag(buf, sizeof(buf), "no_sgv");
|
|
if (cmd->may_need_dma_sync)
|
|
ft_cmd_flag(buf, sizeof(buf), "dma_sync");
|
|
if (cmd->out_of_sn)
|
|
ft_cmd_flag(buf, sizeof(buf), "oo_sn");
|
|
if (cmd->inc_expected_sn_on_done)
|
|
ft_cmd_flag(buf, sizeof(buf), "inc_sn_exp");
|
|
if (cmd->done)
|
|
ft_cmd_flag(buf, sizeof(buf), "done");
|
|
if (cmd->finished)
|
|
ft_cmd_flag(buf, sizeof(buf), "fin");
|
|
|
|
pr_info("%s flags %s\n", prefix, buf);
|
|
pr_info("%s lun %lld sn %d tag %lld cmd_flags %lx\n",
|
|
prefix, cmd->lun, cmd->sn, cmd->tag, cmd->cmd_flags);
|
|
pr_info("%s tgt_sn %d op_flags %x op %s\n",
|
|
prefix, cmd->tgt_sn, cmd->op_flags, cmd->op_name);
|
|
pr_info("%s status %x msg_status %x "
|
|
"host_status %x driver_status %x\n",
|
|
prefix, cmd->status, cmd->msg_status,
|
|
cmd->host_status, cmd->driver_status);
|
|
pr_info("%s cdb_len %d\n", prefix, cmd->cdb_len);
|
|
snprintf(buf, sizeof(buf), "%s cdb ", prefix);
|
|
print_hex_dump(KERN_INFO, buf, DUMP_PREFIX_NONE,
|
|
16, 4, cmd->cdb, SCST_MAX_CDB_SIZE, 0);
|
|
}
|
|
|
|
/*
|
|
* Debug: dump mgmt command.
|
|
*/
|
|
static void ft_cmd_tm_dump(struct scst_mgmt_cmd *mcmd, const char *caller)
|
|
{
|
|
struct ft_cmd *fcmd;
|
|
struct fc_frame_header *fh;
|
|
char prefix[30];
|
|
char buf[150];
|
|
|
|
if (!(ft_debug_logging & FT_DEBUG_IO))
|
|
return;
|
|
fcmd = scst_mgmt_cmd_get_tgt_priv(mcmd);
|
|
fh = fc_frame_header_get(fcmd->req_frame);
|
|
|
|
snprintf(prefix, sizeof(prefix), FT_MODULE ": mcmd");
|
|
|
|
pr_info("%s %s oid %x oxid %x lun %lld\n",
|
|
prefix, caller, ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id),
|
|
(unsigned long long)mcmd->lun);
|
|
pr_info("%s state %d fn %d fin_wait %d done_wait %d comp %d\n",
|
|
prefix, mcmd->state, mcmd->fn,
|
|
mcmd->cmd_finish_wait_count, mcmd->cmd_done_wait_count,
|
|
mcmd->completed_cmd_count);
|
|
buf[0] = '\0';
|
|
if (mcmd->needs_unblocking)
|
|
ft_cmd_flag(buf, sizeof(buf), "needs_unblock");
|
|
if (mcmd->lun_set)
|
|
ft_cmd_flag(buf, sizeof(buf), "lun_set");
|
|
if (mcmd->cmd_sn_set)
|
|
ft_cmd_flag(buf, sizeof(buf), "cmd_sn_set");
|
|
pr_info("%s flags %s\n", prefix, buf);
|
|
if (mcmd->cmd_to_abort)
|
|
ft_cmd_dump(mcmd->cmd_to_abort, caller);
|
|
}
|
|
|
|
/**
|
|
* ft_set_cmd_state() - set the state of a command
|
|
*/
|
|
static enum ft_cmd_state ft_set_cmd_state(struct ft_cmd *fcmd,
|
|
enum ft_cmd_state new)
|
|
{
|
|
enum ft_cmd_state previous;
|
|
|
|
spin_lock(&fcmd->lock);
|
|
previous = fcmd->state;
|
|
if (previous != FT_STATE_DONE)
|
|
fcmd->state = new;
|
|
spin_unlock(&fcmd->lock);
|
|
|
|
return previous;
|
|
}
|
|
|
|
/**
|
|
* ft_test_and_set_cmd_state() - test and set the state of a command
|
|
*
|
|
* Returns true if and only if the previous command state was equal to 'old'.
|
|
*/
|
|
bool ft_test_and_set_cmd_state(struct ft_cmd *fcmd, enum ft_cmd_state old,
|
|
enum ft_cmd_state new)
|
|
{
|
|
enum ft_cmd_state previous;
|
|
|
|
WARN_ON(old == FT_STATE_DONE);
|
|
WARN_ON(new == FT_STATE_NEW);
|
|
|
|
spin_lock(&fcmd->lock);
|
|
previous = fcmd->state;
|
|
if (previous == old)
|
|
fcmd->state = new;
|
|
spin_unlock(&fcmd->lock);
|
|
|
|
return previous == old;
|
|
}
|
|
|
|
static void ft_abort_cmd(struct scst_cmd *cmd)
|
|
{
|
|
struct ft_cmd *fcmd = scst_cmd_get_tgt_priv(cmd);
|
|
struct fc_seq *sp = fcmd->seq;
|
|
struct fc_exch *ep = fc_seq_exch(sp);
|
|
|
|
pr_err("%s: cmd %p ox_id %#x rx_id %#x state %d\n", __func__, cmd,
|
|
ep->oxid, ep->rxid, fcmd->state);
|
|
|
|
spin_lock(&fcmd->lock);
|
|
switch (fcmd->state) {
|
|
case FT_STATE_NEW:
|
|
case FT_STATE_DATA_IN:
|
|
case FT_STATE_MGMT:
|
|
/*
|
|
* Do nothing - defer abort processing until
|
|
* srpt_xmit_response() is invoked.
|
|
*/
|
|
break;
|
|
case FT_STATE_NEED_DATA:
|
|
/* SCST_DATA_WRITE */
|
|
fcmd->state = FT_STATE_DATA_IN;
|
|
scst_rx_data(cmd, SCST_RX_STATUS_ERROR_FATAL,
|
|
SCST_CONTEXT_THREAD);
|
|
break;
|
|
case FT_STATE_CMD_RSP_SENT:
|
|
/*
|
|
* ft_send_response() is either in progress or has finished.
|
|
* Wait until the SCST core has invoked ft_cmd_done().
|
|
*/
|
|
break;
|
|
case FT_STATE_MGMT_RSP_SENT:
|
|
default:
|
|
pr_info("Unexpected command state %d\n", fcmd->state);
|
|
__WARN();
|
|
fcmd->state = FT_STATE_DONE;
|
|
break;
|
|
}
|
|
spin_unlock(&fcmd->lock);
|
|
}
|
|
|
|
/*
|
|
* Free command and associated frame.
|
|
*/
|
|
static void ft_cmd_done(struct ft_cmd *fcmd)
|
|
{
|
|
struct fc_frame *fp = fcmd->req_frame;
|
|
struct fc_seq *sp = fcmd->seq;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
|
struct fc_lport *lport = fr_dev(fp);
|
|
#endif
|
|
|
|
if (sp)
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
|
fc_exch_done(sp);
|
|
#else
|
|
lport->tt.exch_done(sp);
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
|
|
if (fr_seq(fp))
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
|
fc_seq_release(fr_seq(fp));
|
|
#else
|
|
lport->tt.seq_release(fr_seq(fp));
|
|
#endif
|
|
#endif
|
|
|
|
fc_frame_free(fp);
|
|
kfree(fcmd);
|
|
}
|
|
|
|
/*
|
|
* Free command - callback from SCST.
|
|
*/
|
|
void ft_cmd_free(struct scst_cmd *cmd)
|
|
{
|
|
struct ft_cmd *fcmd = scst_cmd_get_tgt_priv(cmd);
|
|
|
|
ft_cmd_done(fcmd);
|
|
}
|
|
|
|
/*
|
|
* Send response.
|
|
*/
|
|
int ft_send_response(struct scst_cmd *cmd)
|
|
{
|
|
struct ft_cmd *fcmd;
|
|
struct fc_frame *fp;
|
|
struct fcp_resp_with_ext *fcp;
|
|
struct fc_lport *lport;
|
|
struct fc_exch *ep;
|
|
unsigned int slen;
|
|
size_t len;
|
|
enum ft_cmd_state prev_state;
|
|
int resid = 0;
|
|
int bi_resid = 0;
|
|
int error;
|
|
int dir;
|
|
u32 status;
|
|
|
|
ft_cmd_dump(cmd, __func__);
|
|
fcmd = scst_cmd_get_tgt_priv(cmd);
|
|
ep = fc_seq_exch(fcmd->seq);
|
|
lport = ep->lp;
|
|
|
|
WARN_ON(fcmd->state != FT_STATE_NEW && fcmd->state != FT_STATE_DATA_IN);
|
|
prev_state = ft_set_cmd_state(fcmd, FT_STATE_CMD_RSP_SENT);
|
|
|
|
if (scst_cmd_aborted_on_xmit(cmd)) {
|
|
FT_IO_DBG("cmd aborted did %x oxid %x\n", ep->did, ep->oxid);
|
|
ft_abort_cmd(cmd);
|
|
goto done;
|
|
}
|
|
|
|
if (!scst_cmd_get_is_send_status(cmd)) {
|
|
FT_IO_DBG("send status not set. feature not implemented\n");
|
|
error = SCST_TGT_RES_FATAL_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
status = scst_cmd_get_status(cmd);
|
|
dir = scst_cmd_get_data_direction(cmd);
|
|
|
|
slen = scst_cmd_get_sense_buffer_len(cmd);
|
|
len = sizeof(*fcp) + slen;
|
|
|
|
/*
|
|
* Send read data and set underflow/overflow residual count.
|
|
* For bi-directional comands, the bi_resid is for the read direction.
|
|
*/
|
|
if (dir & SCST_DATA_WRITE)
|
|
resid = (signed int)scst_cmd_get_bufflen(cmd) -
|
|
fcmd->write_data_len;
|
|
if (dir & SCST_DATA_READ) {
|
|
error = ft_send_read_data(cmd);
|
|
if (error) {
|
|
FT_ERR("ft_send_read_data returned %d\n", error);
|
|
goto err;
|
|
}
|
|
|
|
if (dir == SCST_DATA_BIDI) {
|
|
bi_resid = (signed int)scst_cmd_get_out_bufflen(cmd) -
|
|
scst_cmd_get_resp_data_len(cmd);
|
|
if (bi_resid)
|
|
len += sizeof(__be32);
|
|
} else
|
|
resid = (signed int)scst_cmd_get_bufflen(cmd) -
|
|
scst_cmd_get_resp_data_len(cmd);
|
|
}
|
|
|
|
fp = fc_frame_alloc(lport, len);
|
|
if (!fp) {
|
|
error = SCST_TGT_RES_QUEUE_FULL;
|
|
goto err;
|
|
}
|
|
|
|
fcp = fc_frame_payload_get(fp, len);
|
|
memset(fcp, 0, sizeof(*fcp));
|
|
fcp->resp.fr_status = status;
|
|
|
|
if (slen) {
|
|
fcp->resp.fr_flags |= FCP_SNS_LEN_VAL;
|
|
fcp->ext.fr_sns_len = htonl(slen);
|
|
memcpy(fcp + 1, scst_cmd_get_sense_buffer(cmd), slen);
|
|
}
|
|
if (bi_resid) {
|
|
if (bi_resid < 0) {
|
|
fcp->resp.fr_flags |= FCP_BIDI_READ_OVER;
|
|
bi_resid = -bi_resid;
|
|
} else
|
|
fcp->resp.fr_flags |= FCP_BIDI_READ_UNDER;
|
|
*(__be32 *)((u8 *)(fcp + 1) + slen) = htonl(bi_resid);
|
|
}
|
|
if (resid) {
|
|
if (resid < 0) {
|
|
resid = -resid;
|
|
fcp->resp.fr_flags |= FCP_RESID_OVER;
|
|
} else
|
|
fcp->resp.fr_flags |= FCP_RESID_UNDER;
|
|
fcp->ext.fr_resid = htonl(resid);
|
|
}
|
|
FT_IO_DBG("response did %x oxid %x\n", ep->did, ep->oxid);
|
|
|
|
/*
|
|
* Send response.
|
|
*/
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
|
fcmd->seq = fc_seq_start_next(fcmd->seq);
|
|
#else
|
|
fcmd->seq = lport->tt.seq_start_next(fcmd->seq);
|
|
#endif
|
|
fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
|
|
FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
|
error = FCST_INJ_SEND_ERR(fc_seq_send(lport, fcmd->seq, fp));
|
|
#else
|
|
error = FCST_INJ_SEND_ERR(lport->tt.seq_send(lport, fcmd->seq, fp));
|
|
#endif
|
|
if (error < 0) {
|
|
pr_err("Sending response for exchange with OX_ID %#x and RX_ID"
|
|
" %#x failed: %d\n", ep->oxid, ep->rxid, error);
|
|
error = error == -ENOMEM ? SCST_TGT_RES_QUEUE_FULL :
|
|
SCST_TGT_RES_FATAL_ERROR;
|
|
goto err;
|
|
}
|
|
done:
|
|
scst_tgt_cmd_done(cmd, SCST_CONTEXT_SAME);
|
|
return SCST_TGT_RES_SUCCESS;
|
|
|
|
err:
|
|
ft_set_cmd_state(fcmd, prev_state);
|
|
WARN_ONCE(error != SCST_TGT_RES_QUEUE_FULL &&
|
|
error != SCST_TGT_RES_FATAL_ERROR,
|
|
"%s: invalid error code %d\n",
|
|
__func__, error);
|
|
return error;
|
|
|
|
}
|
|
|
|
/*
|
|
* FC sequence response handler for follow-on sequences (data) and aborts.
|
|
*/
|
|
static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg)
|
|
{
|
|
struct scst_cmd *cmd = arg;
|
|
struct fc_frame_header *fh;
|
|
|
|
/*
|
|
* If an error is being reported, it must be FC_EX_CLOSED.
|
|
* Timeouts don't occur on incoming requests, and there are
|
|
* currently no other errors.
|
|
* The PRLO handler will be also called by libfc to delete
|
|
* the session and all pending commands, so we ignore this response.
|
|
*/
|
|
if (IS_ERR(fp)) {
|
|
pr_err("exchange error %ld - aborting cmd %p / tag %lld\n",
|
|
-PTR_ERR(fp), cmd, cmd->tag);
|
|
ft_abort_cmd(cmd);
|
|
return;
|
|
}
|
|
|
|
fh = fc_frame_header_get(fp);
|
|
switch (fh->fh_r_ctl) {
|
|
case FC_RCTL_DD_SOL_DATA: /* write data */
|
|
ft_recv_write_data(cmd, fp);
|
|
break;
|
|
case FC_RCTL_DD_UNSOL_CTL: /* command */
|
|
case FC_RCTL_DD_SOL_CTL: /* transfer ready */
|
|
case FC_RCTL_DD_DATA_DESC: /* transfer ready */
|
|
default:
|
|
pr_info("%s: unhandled frame r_ctl %x\n", __func__,
|
|
fh->fh_r_ctl);
|
|
fc_frame_free(fp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Command timeout.
|
|
* SCST calls this when the command has taken too long in the device handler.
|
|
*/
|
|
void ft_cmd_timeout(struct scst_cmd *cmd)
|
|
{
|
|
FT_IO_DBG("%p: timeout\n", cmd);
|
|
ft_abort_cmd(cmd);
|
|
}
|
|
|
|
/*
|
|
* Send TX_RDY (transfer ready).
|
|
*/
|
|
int ft_send_xfer_rdy(struct scst_cmd *cmd)
|
|
{
|
|
struct ft_cmd *fcmd;
|
|
struct fc_frame *fp;
|
|
struct fcp_txrdy *txrdy;
|
|
struct fc_lport *lport;
|
|
struct fc_exch *ep;
|
|
int error;
|
|
|
|
fcmd = scst_cmd_get_tgt_priv(cmd);
|
|
|
|
ep = fc_seq_exch(fcmd->seq);
|
|
lport = ep->lp;
|
|
fp = fc_frame_alloc(lport, sizeof(*txrdy));
|
|
if (!fp)
|
|
return SCST_TGT_RES_QUEUE_FULL;
|
|
|
|
WARN_ON(!ft_test_and_set_cmd_state(fcmd, FT_STATE_NEW,
|
|
FT_STATE_NEED_DATA));
|
|
|
|
txrdy = fc_frame_payload_get(fp, sizeof(*txrdy));
|
|
memset(txrdy, 0, sizeof(*txrdy));
|
|
txrdy->ft_data_ro = 0;
|
|
txrdy->ft_burst_len = htonl(scst_cmd_get_bufflen(cmd));
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
|
fcmd->seq = fc_seq_start_next(fcmd->seq);
|
|
#else
|
|
fcmd->seq = lport->tt.seq_start_next(fcmd->seq);
|
|
#endif
|
|
fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
|
|
FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
|
error = FCST_INJ_SEND_ERR(fc_seq_send(lport, fcmd->seq, fp));
|
|
#else
|
|
error = FCST_INJ_SEND_ERR(lport->tt.seq_send(lport, fcmd->seq, fp));
|
|
#endif
|
|
switch (error) {
|
|
case 0:
|
|
return SCST_TGT_RES_SUCCESS;
|
|
case -ENOMEM:
|
|
ft_set_cmd_state(fcmd, FT_STATE_NEW);
|
|
return SCST_TGT_RES_QUEUE_FULL;
|
|
default:
|
|
ft_set_cmd_state(fcmd, FT_STATE_NEW);
|
|
return SCST_TGT_RES_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send a FCP response including SCSI status and optional FCP rsp_code.
|
|
* status is SAM_STAT_GOOD (zero) if code is valid.
|
|
* This is used in error cases, such as allocation failures.
|
|
*/
|
|
static void ft_send_resp_status(struct fc_frame *rx_fp, u32 status,
|
|
enum fcp_resp_rsp_codes code)
|
|
{
|
|
struct fc_frame *fp;
|
|
struct fc_seq *sp;
|
|
const struct fc_frame_header *fh;
|
|
size_t len;
|
|
struct fcp_resp_with_ext *fcp;
|
|
struct fcp_resp_rsp_info *info;
|
|
struct fc_lport *lport;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
|
|
struct fc_exch *ep;
|
|
#endif
|
|
|
|
fh = fc_frame_header_get(rx_fp);
|
|
FT_IO_DBG("FCP error response: did %x oxid %x status %x code %x\n",
|
|
ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id), status, code);
|
|
lport = fr_dev(rx_fp);
|
|
len = sizeof(*fcp);
|
|
if (status == SAM_STAT_GOOD)
|
|
len += sizeof(*info);
|
|
fp = fc_frame_alloc(lport, len);
|
|
if (!fp)
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
|
|
goto out;
|
|
#else
|
|
return;
|
|
#endif
|
|
fcp = fc_frame_payload_get(fp, len);
|
|
memset(fcp, 0, len);
|
|
fcp->resp.fr_status = status;
|
|
if (status == SAM_STAT_GOOD) {
|
|
fcp->ext.fr_rsp_len = htonl(sizeof(*info));
|
|
fcp->resp.fr_flags |= FCP_RSP_LEN_VAL;
|
|
info = (struct fcp_resp_rsp_info *)(fcp + 1);
|
|
info->rsp_code = code;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
|
|
sp = fr_seq(rx_fp);
|
|
sp = lport->tt.seq_start_next(sp);
|
|
ep = fc_seq_exch(sp);
|
|
fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
|
|
FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);
|
|
|
|
lport->tt.seq_send(lport, sp, fp);
|
|
out:
|
|
;
|
|
#else
|
|
fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_DD_CMD_STATUS, 0);
|
|
sp = fr_seq(fp);
|
|
if (sp)
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
|
fc_seq_send(lport, sp, fp);
|
|
#else
|
|
lport->tt.seq_send(lport, sp, fp);
|
|
#endif
|
|
else
|
|
lport->tt.frame_send(lport, fp);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Send error or task management response.
|
|
* Always frees the cmd and associated state.
|
|
*/
|
|
static void ft_send_resp_code(struct ft_cmd *fcmd, enum fcp_resp_rsp_codes code)
|
|
{
|
|
ft_send_resp_status(fcmd->req_frame, SAM_STAT_GOOD, code);
|
|
ft_cmd_done(fcmd);
|
|
}
|
|
|
|
void ft_cmd_tm_done(struct scst_mgmt_cmd *mcmd)
|
|
{
|
|
struct ft_cmd *fcmd;
|
|
enum fcp_resp_rsp_codes code;
|
|
|
|
ft_cmd_tm_dump(mcmd, __func__);
|
|
fcmd = scst_mgmt_cmd_get_tgt_priv(mcmd);
|
|
if (!fcmd)
|
|
return;
|
|
|
|
ft_set_cmd_state(fcmd, FT_STATE_MGMT_RSP_SENT);
|
|
|
|
switch (scst_mgmt_cmd_get_status(mcmd)) {
|
|
case SCST_MGMT_STATUS_SUCCESS:
|
|
code = FCP_TMF_CMPL;
|
|
break;
|
|
case SCST_MGMT_STATUS_REJECTED:
|
|
code = FCP_TMF_REJECTED;
|
|
break;
|
|
case SCST_MGMT_STATUS_LUN_NOT_EXIST:
|
|
code = FCP_TMF_INVALID_LUN;
|
|
break;
|
|
case SCST_MGMT_STATUS_TASK_NOT_EXIST:
|
|
case SCST_MGMT_STATUS_FN_NOT_SUPPORTED:
|
|
case SCST_MGMT_STATUS_FAILED:
|
|
default:
|
|
code = FCP_TMF_FAILED;
|
|
break;
|
|
}
|
|
FT_IO_DBG("tm cmd done fn %d code %d\n", mcmd->fn, code);
|
|
ft_send_resp_code(fcmd, code);
|
|
}
|
|
|
|
/*
|
|
* Handle an incoming FCP task management command frame.
|
|
* Note that this may be called directly from the softirq context.
|
|
*/
|
|
static void ft_recv_tm(struct scst_session *scst_sess,
|
|
struct ft_cmd *fcmd, struct fcp_cmnd *fcp)
|
|
{
|
|
struct scst_rx_mgmt_params params;
|
|
int ret;
|
|
|
|
ft_set_cmd_state(fcmd, FT_STATE_MGMT);
|
|
|
|
scst_rx_mgmt_params_init(¶ms);
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) || \
|
|
defined(CONFIG_SUSE_KERNEL) && \
|
|
LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 101)
|
|
params.lun = fcp->fc_lun.scsi_lun;
|
|
#else
|
|
params.lun = fcp->fc_lun;
|
|
#endif
|
|
params.lun_len = sizeof(fcp->fc_lun);
|
|
params.lun_set = 1;
|
|
params.atomic = SCST_ATOMIC;
|
|
params.tgt_priv = fcmd;
|
|
|
|
switch (fcp->fc_tm_flags) {
|
|
case FCP_TMF_LUN_RESET:
|
|
params.fn = SCST_LUN_RESET;
|
|
break;
|
|
case FCP_TMF_TGT_RESET:
|
|
params.fn = SCST_TARGET_RESET;
|
|
params.lun_set = 0;
|
|
break;
|
|
case FCP_TMF_CLR_TASK_SET:
|
|
params.fn = SCST_CLEAR_TASK_SET;
|
|
break;
|
|
case FCP_TMF_ABT_TASK_SET:
|
|
params.fn = SCST_ABORT_TASK_SET;
|
|
break;
|
|
case FCP_TMF_CLR_ACA:
|
|
params.fn = SCST_CLEAR_ACA;
|
|
break;
|
|
default:
|
|
/*
|
|
* FCP4r01 indicates having a combination of
|
|
* tm_flags set is invalid.
|
|
*/
|
|
FT_IO_DBG("invalid FCP tm_flags %x\n", fcp->fc_tm_flags);
|
|
ft_send_resp_code(fcmd, FCP_CMND_FIELDS_INVALID);
|
|
return;
|
|
}
|
|
FT_IO_DBG("submit tm cmd fn %d\n", params.fn);
|
|
ret = scst_rx_mgmt_fn(scst_sess, ¶ms);
|
|
FT_IO_DBG("scst_rx_mgmt_fn ret %d\n", ret);
|
|
if (ret)
|
|
ft_send_resp_code(fcmd, FCP_TMF_FAILED);
|
|
}
|
|
|
|
/*
|
|
* Handle an incoming FCP command frame.
|
|
* Note that this may be called directly from the softirq context.
|
|
*/
|
|
static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp)
|
|
{
|
|
struct fc_seq *sp;
|
|
struct scst_cmd *cmd;
|
|
struct ft_cmd *fcmd = NULL;
|
|
struct fcp_cmnd *fcp;
|
|
struct fc_lport *lport;
|
|
int data_dir;
|
|
u32 data_len;
|
|
int cdb_len;
|
|
|
|
lport = sess->tport->lport;
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
|
|
sp = fr_seq(fp);
|
|
#else
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
|
sp = fc_seq_assign(lport, fp);
|
|
#else
|
|
sp = lport->tt.seq_assign(lport, fp);
|
|
#endif
|
|
if (!sp)
|
|
goto busy;
|
|
#endif
|
|
|
|
fcmd = kzalloc(sizeof(*fcmd), GFP_ATOMIC);
|
|
if (!fcmd)
|
|
goto busy;
|
|
fcmd->max_payload = sess->max_payload;
|
|
fcmd->max_lso_payload = sess->max_lso_payload;
|
|
fcmd->req_frame = fp;
|
|
spin_lock_init(&fcmd->lock);
|
|
|
|
fcp = fc_frame_payload_get(fp, sizeof(*fcp));
|
|
if (!fcp)
|
|
goto err;
|
|
if (fcp->fc_tm_flags) {
|
|
ft_recv_tm(sess->scst_sess, fcmd, fcp);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* re-check length including specified CDB length.
|
|
* data_len is just after the CDB.
|
|
*/
|
|
cdb_len = fcp->fc_flags & FCP_CFL_LEN_MASK;
|
|
fcp = fc_frame_payload_get(fp, sizeof(*fcp) + cdb_len);
|
|
if (!fcp)
|
|
goto err;
|
|
cdb_len += sizeof(fcp->fc_cdb);
|
|
data_len = ntohl(*(__be32 *)(fcp->fc_cdb + cdb_len));
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) || \
|
|
defined(CONFIG_SUSE_KERNEL) && \
|
|
LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 101)
|
|
cmd = scst_rx_cmd(sess->scst_sess, fcp->fc_lun.scsi_lun,
|
|
sizeof(fcp->fc_lun), fcp->fc_cdb, cdb_len,
|
|
SCST_ATOMIC);
|
|
#else
|
|
cmd = scst_rx_cmd(sess->scst_sess, fcp->fc_lun, sizeof(fcp->fc_lun),
|
|
fcp->fc_cdb, cdb_len, SCST_ATOMIC);
|
|
#endif
|
|
if (!cmd)
|
|
goto busy;
|
|
fcmd->scst_cmd = cmd;
|
|
scst_cmd_set_tgt_priv(cmd, fcmd);
|
|
cmd->state = FT_STATE_NEW;
|
|
|
|
fcmd->seq = sp;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
|
fc_seq_set_resp(sp, ft_recv_seq, cmd);
|
|
#else
|
|
lport->tt.seq_set_resp(sp, ft_recv_seq, cmd);
|
|
#endif
|
|
|
|
switch (fcp->fc_flags & (FCP_CFL_RDDATA | FCP_CFL_WRDATA)) {
|
|
case 0:
|
|
default:
|
|
data_dir = SCST_DATA_NONE;
|
|
break;
|
|
case FCP_CFL_RDDATA:
|
|
data_dir = SCST_DATA_READ;
|
|
break;
|
|
case FCP_CFL_WRDATA:
|
|
data_dir = SCST_DATA_WRITE;
|
|
break;
|
|
case FCP_CFL_RDDATA | FCP_CFL_WRDATA:
|
|
data_dir = SCST_DATA_BIDI;
|
|
break;
|
|
}
|
|
scst_cmd_set_expected(cmd, data_dir, data_len);
|
|
|
|
switch (fcp->fc_pri_ta & FCP_PTA_MASK) {
|
|
case FCP_PTA_SIMPLE:
|
|
scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_SIMPLE);
|
|
break;
|
|
case FCP_PTA_HEADQ:
|
|
scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_HEAD_OF_QUEUE);
|
|
break;
|
|
case FCP_PTA_ACA:
|
|
scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_ACA);
|
|
break;
|
|
case FCP_PTA_ORDERED:
|
|
default:
|
|
scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_ORDERED);
|
|
break;
|
|
}
|
|
|
|
scst_cmd_set_tag(cmd, fc_seq_exch(sp)->rxid);
|
|
scst_cmd_init_done(cmd, SCST_CONTEXT_THREAD);
|
|
return;
|
|
|
|
err:
|
|
ft_send_resp_code(fcmd, FCP_CMND_FIELDS_INVALID);
|
|
return;
|
|
|
|
busy:
|
|
FT_IO_DBG("cmd allocation failure - sending BUSY\n");
|
|
ft_send_resp_status(fp, SAM_STAT_BUSY, 0);
|
|
if (fcmd)
|
|
ft_cmd_done(fcmd);
|
|
else if (sp)
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
|
fc_exch_done(sp);
|
|
#else
|
|
lport->tt.exch_done(sp);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Send FCP ELS-4 Reject.
|
|
*/
|
|
static void ft_cmd_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason,
|
|
enum fc_els_rjt_explan explan)
|
|
{
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
|
|
struct fc_seq *sp = fr_seq(rx_fp);
|
|
struct fc_frame *fp;
|
|
struct fc_els_ls_rjt *rjt;
|
|
struct fc_lport *lport;
|
|
struct fc_exch *ep;
|
|
|
|
ep = fc_seq_exch(sp);
|
|
lport = ep->lp;
|
|
fp = fc_frame_alloc(lport, sizeof(*rjt));
|
|
if (!fp)
|
|
return;
|
|
|
|
rjt = fc_frame_payload_get(fp, sizeof(*rjt));
|
|
memset(rjt, 0, sizeof(*rjt));
|
|
rjt->er_cmd = ELS_LS_RJT;
|
|
rjt->er_reason = reason;
|
|
rjt->er_explan = explan;
|
|
|
|
sp = lport->tt.seq_start_next(sp);
|
|
fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, FC_TYPE_FCP,
|
|
FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_LAST_SEQ, 0);
|
|
lport->tt.seq_send(lport, sp, fp);
|
|
#else
|
|
struct fc_seq_els_data rjt_data;
|
|
struct fc_lport *lport;
|
|
|
|
lport = fr_dev(rx_fp);
|
|
rjt_data.reason = reason;
|
|
rjt_data.explan = explan;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
|
fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
|
|
#else
|
|
lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Handle an incoming FCP ELS-4 command frame.
|
|
* Note that this may be called directly from the softirq context.
|
|
*/
|
|
static void ft_recv_els4(struct ft_sess *sess, struct fc_frame *fp)
|
|
{
|
|
u8 op = fc_frame_payload_op(fp);
|
|
|
|
switch (op) {
|
|
case ELS_SRR: /* TBD */
|
|
default:
|
|
FT_IO_DBG("unsupported ELS-4 op %x\n", op);
|
|
ft_cmd_ls_rjt(fp, ELS_RJT_INVAL, ELS_EXPL_NONE);
|
|
fc_frame_free(fp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle an incoming FCP frame.
|
|
* Note that this may be called directly from the softirq context.
|
|
*/
|
|
void ft_recv_req(struct ft_sess *sess, struct fc_frame *fp)
|
|
{
|
|
struct fc_frame_header *fh = fc_frame_header_get(fp);
|
|
|
|
switch (fh->fh_r_ctl) {
|
|
case FC_RCTL_DD_UNSOL_CMD:
|
|
ft_recv_cmd(sess, fp);
|
|
break;
|
|
case FC_RCTL_ELS4_REQ:
|
|
ft_recv_els4(sess, fp);
|
|
break;
|
|
default:
|
|
pr_info("%s: unhandled frame r_ctl %x\n", __func__,
|
|
fh->fh_r_ctl);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
|
|
sess->tport->lport->tt.exch_done(fr_seq(fp));
|
|
#endif
|
|
fc_frame_free(fp);
|
|
break;
|
|
}
|
|
}
|