From 6a2d41b4802ad58010847f548d5adc7a169c54b7 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 8 May 2014 08:32:01 +0000 Subject: [PATCH] Merged revisions 5456-5485,5487-5508 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk ........ r5456 | bvassche | 2014-04-23 08:15:57 +0300 (Wed, 23 Apr 2014) | 1 line scst/README: Update implicit ALUA section ........ r5457 | bvassche | 2014-04-23 11:57:17 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt: RHEL 5.9 build fix ........ r5458 | bvassche | 2014-04-23 11:59:18 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt: Clean up the CM event handling code ........ r5459 | bvassche | 2014-04-23 12:02:29 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt: Clean up the CM event handling code (part 2) ........ r5460 | bvassche | 2014-04-23 12:04:15 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt, RDMA/CM: Avoid hanging sessions due to a cable pull ........ r5461 | bvassche | 2014-04-23 12:20:52 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt: RHEL 5.9 build fix (part 2) ........ r5462 | bvassche | 2014-04-23 16:38:54 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt: Clean up the CM event handling messages (part 3) ........ r5463 | vlnb | 2014-04-24 03:07:22 +0300 (Thu, 24 Apr 2014) | 3 lines Cleanups ........ r5464 | vlnb | 2014-04-24 05:30:52 +0300 (Thu, 24 Apr 2014) | 35 lines scst_lib: Avoid integer overflows This patch fixes the following kernel oops: [3696]: scst: scst_parse_cmd:826:Warning: expected transfer length 522240 for opcode 0x08 (handler vcdrom, target iscsi) doesn't match decoded value -2048 [3696]: scst_parse_cmd:828:Suspicious CDB: (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F 0: 08 1f ff ff ff 00 ...... BUG: unable to handle kernel paging request at ffff88283597f0c8 IP: [] sg_init_table+0x5f/0x90 Call Trace: [] sgv_pool_alloc+0x3b8/0xbf0 [scst] [] scst_alloc_space+0xb6/0x290 [scst] [] scst_prepare_space+0x3b8/0x6e0 [scst] [] scst_process_active_cmd+0x455/0x7e0 [scst] [] scst_cmd_init_done+0x2f2/0x5c0 [scst] [] scst_cmd_init_stage1_done.constprop.37+0x12/0x20 [iscsi_scst] [] scsi_cmnd_start+0x25a/0x550 [iscsi_scst] [] cmnd_rx_start+0x148/0x1a0 [iscsi_scst] [] process_read_io+0x3b8/0x800 [iscsi_scst] [] scst_do_job_rd+0xc7/0x220 [iscsi_scst] [] istrd+0x16d/0x2e0 [iscsi_scst] [] kthread+0xed/0x110 [] ret_from_fork+0x7c/0xb0 and causes the following message to be reported instead: [11269]: scst: scst_generic_parse:7402:***WARNING***: bufflen 16777215, data_len 16777215 or out_bufflen 0 too large for device disk12 (block size 2048) scst_generic_parse:CDB: (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F 0: 08 1f ff ff ff 00 ...... Signed-off-by: Bart Van Assche ........ r5465 | vlnb | 2014-04-24 05:33:07 +0300 (Thu, 24 Apr 2014) | 5 lines scst_targ: Clarify a comment Signed-off-by: Bart Van Assche ........ r5466 | vlnb | 2014-04-24 05:39:05 +0300 (Thu, 24 Apr 2014) | 12 lines scst: Avoid that smatch complains about dead code The panic() function that implements BUG() has been declared with attribute noreturn in some RHEL 5 kernel headers and also in the RHEL 6 kernel headers. Smatch warns about dead code if any code follows a function that has been declared with attribute noreturn. Hence add a few preprocessor statements to suppress the smatch warning when building against an upstream kernel or when building against RHEL 6 or later. Signed-off-by: Bart Van Assche ........ r5467 | vlnb | 2014-04-24 06:15:12 +0300 (Thu, 24 Apr 2014) | 3 lines Fix queuing of UA for aborted by PREEMPT AND ABORT, if TAS is 0, + some clarifications ........ r5468 | vlnb | 2014-04-24 07:03:51 +0300 (Thu, 24 Apr 2014) | 6 lines Fix for TARGET RESET race It can happen, when a device added after blocking, so unblocking then will make dev->block_count of the new device negative. ........ r5469 | bvassche | 2014-04-24 11:41:15 +0300 (Thu, 24 Apr 2014) | 1 line scst/README: Update multipathd information in implicit ALUA section ........ r5470 | bvassche | 2014-04-24 13:37:58 +0300 (Thu, 24 Apr 2014) | 1 line ib_srpt: OFED 3.12 build fix ........ r5471 | bvassche | 2014-04-24 13:38:37 +0300 (Thu, 24 Apr 2014) | 1 line ib_srpt: Add support in the Makefile for MLNX OFED and for OFED 3.x ........ r5472 | bvassche | 2014-04-24 14:11:26 +0300 (Thu, 24 Apr 2014) | 1 line ib_srpt: Make srpt_disconnect_ch() close sessions properly that have not yet reached the connected state ........ r5473 | bvassche | 2014-04-24 15:27:00 +0300 (Thu, 24 Apr 2014) | 1 line ib_srpt, Makefile: Introduce the OFED_KERNEL_DIR variable ........ r5474 | bvassche | 2014-04-24 21:43:41 +0300 (Thu, 24 Apr 2014) | 1 line ib_srpt: Unload properly with RDMA/CM disabled ........ r5475 | bvassche | 2014-04-24 21:45:23 +0300 (Thu, 24 Apr 2014) | 1 line scstadmin, regression tests: Follow-up for r5409 ........ r5476 | bvassche | 2014-04-24 21:49:52 +0300 (Thu, 24 Apr 2014) | 16 lines scst: Avoid that reassigning a session triggers a kernel crash This patch fixes the following kernel bug: BUG: unable to handle kernel NULL pointer dereference at 0000000000000064 IP: [] scst_alloc_add_tgt_dev+0x9c/0x540 [scst] Call Trace: [] scst_check_reassign_sessions+0x367/0x3b0 [scst] [] scst_acg_add_acn+0x117/0x1a0 [scst] [] scst_acg_ini_mgmt_store_work_fn+0x152/0x370 [scst] [] sysfs_work_thread_fn+0xa6/0x2f0 [scst] [] kthread+0xd2/0xf0 [] ret_from_fork+0x7c/0xb0 Reported-by: Zhen Xu ........ r5477 | vlnb | 2014-04-25 02:07:58 +0300 (Fri, 25 Apr 2014) | 3 lines Minor logging cleanup ........ r5478 | vlnb | 2014-04-25 02:44:25 +0300 (Fri, 25 Apr 2014) | 3 lines Cleanup ........ r5479 | vlnb | 2014-04-25 05:03:04 +0300 (Fri, 25 Apr 2014) | 3 lines Saved mode pages added ........ r5480 | vlnb | 2014-04-25 05:47:31 +0300 (Fri, 25 Apr 2014) | 3 lines Follow up for the previous commit ........ r5481 | vlnb | 2014-04-26 04:56:36 +0300 (Sat, 26 Apr 2014) | 3 lines Processing of QErr and TMF_ONLY added ........ r5482 | vlnb | 2014-04-26 05:12:17 +0300 (Sat, 26 Apr 2014) | 3 lines Cleanups ........ r5483 | bvassche | 2014-04-26 09:32:32 +0300 (Sat, 26 Apr 2014) | 7 lines scst: Fix recently introduced checkpatch complaints about whitespace Fix two instances of the following checkpatch errors: ERROR: code indent should use tabs where possible ERROR: spaces required around that ':' (ctx:VxW) ........ r5484 | bvassche | 2014-04-27 09:26:29 +0300 (Sun, 27 Apr 2014) | 6 lines scst_const.h: Fix a checkpatch complaint about whitespace Avoid that checkpatch reports the following warning message: WARNING: please, no space before tabs ........ r5485 | bvassche | 2014-04-28 12:29:25 +0300 (Mon, 28 Apr 2014) | 5 lines scst: Export scst_path_put() This patch makes the code that was added via r5479 build against kernel version 2.6.38 and before. ........ r5487 | vlnb | 2014-04-28 23:31:14 +0300 (Mon, 28 Apr 2014) | 3 lines Cleanups ........ r5488 | vlnb | 2014-04-29 00:38:22 +0300 (Tue, 29 Apr 2014) | 3 lines Returned sense cleanups ........ r5489 | vlnb | 2014-04-29 01:30:03 +0300 (Tue, 29 Apr 2014) | 3 lines Let REPORT SUPPORTED OPERATION CODES be handled by dev handler as well ........ r5490 | vlnb | 2014-04-29 03:13:24 +0300 (Tue, 29 Apr 2014) | 6 lines It's wrong to clean reservation on failed RESERVE commands With multiple outstanding commands it can open a race window leading to loose of a valid reservation ........ r5491 | vlnb | 2014-04-29 04:32:13 +0300 (Tue, 29 Apr 2014) | 3 lines SCSI logging improvements ........ r5492 | vlnb | 2014-04-29 04:45:43 +0300 (Tue, 29 Apr 2014) | 3 lines Missed hunk in the previous commit ........ r5493 | vlnb | 2014-04-29 05:09:29 +0300 (Tue, 29 Apr 2014) | 3 lines Minor logging improvement ........ r5494 | bvassche | 2014-04-29 15:35:41 +0300 (Tue, 29 Apr 2014) | 1 line ib_srpt, README: Fix RDMA/CM login instructions ........ r5495 | bvassche | 2014-04-29 15:39:50 +0300 (Tue, 29 Apr 2014) | 4 lines ib_srpt: Make LUN masking work again This patch fixes a regression that was introduced in r5425. ........ r5496 | vlnb | 2014-04-30 04:04:27 +0300 (Wed, 30 Apr 2014) | 3 lines Extended INQUIRY page added ........ r5497 | vlnb | 2014-04-30 04:07:41 +0300 (Wed, 30 Apr 2014) | 3 lines Cleanup ........ r5498 | bvassche | 2014-04-30 08:30:45 +0300 (Wed, 30 Apr 2014) | 1 line ib_srpt: Fix two recently introduced checkpatch complaints about whitespace ........ r5499 | bvassche | 2014-04-30 09:01:17 +0300 (Wed, 30 Apr 2014) | 1 line nightly build: Update kernel versions ........ r5500 | vlnb | 2014-05-02 05:50:12 +0300 (Fri, 02 May 2014) | 3 lines Fix CDROM empty case ........ r5501 | vlnb | 2014-05-02 05:50:34 +0300 (Fri, 02 May 2014) | 3 lines Cleanups ........ r5502 | vlnb | 2014-05-02 05:56:19 +0300 (Fri, 02 May 2014) | 9 lines scst_sysfs: Save the value of the 'preferred' attribute only if it has been set The default value of the 'preferred' attribute is 0 (disabled). Hence it is only necessary that scstadmin saves the value of that attribute if it is not zero. Signed-off-by: Bart Van Assche ........ r5503 | vlnb | 2014-05-07 02:22:32 +0300 (Wed, 07 May 2014) | 3 lines Cleanups and logging improvements ........ r5504 | vlnb | 2014-05-07 05:13:11 +0300 (Wed, 07 May 2014) | 5 lines Fix COMMAND DATA LENGTH in All_commands parameter data of REPORT SUPPORTED OPERATION CODES Reported by Sebastian Herbszt ........ r5505 | bvassche | 2014-05-07 11:38:47 +0300 (Wed, 07 May 2014) | 5 lines scst: Fix the procfs build Move the definition of scst_get_opcode_name() up such that it occurs outside #ifndef CONFIG_SCST_PROC / #endif. See also r5491. ........ r5506 | bvassche | 2014-05-07 11:57:04 +0300 (Wed, 07 May 2014) | 4 lines scst_vdisk: Build fix for kernel versions < 2.6.37 See also r5420 / r5479. ........ r5507 | bvassche | 2014-05-07 16:42:56 +0300 (Wed, 07 May 2014) | 1 line nightly build: Update kernel versions ........ r5508 | vlnb | 2014-05-08 05:28:49 +0300 (Thu, 08 May 2014) | 7 lines Avoid that the code for dumping the PR state triggers a race condition Callers of scst_pr_dump_prs() must hold dev_pr_mutex. Signed-off-by: Bart Van Assche ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5509 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/iscsi.c | 5 +- nightly/conf/nightly.conf | 6 +- qla2x00t/qla2x00-target/qla2x00t.c | 26 +- scst/README | 184 ++-- scst/README_in-tree | 30 +- scst/include/scst.h | 92 +- scst/include/scst_const.h | 20 +- scst/include/scst_user.h | 2 + scst/src/Makefile | 2 +- scst/src/dev_handlers/Makefile | 1 + scst/src/dev_handlers/scst_disk.c | 5 +- scst/src/dev_handlers/scst_user.c | 58 +- scst/src/dev_handlers/scst_vdisk.c | 764 +++++++++++++-- scst/src/scst_lib.c | 880 ++++++++++++++++-- scst/src/scst_main.c | 4 +- scst/src/scst_pres.c | 59 +- scst/src/scst_priv.h | 7 +- scst/src/scst_proc.c | 7 +- scst/src/scst_sysfs.c | 16 +- scst/src/scst_targ.c | 433 +++++---- .../scst-0.9.10/t/03-targets.t | 16 +- srpt/Makefile | 53 +- srpt/README | 2 +- srpt/src/ib_srpt.c | 206 ++-- srpt/src/ib_srpt.h | 22 +- usr/fileio/fileio.c | 8 +- 26 files changed, 2295 insertions(+), 613 deletions(-) diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index b6e5599e5..bb724fb0b 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -1457,9 +1457,10 @@ static void cmnd_prepare_get_rejected_immed_data(struct iscsi_cmnd *cmnd) TRACE_DBG_FLAG(iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(cmnd), "Skipping (cmnd %p, ITT %x, op %x, cmd op %x, " - "datasize %u, scst_cmd %p, scst state %d)", cmnd, + "datasize %u, scst_cmd %p, scst state %d, status %d)", cmnd, cmnd->pdu.bhs.itt, cmnd_opcode(cmnd), cmnd_hdr(cmnd)->scb[0], - cmnd->pdu.datasize, cmnd->scst_cmd, cmnd->scst_state); + cmnd->pdu.datasize, cmnd->scst_cmd, cmnd->scst_state, + cmnd->scst_cmd ? cmnd->scst_cmd->status : -1); iscsi_extracheck_is_rd_thread(conn); diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index f01180f32..ac7cd2ecc 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,17 +3,17 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.14.1 \ +3.14.3 \ 3.13.10 \ 3.12.17-nc \ 3.11.10-nc \ -3.10.37-nc \ +3.10.39-nc \ 3.9.11-nc \ 3.8.14-nc \ 3.7.10-nc \ 3.6.11-nc \ 3.5.7-nc \ -3.4.87-nc \ +3.4.89-nc \ 3.3.8-nc \ 3.2.57-nc \ 3.1.10-nc \ diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index 720daea09..346d6df25 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -2623,16 +2623,16 @@ static int q2t_pre_xmit_response(struct q2t_cmd *cmd, if (unlikely(scst_get_resid(scst_cmd, &prm->residual, NULL))) { if (prm->residual > 0) { TRACE_DBG("Residual underflow: %d (tag %lld, " - "op %x, bufflen %d, rq_result %x)", + "op %s, bufflen %d, rq_result %x)", prm->residual, scst_cmd->tag, - scst_cmd->cdb[0], cmd->bufflen, + scst_get_opcode_name(scst_cmd), cmd->bufflen, prm->rq_result); prm->rq_result |= SS_RESIDUAL_UNDER; } else if (prm->residual < 0) { TRACE_DBG("Residual overflow: %d (tag %lld, " - "op %x, bufflen %d, rq_result %x)", + "op %s, bufflen %d, rq_result %x)", prm->residual, scst_cmd->tag, - scst_cmd->cdb[0], cmd->bufflen, + scst_get_opcode_name(scst_cmd), cmd->bufflen, prm->rq_result); prm->rq_result |= SS_RESIDUAL_OVER; prm->residual = -prm->residual; @@ -3591,18 +3591,18 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, TRACE(TRACE_MINOR_AND_MGMT_DBG, "qla2x00t(%ld): CTIO with " "status %#x received, state %x, scst_cmd %p, " - "op %x (LIP_RESET=e, ABORTED=2, TARGET_RESET=17, " + "op %s (LIP_RESET=e, ABORTED=2, TARGET_RESET=17, " "TIMEOUT=b, INVALID_RX_ID=8)", ha->instance, - status, cmd->state, scst_cmd, scst_cmd->cdb[0]); + status, cmd->state, scst_cmd, scst_get_opcode_name(scst_cmd)); break; case CTIO_PORT_LOGGED_OUT: case CTIO_PORT_UNAVAILABLE: PRINT_INFO("qla2x00t(%ld): CTIO with PORT LOGGED " "OUT (29) or PORT UNAVAILABLE (28) status %x " - "received (state %x, scst_cmd %p, op %x)", + "received (state %x, scst_cmd %p, op %s)", ha->instance, status, cmd->state, scst_cmd, - scst_cmd->cdb[0]); + scst_get_opcode_name(scst_cmd)); break; case CTIO_SRR_RECEIVED: @@ -3613,9 +3613,9 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, default: PRINT_ERROR("qla2x00t(%ld): CTIO with error status " - "0x%x received (state %x, scst_cmd %p, op %x)", + "0x%x received (state %x, scst_cmd %p, op %s)", ha->instance, status, cmd->state, scst_cmd, - scst_cmd->cdb[0]); + scst_get_opcode_name(scst_cmd)); break; } @@ -4652,10 +4652,10 @@ restart: cmd->sg = scst_cmd_get_sg(&cmd->scst_cmd); cmd->sg_cnt = scst_cmd_get_sg_cnt(&cmd->scst_cmd); - TRACE_MGMT_DBG("SRR cmd %p (scst_cmd %p, tag %d, op %x), " + TRACE_MGMT_DBG("SRR cmd %p (scst_cmd %p, tag %d, op %s), " "sg_cnt=%d, offset=%d", cmd, &cmd->scst_cmd, - cmd->tag, cmd->scst_cmd.cdb[0], cmd->sg_cnt, - cmd->offset); + cmd->tag, scst_get_opcode_name(&cmd->scst_cmd), + cmd->sg_cnt, cmd->offset); if (IS_FWI2_CAPABLE(ha)) q24_handle_srr(ha, sctio, imm); diff --git a/scst/README b/scst/README index 2306f1371..ba4c4f5d5 100644 --- a/scst/README +++ b/scst/README @@ -949,6 +949,11 @@ cache. The following parameters possible for vdisk_fileio: initiators can unmap blocks of storage, if they don't need them anymore. Backend storage also must support this facility. + - tst - allows to specify TST control mode page field. It specifies + the type of task set in the device. Possible values are: 0 - the + device maintains one task set for all I_T nexuses and 1 - the device + maintains separate task sets for each I_T nexus. Default - 1. + - removable - with this flag set the device is reported to remote initiators as removable. @@ -965,16 +970,17 @@ storage HBAs and for applications that either do not need caching between application and disk or need the large block throughput. See below for more info. -The following parameters possible for vdisk_blockio: filename, -blocksize, nv_cache, read_only, removable, rotational, thin_provisioned. -See vdisk_fileio above for description of those parameters. +The following common with vdisk_fileio parameters are possible for +vdisk_blockio: filename, blocksize, nv_cache, write_through, read_only, +removable, rotational, thin_provisioned, tst. See vdisk_fileio above for +description of those parameters. Handler vdisk_nullio provides NULLIO mode to create virtual devices. In this mode no real I/O is done, but success returned to initiators. Intended to be used for performance measurements at the same way as "*_perf" handlers. The following parameters possible for vdisk_nullio: -blocksize, read_only, removable. See vdisk_fileio above for description -of those parameters. +blocksize, read_only, removable, tst. See vdisk_fileio above for +description of those parameters. vdisk_nullio also has extra attribute: @@ -986,7 +992,7 @@ vdisk_nullio also has extra attribute: "dummy" placeholder on LUN 0, if LUN 0 is not desired. Handler vcdrom allows emulation of a virtual CDROM device using an ISO -file as backend. It doesn't have any parameters. +file as backend. It has only single parameter: tst. For example: @@ -1025,6 +1031,9 @@ Each vdisk_fileio's device has the following attributes in SCST device belongs to (in SCSI terminology all SCST devices called Logical Units). See SPC for more info. + - tst - contains TST field of SCSI Control mode page. See SPC-4 for + more details about this field. + - thin_provisioned - contains thin provisioning status of this virtual device. @@ -1087,6 +1096,7 @@ For example: |-- thin_provisioned |-- threads_num |-- threads_pool_type +|-- tst |-- type |-- usn `-- write_through @@ -1094,17 +1104,17 @@ For example: Each vdisk_blockio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, filename, nv_cache, read_only, removable, resync_size, rotational, size_mb, t10_dev_id, -thin_provisioned, threads_num, threads_pool_type, type, usn. See above -description of those parameters. +thin_provisioned, threads_num, threads_pool_type, tst, type, usn. See +above description of those parameters. Each vdisk_nullio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, read_only, removable, size_mb, t10_dev_id, threads_num, threads_pool_type, type, -usn, dummy. See above description of those parameters. +tst, usn, dummy. See above description of those parameters. Each vcdrom's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: filename, size_mb, -t10_dev_id, threads_num, threads_pool_type, type, usn. See above +t10_dev_id, threads_num, threads_pool_type, type, usn, tst. See above description of those parameters. Exception is filename attribute. For vcdrom it is writable. Writing to it allows to virtually insert or change virtual CD media in the virtual CDROM device. For example: @@ -1352,70 +1362,48 @@ The steps involved in configuring ALUA are: As an example, in a H.A. setup with two systems each having one InfiniBand HCA controlled by the ib_srpt driver and where each system exports two LUNs -could be configured as follows: +the following configuration can be used in scst.conf on both systems: -own_tgt_id=1 -other_tgt_id=2 -cd /sys/kernel/scst_tgt/device_groups -echo del dgroup1 >mgmt -echo del dgroup2 >mgmt -echo create dgroup1 >mgmt -echo add disk01 >dgroup1/devices/mgmt -echo create tgroup1 >dgroup1/target_groups/mgmt -echo ${own_tgt_id} >dgroup1/target_groups/tgroup1/group_id -echo add ib_srpt_0 >dgroup1/target_groups/tgroup1/mgmt -echo ${own_tgt_id} >dgroup1/target_groups/tgroup1/ib_srpt_0/rel_tgt_id -if [ ${own_tgt_id} = 1 ]; then - echo 1 >dgroup1/target_groups/tgroup1/preferred -fi -echo create tgroup2 >dgroup1/target_groups/mgmt -echo ${other_tgt_id} >dgroup1/target_groups/tgroup2/group_id -echo add ib_srpt_0-other >dgroup1/target_groups/tgroup2/mgmt -echo ${other_tgt_id} >dgroup1/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id -if [ ${other_tgt_id} = 1 ]; then - echo 1 >dgroup1/target_groups/tgroup2/preferred -fi -echo create dgroup2 >mgmt -echo add disk02 >dgroup2/devices/mgmt -echo create tgroup1 >dgroup2/target_groups/mgmt -echo ${own_tgt_id} >dgroup2/target_groups/tgroup1/group_id -echo add ib_srpt_0 >dgroup2/target_groups/tgroup1/mgmt -echo ${own_tgt_id} >dgroup2/target_groups/tgroup1/ib_srpt_0/rel_tgt_id -if [ ${own_tgt_id} = 2 ]; then - echo 1 >dgroup2/target_groups/tgroup1/preferred -fi -echo create tgroup2 >dgroup2/target_groups/mgmt -echo ${other_tgt_id} >dgroup2/target_groups/tgroup2/group_id -echo add ib_srpt_0-other >dgroup2/target_groups/tgroup2/mgmt -echo ${other_tgt_id} >dgroup2/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id -if [ ${other_tgt_id} = 2 ]; then - echo 1 >dgroup2/target_groups/tgroup2/preferred -fi +DEVICE_GROUP dgroup1 { + DEVICE disk01 -The second system in the same H.A. setup can be configured with the same -commands but with the values of ${own_rel_tgt_id} and ${other_rel_tgt_id} -swapped. + TARGET_GROUP tgroup1 { + group_id 256 + preferred 1 + state active + TARGET fe80:0000:0000:0000:0002:c903:00fa:b7e1 { + rel_tgt_id 1 + } + } + TARGET_GROUP tgroup2 { + group_id 257 + state standby + TARGET fe80:0000:0000:0000:0002:c903:00fa:b7f2 { + rel_tgt_id 2 + } + } +} -The result of the above commands is: +DEVICE_GROUP dgroup2 { + DEVICE disk02 + + TARGET_GROUP tgroup1 { + group_id 256 + state standby + TARGET fe80:0000:0000:0000:0002:c903:00fa:b7e1 { + rel_tgt_id 1 + } + } + TARGET_GROUP tgroup2 { + group_id 257 + preferred 1 + state active + TARGET fe80:0000:0000:0000:0002:c903:00fa:b7f2 { + rel_tgt_id 2 + } + } +} -$ find -type f | grep -v '/mgmt$' | cut -c3- | sort | \ - while read f; do echo $f = $(head -n 1 $f); done -dgroup1/target_groups/tgroup1/group_id = 1 -dgroup1/target_groups/tgroup1/ib_srpt_0/rel_tgt_id = 1 -dgroup1/target_groups/tgroup1/preferred = 1 -dgroup1/target_groups/tgroup1/state = active -dgroup1/target_groups/tgroup2/group_id = 2 -dgroup1/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id = 2 -dgroup1/target_groups/tgroup2/preferred = 0 -dgroup1/target_groups/tgroup2/state = active -dgroup2/target_groups/tgroup1/group_id = 1 -dgroup2/target_groups/tgroup1/ib_srpt_0/rel_tgt_id = 1 -dgroup2/target_groups/tgroup1/preferred = 1 -dgroup2/target_groups/tgroup1/state = active -dgroup2/target_groups/tgroup2/group_id = 2 -dgroup2/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id = 2 -dgroup2/target_groups/tgroup2/preferred = 0 -dgroup2/target_groups/tgroup2/state = active Checking the Target Configuration ................................. @@ -1446,7 +1434,7 @@ configured on the target: # sg_rtpg /dev/sdb Report target port groups: - target port group id : 0x1 , Pref=1 + target port group id : 0x100 , Pref=1 target port group asymmetric access state : 0x00 T_SUP : 0, O_SUP : 0, LBD_SUP : 0, U_SUP : 1, S_SUP : 1, AN_SUP : 1, AO_SUP : 1 status code : 0x02 @@ -1454,7 +1442,7 @@ Report target port groups: target port count : 01 Relative target port ids: 0x01 - target port group id : 0x2 , Pref=0 + target port group id : 0x101 , Pref=0 target port group asymmetric access state : 0x00 T_SUP : 0, O_SUP : 0, LBD_SUP : 0, U_SUP : 1, S_SUP : 1, AN_SUP : 1, AO_SUP : 1 status code : 0x02 @@ -1463,6 +1451,24 @@ Report target port groups: Relative target port ids: 0x02 +The relative target port ID and the target port group ID for a certain path +can be queried e.g. as follows: + +# sg_vpd -p di /dev/sdb +Device Identification VPD page: + Addressed logical unit: + designator type: T10 vendor identification, code set: ASCII + vendor id: SCST_FIO + vendor specific: 27cddc71-disk01 + designator type: EUI-64 based, code set: Binary + 0x3237636464633731 + Target port: + designator type: Relative target port, code set: Binary + Relative target port: 0x1 + designator type: Target port group, code set: Binary + Target port group: 0x100 + + Initiator Support ................. @@ -1472,14 +1478,40 @@ modify at least the following in /etc/multipath.conf to enable implicit ALUA: * hardware_handler "1 alua" * prio alua * path_grouping_policy group_by_prio +* path_checker tur -Note: newer versions of multipathd support a parameter called -"detect_prio". It can be more convenient to enable this parameter instead of -setting the parameter "prio" to "alua" for only those LUNs that support ALUA. +Notes: +- Newer versions of multipathd support a parameter called + "detect_prio". It can be more convenient to enable this parameter instead of + setting the parameter "prio" to "alua" for only those LUNs that support ALUA. +- Older versions of multipathd (e.g. RHEL 5 and SLES 10 SP1) need + 'prio_callout "/sbin/mpath_prio_alua /dev/%n"' instead of 'prio alua'. + +# multipath -ll +23237636464633731 dm-3 SCST_FIO,disk01 +size=1.0G features='3 queue_if_no_path pg_init_retries 50' hwhandler='1 alua' wp=rw +|-+- policy='service-time 0' prio=1 status=active +| `- 10:0:0:0 sdd 8:48 active ready running +`-+- policy='service-time 0' prio=130 status=enabled + `- 11:0:0:0 sde 8:64 active ready running +23133326137346538 dm-4 SCST_FIO,disk02 +size=1.0G features='3 queue_if_no_path pg_init_retries 50' hwhandler='1 alua' wp=rw +|-+- policy='service-time 0' prio=130 status=active +| `- 10:0:0:2 sdn 8:208 active ready running +`-+- policy='service-time 0' prio=1 status=enabled + `- 11:0:0:2 sdp 8:240 active ready running + +The following information can be derived from the above output: +* That the hardware handler (hw_handler) has been set to "1 alua". +* That multipathd created two priority groups - one with priority 1 and one + with priority 130. +* That the SRP path with SCSI host number 10 will be used for communication + with LUN "disk01" and that the SRP path with SCSI host number 11 will be used + for communication with LUN "disk02". More information about how to configure the device mapper and the scsi_dh_alua driver can be found in the manual of your Linux distribution ("man -multipath.conf"). +multipath.conf", "man multipath" and "man multipathd"). Windows initiator systems support ALUA from Windows Server 2008 on. For more information about ALUA support in Windows Server, see also: diff --git a/scst/README_in-tree b/scst/README_in-tree index f66ef58e6..739647867 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -807,6 +807,11 @@ cache. The following parameters possible for vdisk_fileio: initiators can unmap blocks of storage, if they don't need them anymore. Backend storage also must support this facility. + - tst - allows to specify TST control mode page field. It specifies + the type of task set in the device. Possible values are: 0 - the + device maintains one task set for all I_T nexuses and 1 - the device + maintains separate task sets for each I_T nexus. Default - 1. + - removable - with this flag set the device is reported to remote initiators as removable. @@ -823,16 +828,17 @@ storage HBAs and for applications that either do not need caching between application and disk or need the large block throughput. See below for more info. -The following parameters possible for vdisk_blockio: filename, -blocksize, nv_cache, read_only, removable, rotational, thin_provisioned. -See vdisk_fileio above for description of those parameters. +The following common with vdisk_fileio parameters are possible for +vdisk_blockio: filename, blocksize, nv_cache, write_through, read_only, +removable, rotational, thin_provisioned, tst. See vdisk_fileio above for +description of those parameters. Handler vdisk_nullio provides NULLIO mode to create virtual devices. In this mode no real I/O is done, but success returned to initiators. Intended to be used for performance measurements at the same way as "*_perf" handlers. The following parameters possible for vdisk_nullio: -blocksize, read_only, removable. See vdisk_fileio above for description -of those parameters. +blocksize, read_only, removable, tst. See vdisk_fileio above for +description of those parameters. vdisk_nullio also has extra attribute: @@ -844,7 +850,7 @@ vdisk_nullio also has extra attribute: "dummy" placeholder on LUN 0, if LUN 0 is not desired. Handler vcdrom allows emulation of a virtual CDROM device using an ISO -file as backend. It doesn't have any parameters. +file as backend. It has only single parameter: tst. For example: @@ -883,6 +889,9 @@ Each vdisk_fileio's device has the following attributes in SCST device belongs to (in SCSI terminology all SCST devices called Logical Units). See SPC for more info. + - tst - contains TST field of SCSI Control mode page. See SPC-4 for + more details about this field. + - thin_provisioned - contains thin provisioning status of this virtual device. @@ -939,6 +948,7 @@ For example: |-- thin_provisioned |-- threads_num |-- threads_pool_type +|-- tst |-- type |-- usn `-- write_through @@ -946,17 +956,17 @@ For example: Each vdisk_blockio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, filename, nv_cache, read_only, removable, resync_size, rotational, size_mb, t10_dev_id, -thin_provisioned, threads_num, threads_pool_type, type, usn. See above -description of those parameters. +thin_provisioned, threads_num, threads_pool_type, tst, type, usn. See +above description of those parameters. Each vdisk_nullio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, read_only, removable, size_mb, t10_dev_id, threads_num, threads_pool_type, type, -usn, dummy. See above description of those parameters. +tst, usn, dummy. See above description of those parameters. Each vcdrom's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: filename, size_mb, -t10_dev_id, threads_num, threads_pool_type, type, usn. See above +t10_dev_id, threads_num, threads_pool_type, type, usn, tst. See above description of those parameters. Exception is filename attribute. For vcdrom it is writable. Writing to it allows to virtually insert or change virtual CD media in the virtual CDROM device. For example: diff --git a/scst/include/scst.h b/scst/include/scst.h index 518e434a5..53142c430 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -225,6 +225,8 @@ static inline bool list_entry_in_list(const struct list_head *entry) ** !! as well! *************************************************************/ enum { + /** Active states **/ + /* Dev handler's parse() is going to be called */ SCST_CMD_STATE_PARSE = 0, @@ -258,6 +260,12 @@ enum { /* Checks before target driver's xmit_response() is called */ SCST_CMD_STATE_PRE_XMIT_RESP, + /* Checks 1 before target driver's xmit_response() is called */ + SCST_CMD_STATE_PRE_XMIT_RESP1, + + /* Checks 2 before target driver's xmit_response() is called */ + SCST_CMD_STATE_PRE_XMIT_RESP2, + /* Target driver's xmit_response() is going to be called */ SCST_CMD_STATE_XMIT_RESP, @@ -269,6 +277,8 @@ enum { SCST_CMD_STATE_LAST_ACTIVE = (SCST_CMD_STATE_FINISHED_INTERNAL+100), + /** Passive states **/ + /* A cmd is created, but scst_cmd_init_done() not called */ SCST_CMD_STATE_INIT_WAIT, @@ -568,7 +578,11 @@ enum scst_exec_context { /* Set if the cmd is aborted by other initiator */ #define SCST_CMD_ABORTED_OTHER 1 -/* Set if no response should be sent to the target about this cmd */ +/* + * Set if no response should be sent to the target about this cmd. + * Must be set together with SCST_CMD_ABORTED for better processing + * in scst_pre_xmit_response2(). + */ #define SCST_CMD_NO_RESP 2 /* Set if the cmd is dead and can be destroyed at any time */ @@ -2078,8 +2092,8 @@ struct scst_cmd { unsigned long start_time; - /* List entry for tgt_dev's SN related lists */ - struct list_head sn_cmd_list_entry; + /* List entry for tgt_dev's deferred (SN, etc.) lists */ + struct list_head deferred_cmd_list_entry; /* Cmd's serial number, used to execute cmd's in order of arrival */ unsigned int sn; @@ -2243,6 +2257,10 @@ struct scst_cmd { void *cmd_data_descriptors; int cmd_data_descriptors_cnt; +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + char not_parsed_op_name[8]; +#endif + #ifdef CONFIG_SCST_MEASURE_LATENCY uint64_t start, curr_start, parse_time, alloc_buf_time; uint64_t restart_waiting_time, rdy_to_xfer_time; @@ -2311,6 +2329,7 @@ struct scst_mgmt_cmd { unsigned int needs_unblocking:1; unsigned int lun_set:1; /* set, if lun field is valid */ unsigned int cmd_sn_set:1; /* set, if cmd_sn field is valid */ + unsigned int scst_get_called:1; /* set, if scst_get() was called */ /* Set if dev handler's task_mgmt_fn_received was called */ unsigned int task_mgmt_fn_received_called:1; unsigned int mcmd_dropped:1; /* set if mcmd was dropped */ @@ -2403,20 +2422,51 @@ struct scst_device { /************************************************************* ** Dev's control mode page related values. Updates serialized - ** by scst_block_dev(). Modified independently to the above - ** fields, hence the alignment. + ** by device blocking. Since device blocking protects only + ** commands on the execution stage, in all other read cases + ** use ACCESS_ONCE(), if necessary. Modified independently + ** to the above fields, hence the alignment. *************************************************************/ unsigned int queue_alg:4 __aligned(sizeof(long)); unsigned int tst:3; + unsigned int qerr:2; + unsigned int tmf_only:1; unsigned int tas:1; unsigned int swp:1; unsigned int d_sense:1; + /** + ** Saved and default versions of them, which supported. TST is not + ** among them, because it's hard to switch curr_order_data on the + ** fly. To ensure that no commands lost, we need to flush the previous + ** curr_order_data at first and with one being active command (MODE + ** SELECT), we don't have facility for that at the moment. Suspending + ** activities will hang waiting for the active MODE SELECT. ToDo. + **/ + + unsigned int queue_alg_saved:4; + unsigned int queue_alg_default:4; + + unsigned int tmf_only_saved:1; + unsigned int tmf_only_default:1; + + unsigned int qerr_saved:2; + unsigned int qerr_default:2; + + unsigned int tas_saved:1; + unsigned int tas_default:1; + + unsigned int swp_saved:1; + unsigned int swp_default:1; + + unsigned int d_sense_saved:1; + unsigned int d_sense_default:1; + /* * Set if device implements own ordered commands management. If not set - * and queue_alg is SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER, - * expected_sn will be incremented only after commands finished. + * and queue_alg is SCST_QUEUE_ALG_0_RESTRICTED_REORDER, expected_sn + * will be incremented only after commands finished. */ unsigned int has_own_order_mgmt:1; @@ -2884,7 +2934,6 @@ struct scst_tg_tgt { uint16_t rel_tgt_id; }; - /* * Used to store per-session UNIT ATTENTIONs */ @@ -4260,7 +4309,7 @@ static inline int scst_check_local_events(struct scst_cmd *cmd) return __scst_check_local_events(cmd, true); } -int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd); +int scst_get_cmd_abnormal_done_state(struct scst_cmd *cmd); void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd); struct scst_trace_log { @@ -4559,6 +4608,15 @@ static inline void put_unaligned_be24(const uint32_t v, uint8_t *const p) p[2] = v >> 0; } +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) +const char *scst_get_opcode_name(struct scst_cmd *cmd); +#else +static inline const char *scst_get_opcode_name(struct scst_cmd *cmd) +{ + return cmd->op_name; +} +#endif + #ifndef CONFIG_SCST_PROC /* @@ -4593,6 +4651,7 @@ int scst_wait_info_completion(struct scst_sysfs_user_info *info, unsigned int scst_get_setup_id(void); + /* * Needed to avoid potential circular locking dependency between scst_mutex * and internal sysfs locking (s_active). It could be since most sysfs entries @@ -4694,4 +4753,19 @@ void scst_write_same(struct scst_cmd *cmd); __be64 scst_pack_lun(const uint64_t lun, enum scst_lun_addr_method addr_method); uint64_t scst_unpack_lun(const uint8_t *lun, int len); +int scst_save_global_mode_pages(const struct scst_device *dev, + uint8_t *buf, int size); +int scst_restore_global_mode_pages(struct scst_device *dev, char *params, + char **last_param); + +int scst_read_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, uint8_t *buf, int size); +int scst_write_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, const uint8_t *buf, int size); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) +void scst_path_put(struct nameidata *nd); +#endif +int scst_remove_file(const char *name); + #endif /* __SCST_H */ diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index e4ebb6944..de3dd38be 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -495,20 +495,28 @@ enum { /************************************************************* ** Values for the control mode page TST field *************************************************************/ -#define SCST_CONTR_MODE_ONE_TASK_SET 0 -#define SCST_CONTR_MODE_SEP_TASK_SETS 1 +#define SCST_TST_0_SINGLE_TASK_SET 0 +#define SCST_TST_1_SEP_TASK_SETS 1 /******************************************************************* ** Values for the control mode page QUEUE ALGORITHM MODIFIER field *******************************************************************/ -#define SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER 0 -#define SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER 1 +#define SCST_QUEUE_ALG_0_RESTRICTED_REORDER 0 +#define SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER 1 /************************************************************* ** Values for the control mode page D_SENSE field *************************************************************/ -#define SCST_CONTR_MODE_FIXED_SENSE 0 -#define SCST_CONTR_MODE_DESCR_SENSE 1 +#define SCST_D_SENSE_0_FIXED_SENSE 0 +#define SCST_D_SENSE_1_DESCR_SENSE 1 + +/************************************************************* + ** Values for the control mode page QErr field + *************************************************************/ +#define SCST_QERR_0_ALL_RESUME 0 +#define SCST_QERR_1_ABORT_ALL 1 +#define SCST_QERR_2_RESERVED 2 +#define SCST_QERR_3_ABORT_THIS_NEXUS_ONLY 3 /************************************************************* ** TransportID protocol identifiers diff --git a/scst/include/scst_user.h b/scst/include/scst_user.h index 0e1306de5..1cda6c214 100644 --- a/scst/include/scst_user.h +++ b/scst/include/scst_user.h @@ -88,7 +88,9 @@ struct scst_user_opt { /* SCSI control mode page parameters, see SPC */ uint8_t tst; + uint8_t tmf_only; uint8_t queue_alg; + uint8_t qerr; uint8_t tas; uint8_t swp; uint8_t d_sense; diff --git a/scst/src/Makefile b/scst/src/Makefile index 07b179724..32575c7ef 100644 --- a/scst/src/Makefile +++ b/scst/src/Makefile @@ -116,7 +116,7 @@ endif @echo "*!! target drivers, custom dev handlers and necessary user !!*" @echo "*!! space applications. Otherwise, because of the versions !!*" @echo "*!! mismatch, you could have many problems and crashes. !!*" - @echo "*!! See IMPORTANT note in the \"Installation\" section of !!*" + @echo "*!! See IMPORTANT note in the \"Installation\" section of !!*" @echo "*!! SCST's README file for more info. !!*" @echo "*!! !!*" @echo "*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*" diff --git a/scst/src/dev_handlers/Makefile b/scst/src/dev_handlers/Makefile index 3ae5471d6..464ff06db 100644 --- a/scst/src/dev_handlers/Makefile +++ b/scst/src/dev_handlers/Makefile @@ -71,6 +71,7 @@ all: $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) install: all + mkdir -p $(DESTDIR)/var/lib/scst/vdev_mode_pages $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) \ modules_install diff --git a/scst/src/dev_handlers/scst_disk.c b/scst/src/dev_handlers/scst_disk.c index 4e432f6f1..684aca4ff 100644 --- a/scst/src/dev_handlers/scst_disk.c +++ b/scst/src/dev_handlers/scst_disk.c @@ -406,8 +406,9 @@ static int disk_exec(struct scst_cmd *cmd) if (unlikely((cmd->bufflen >> block_shift) > max_sectors)) { if ((cmd->out_bufflen >> block_shift) > max_sectors) { PRINT_ERROR("Too limited max_sectors %d for " - "bidirectional cmd %x (out_bufflen %d)", - max_sectors, cmd->cdb[0], cmd->out_bufflen); + "bidirectional cmd %p (op %s, out_bufflen %d)", + max_sectors, cmd, scst_get_opcode_name(cmd), + cmd->out_bufflen); /* Let lower level handle it */ res = SCST_EXEC_NOT_COMPLETED; goto out; diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index 962a496b7..e2cf60d26 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -62,7 +62,9 @@ struct scst_user_dev { unsigned int blocking:1; unsigned int cleanup_done:1; unsigned int tst:3; + unsigned int tmf_only:1; unsigned int queue_alg:4; + unsigned int qerr:2; unsigned int tas:1; unsigned int swp:1; unsigned int d_sense:1; @@ -1368,8 +1370,8 @@ out_process: return res; out_inval: - PRINT_ERROR("Invalid parse_reply parameters (LUN %lld, op %x, cmd %p)", - (long long unsigned int)cmd->lun, cmd->cdb[0], cmd); + PRINT_ERROR("Invalid parse_reply parameters (LUN %lld, op %s, cmd %p)", + (long long unsigned int)cmd->lun, scst_get_opcode_name(cmd), cmd); PRINT_BUFFER("Invalid parse_reply", reply, sizeof(*reply)); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); res = -EINVAL; @@ -1554,8 +1556,8 @@ out: return res; out_inval: - PRINT_ERROR("Invalid exec_reply parameters (LUN %lld, op %x, cmd %p)", - (long long unsigned int)cmd->lun, cmd->cdb[0], cmd); + PRINT_ERROR("Invalid exec_reply parameters (LUN %lld, op %s, cmd %p)", + (long long unsigned int)cmd->lun, scst_get_opcode_name(cmd), cmd); PRINT_BUFFER("Invalid exec_reply", reply, sizeof(*reply)); out_hwerr: @@ -2618,10 +2620,22 @@ static int dev_user_attach(struct scst_device *sdev) sdev->dh_priv = dev; sdev->tst = dev->tst; + sdev->tmf_only = dev->tmf_only; + sdev->tmf_only_saved = dev->tmf_only; + sdev->tmf_only_default = dev->tmf_only; sdev->queue_alg = dev->queue_alg; + sdev->qerr = dev->qerr; + sdev->qerr_saved = dev->qerr; + sdev->qerr_default = dev->qerr; sdev->swp = dev->swp; + sdev->swp_saved = dev->swp; + sdev->swp_default = dev->swp; sdev->tas = dev->tas; + sdev->tas_saved = dev->tas; + sdev->tas_default = dev->tas; sdev->d_sense = dev->d_sense; + sdev->d_sense_saved = dev->d_sense; + sdev->d_sense_default = dev->d_sense; sdev->has_own_order_mgmt = dev->has_own_order_mgmt; dev->sdev = sdev; @@ -3350,20 +3364,34 @@ static int __dev_user_set_opt(struct scst_user_dev *dev, goto out; } - if (((opt->tst != SCST_CONTR_MODE_ONE_TASK_SET) && - (opt->tst != SCST_CONTR_MODE_SEP_TASK_SETS)) || - ((opt->queue_alg != SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER) && - (opt->queue_alg != SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER)) || + if (((opt->tst != SCST_TST_0_SINGLE_TASK_SET) && + (opt->tst != SCST_TST_1_SEP_TASK_SETS)) || + (opt->tmf_only > 1) || + ((opt->queue_alg != SCST_QUEUE_ALG_0_RESTRICTED_REORDER) && + (opt->queue_alg != SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER)) || + ((opt->qerr == SCST_QERR_2_RESERVED) || + (opt->qerr > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) || (opt->swp > 1) || (opt->tas > 1) || (opt->has_own_order_mgmt > 1) || (opt->d_sense > 1)) { - PRINT_ERROR("Invalid SCSI option (tst %x, queue_alg %x, swp %x," - " tas %x, d_sense %d, has_own_order_mgmt %x)", opt->tst, - opt->queue_alg, opt->swp, opt->tas, opt->d_sense, - opt->has_own_order_mgmt); + PRINT_ERROR("Invalid SCSI option (tst %x, tmf_only %x, " + "queue_alg %x, qerr %x, swp %x, tas %x, d_sense %d, " + "has_own_order_mgmt %x)", + opt->tst, opt->tmf_only, opt->queue_alg, opt->qerr, + opt->swp, opt->tas, opt->d_sense, opt->has_own_order_mgmt); res = -EINVAL; goto out; } +#if 1 + if ((dev->tst != opt->tst) && (dev->sdev != NULL) && + !list_empty(&dev->sdev->dev_tgt_dev_list)) { + PRINT_ERROR("On the fly setting of TST not supported. " + "See comment in struct scst_device."); + res = -EINVAL; + goto out; + } +#endif + dev->parse_type = opt->parse_type; dev->on_free_cmd_type = opt->on_free_cmd_type; dev->memory_reuse_type = opt->memory_reuse_type; @@ -3371,14 +3399,18 @@ static int __dev_user_set_opt(struct scst_user_dev *dev, dev->partial_len = opt->partial_len; dev->tst = opt->tst; + dev->tmf_only = opt->tmf_only; dev->queue_alg = opt->queue_alg; + dev->qerr = opt->qerr; dev->swp = opt->swp; dev->tas = opt->tas; dev->d_sense = opt->d_sense; dev->has_own_order_mgmt = opt->has_own_order_mgmt; if (dev->sdev != NULL) { dev->sdev->tst = opt->tst; + dev->sdev->tmf_only = opt->tmf_only; dev->sdev->queue_alg = opt->queue_alg; + dev->sdev->qerr = opt->qerr; dev->sdev->swp = opt->swp; dev->sdev->tas = opt->tas; dev->sdev->d_sense = opt->d_sense; @@ -3449,7 +3481,9 @@ static int dev_user_get_opt(struct file *file, void __user *arg) opt.partial_transfers_type = dev->partial_transfers_type; opt.partial_len = dev->partial_len; opt.tst = dev->tst; + opt.tmf_only = dev->tmf_only; opt.queue_alg = dev->queue_alg; + opt.qerr = dev->qerr; opt.tas = dev->tas; opt.swp = dev->swp; opt.d_sense = dev->d_sense; diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 58ae8a9f9..8ca710993 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -38,6 +38,7 @@ #include #include #include +#include #ifndef INSIDE_KERNEL_TREE #include #endif @@ -114,18 +115,20 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = { #define VDISK_NULLIO_SIZE (5LL*1024*1024*1024*1024/2) -#define DEF_TST SCST_CONTR_MODE_SEP_TASK_SETS +#define DEF_TST SCST_TST_1_SEP_TASK_SETS +#define DEF_TMF_ONLY 0 /* * Since we can't control backstorage device's reordering, we have to always * report unrestricted reordering. */ -#define DEF_QUEUE_ALG_WT SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER -#define DEF_QUEUE_ALG SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER +#define DEF_QUEUE_ALG_WT SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER +#define DEF_QUEUE_ALG SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER + +#define DEF_QERR SCST_QERR_0_ALL_RESUME #define DEF_SWP 0 #define DEF_TAS 0 - -#define DEF_DSENSE SCST_CONTR_MODE_FIXED_SENSE +#define DEF_DSENSE SCST_D_SENSE_0_FIXED_SENSE #ifdef CONFIG_SCST_PROC #define VDISK_PROC_HELP "help" @@ -162,6 +165,8 @@ struct scst_vdisk_dev { unsigned int thin_provisioned_manually_set:1; unsigned int dev_thin_provisioned:1; unsigned int rotational:1; + unsigned int wt_flag_saved:1; + unsigned int tst:3; unsigned int format_active:1; struct file *fd; @@ -217,6 +222,8 @@ struct vdisk_cmd_params { bool use_zero_copy; }; +static bool vdev_saved_mode_pages_enabled = true; + enum compl_status_e { #if defined(SCST_DEBUG) COMPL_STATUS_START_AT = 777, @@ -332,6 +339,8 @@ static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_tp_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_sysfs_tst_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_rotational_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj, @@ -402,6 +411,8 @@ static struct kobj_attribute vdisk_wt_attr = __ATTR(write_through, S_IRUGO, vdisk_sysfs_wt_show, NULL); static struct kobj_attribute vdisk_tp_attr = __ATTR(thin_provisioned, S_IRUGO, vdisk_sysfs_tp_show, NULL); +static struct kobj_attribute vdisk_tst_attr = + __ATTR(tst, S_IRUGO, vdisk_sysfs_tst_show, NULL); static struct kobj_attribute vdisk_rotational_attr = __ATTR(rotational, S_IRUGO, vdisk_sysfs_rotational_show, NULL); static struct kobj_attribute vdisk_nv_cache_attr = @@ -455,6 +466,7 @@ static const struct attribute *vdisk_fileio_attrs[] = { &vdisk_rd_only_attr.attr, &vdisk_wt_attr.attr, &vdisk_tp_attr.attr, + &vdisk_tst_attr.attr, &vdisk_rotational_attr.attr, &vdisk_nv_cache_attr.attr, &vdisk_o_direct_attr.attr, @@ -480,6 +492,7 @@ static const struct attribute *vdisk_blockio_attrs[] = { &vdisk_rd_only_attr.attr, &vdisk_wt_attr.attr, &vdisk_nv_cache_attr.attr, + &vdisk_tst_attr.attr, &vdisk_removable_attr.attr, &vdisk_rotational_attr.attr, &vdisk_filename_attr.attr, @@ -501,6 +514,7 @@ static const struct attribute *vdisk_nullio_attrs[] = { &vdev_size_mb_rw_attr.attr, &vdisk_blocksize_attr.attr, &vdisk_rd_only_attr.attr, + &vdisk_tst_attr.attr, &vdev_dummy_attr.attr, &vdisk_removable_attr.attr, &vdev_t10_vend_id_attr.attr, @@ -519,6 +533,7 @@ static const struct attribute *vcdrom_attrs[] = { &vdev_size_ro_attr.attr, &vdev_size_mb_ro_attr.attr, &vcdrom_filename_attr.attr, + &vdisk_tst_attr.attr, &vdev_t10_vend_id_attr.attr, &vdev_vend_specific_id_attr.attr, &vdev_prod_id_attr.attr, @@ -589,6 +604,7 @@ static struct scst_dev_type vdisk_file_devtype = { "removable, " "rotational, " "thin_provisioned, " + "tst, " "write_through, " "zero_copy", #endif @@ -634,6 +650,7 @@ static struct scst_dev_type vdisk_blk_devtype = { "removable, " "rotational, " "thin_provisioned, " + "tst, " "write_through", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) @@ -675,7 +692,8 @@ static struct scst_dev_type vdisk_null_devtype = { "removable, " "rotational, " "size, " - "size_mb", + "size_mb, " + "tst", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, @@ -711,7 +729,7 @@ static struct scst_dev_type vcdrom_devtype = { .add_device = vcdrom_add_device, .del_device = vcdrom_del_device, .dev_attrs = vcdrom_attrs, - .add_device_parameters = NULL, + .add_device_parameters = "tst", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, @@ -944,6 +962,274 @@ static struct scst_vdisk_dev *vdev_find(const char *name) return res; } +#define VDEV_WT_LABEL "WRITE_THROUGH" +#define VDEV_MODE_PAGES_BUF_SIZE (64*1024) +#define VDEV_MODE_PAGES_DIR "/var/lib/scst/vdev_mode_pages" + +static int __vdev_save_mode_pages(const struct scst_vdisk_dev *virt_dev, + uint8_t *buf, int size) +{ + int res = 0; + + TRACE_ENTRY(); + + if (virt_dev->wt_flag != DEF_WRITE_THROUGH) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + VDEV_WT_LABEL, virt_dev->wt_flag); + if (res >= size-1) + goto out_overflow; + } + +out: + TRACE_EXIT_RES(res); + return res; + +out_overflow: + PRINT_ERROR("Mode pages buffer overflow (size %d)", size); + res = -EOVERFLOW; + goto out; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) && !defined(RHEL_MAJOR) +/* + * See also patch "mm: add vzalloc() and vzalloc_node() helpers" (commit + * e1ca7788dec6773b1a2bce51b7141948f2b8bccf). + */ +static void *vzalloc(unsigned long size) +{ + return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, + PAGE_KERNEL); +} +#endif + +static int vdev_save_mode_pages(const struct scst_vdisk_dev *virt_dev) +{ + int res, rc, offs; + uint8_t *buf; + int size; + char *name, *name1; + + TRACE_ENTRY(); + + size = VDEV_MODE_PAGES_BUF_SIZE; + + buf = vzalloc(size); + if (buf == NULL) { + PRINT_ERROR("Unable to alloc mode pages buffer (size %d)", size); + res = -ENOMEM; + goto out; + } + + name = kasprintf(GFP_KERNEL, "%s/%s", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name == NULL) { + PRINT_ERROR("Unable to create name %s/%s", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_vfree; + } + + name1 = kasprintf(GFP_KERNEL, "%s/%s1", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name1 == NULL) { + PRINT_ERROR("Unable to create name %s/%s1", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_free_name; + } + + offs = scst_save_global_mode_pages(virt_dev->dev, buf, size); + if (offs < 0) { + res = offs; + goto out_free_name1; + } + + rc = __vdev_save_mode_pages(virt_dev, &buf[offs], size - offs); + if (rc < 0) { + res = rc; + goto out_free_name1; + } + + offs += rc; + if (offs == 0) { + res = 0; + scst_remove_file(name); + scst_remove_file(name1); + goto out_free_name1; + } + + res = scst_write_file_transactional(name, name1, + virt_dev->name, strlen(virt_dev->name), buf, offs); + +out_free_name1: + kfree(name1); + +out_free_name: + kfree(name); + +out_vfree: + vfree(buf); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int vdev_restore_wt(struct scst_vdisk_dev *virt_dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, VDEV_WT_LABEL, virt_dev->name); + res = -EINVAL; + goto out; + } + + virt_dev->wt_flag = val; + virt_dev->wt_flag_saved = val; + + PRINT_INFO("WT_FLAG restored to %d for vdev %s", virt_dev->wt_flag, + virt_dev->name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* Params are NULL-terminated */ +static int __vdev_load_mode_pages(struct scst_vdisk_dev *virt_dev, char *params) +{ + int res; + char *param, *p, *pp; + unsigned long val; + + TRACE_ENTRY(); + + while (1) { + param = scst_get_next_token_str(¶ms); + if (param == NULL) + break; + + p = scst_get_next_lexem(¶m); + if (*p == '\0') + break; + + pp = scst_get_next_lexem(¶m); + if (*pp == '\0') + goto out_need_param; + + if (scst_get_next_lexem(¶m)[0] != '\0') + goto out_too_many; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + res = kstrtoul(pp, 0, &val); +#else + res = strict_strtoul(pp, 0, &val); +#endif + if (res != 0) + goto out_strtoul_failed; + + if (strcasecmp(VDEV_WT_LABEL, p) == 0) + res = vdev_restore_wt(virt_dev, val); + else { + TRACE_DBG("Unknown parameter %s", p); + res = -EINVAL; + break; + } + if (res != 0) + goto out; + } + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; + +out_strtoul_failed: + PRINT_ERROR("strtoul() for %s failed: %d (device %s)", pp, res, + virt_dev->name); + goto out; + +out_need_param: + PRINT_ERROR("Parameter %s value missed for device %s", p, virt_dev->name); + res = -EINVAL; + goto out; + +out_too_many: + PRINT_ERROR("Too many parameter's %s values (device %s)", p, virt_dev->name); + res = -EINVAL; + goto out; +} + +static int vdev_load_mode_pages(struct scst_vdisk_dev *virt_dev) +{ + int res; + struct scst_device *dev = virt_dev->dev; + uint8_t *buf; + int size; + char *name, *name1, *params; + + TRACE_ENTRY(); + + size = VDEV_MODE_PAGES_BUF_SIZE; + + buf = vzalloc(size); + if (buf == NULL) { + PRINT_ERROR("Unable to alloc mode pages buffer (size %d)", size); + res = -ENOMEM; + goto out; + } + + name = kasprintf(GFP_KERNEL, "%s/%s", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name == NULL) { + PRINT_ERROR("Unable to create name %s/%s", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_vfree; + } + + name1 = kasprintf(GFP_KERNEL, "%s/%s1", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name1 == NULL) { + PRINT_ERROR("Unable to create name %s/%s1", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_free_name; + } + + size = scst_read_file_transactional(name, name1, + virt_dev->name, strlen(virt_dev->name), buf, size-1); + if (size <= 0) { + res = size; + goto out_free_name1; + } + + buf[size-1] = '\0'; + + res = scst_restore_global_mode_pages(dev, &buf[strlen(virt_dev->name)+1], + ¶ms); + if ((res != 0) || (params == NULL)) + goto out_free_name1; + + res = __vdev_load_mode_pages(virt_dev, params); + +out_free_name1: + kfree(name1); + +out_free_name: + kfree(name); + +out_vfree: + vfree(buf); + +out: + TRACE_EXIT_RES(res); + return res; +} + static int vdisk_attach(struct scst_device *dev) { int res = 0; @@ -1021,14 +1307,31 @@ static int vdisk_attach(struct scst_device *dev) dev->dh_priv = virt_dev; - dev->tst = DEF_TST; + dev->tst = virt_dev->tst; + dev->tmf_only = DEF_TMF_ONLY; + dev->tmf_only_saved = DEF_TMF_ONLY; + dev->tmf_only_default = DEF_TMF_ONLY; dev->d_sense = DEF_DSENSE; + dev->d_sense_saved = DEF_DSENSE; + dev->d_sense_default = DEF_DSENSE; if (virt_dev->wt_flag && !virt_dev->nv_cache) dev->queue_alg = DEF_QUEUE_ALG_WT; else dev->queue_alg = DEF_QUEUE_ALG; + dev->queue_alg_saved = dev->queue_alg; + dev->queue_alg_default = dev->queue_alg; + dev->qerr = DEF_QERR; + dev->qerr_saved = DEF_QERR; + dev->qerr_default = DEF_QERR; dev->swp = DEF_SWP; + dev->swp_saved = DEF_SWP; + dev->swp_default = DEF_SWP; dev->tas = DEF_TAS; + dev->tas_saved = DEF_TAS; + dev->tas_default = DEF_TAS; + + if (vdev_saved_mode_pages_enabled) + vdev_load_mode_pages(virt_dev); out: TRACE_EXIT(); @@ -1258,7 +1561,7 @@ static enum compl_status_e vdisk_exec_format_unit(struct vdisk_cmd_params *p) PRINT_ERROR("FORMAT UNIT: too small parameters list " "header %d (dev %s)", length, dev->virt_name); scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + SCST_LOAD_SENSE(scst_sense_parameter_list_length_invalid)); goto out_put; } @@ -1278,8 +1581,8 @@ static enum compl_status_e vdisk_exec_format_unit(struct vdisk_cmd_params *p) PRINT_ERROR("FORMAT UNIT: too small long " "parameters list header %d (dev %s)", length, dev->virt_name); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_cdb(cmd, 1, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 5); goto out_put; } if ((buf[3] & 0xF0) != 0) { @@ -1404,7 +1707,7 @@ out_put: static enum compl_status_e vdisk_invalid_opcode(struct vdisk_cmd_params *p) { - TRACE_DBG("Invalid opcode %d", p->cmd->cdb[0]); + TRACE_DBG("Invalid opcode %s", scst_get_opcode_name(p->cmd)); return INVALID_OPCODE; } @@ -1883,9 +2186,8 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd) TRACE_ENTRY(); if (unlikely(!(cmd->op_flags & SCST_INFO_VALID))) { - TRACE(TRACE_MINOR, "Unknown opcode 0x%02x", cmd->cdb[0]); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + TRACE(TRACE_MINOR, "Unknown opcode %s", scst_get_opcode_name(cmd)); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); res = false; goto out; } @@ -1899,10 +2201,12 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd) switch (cmd->queue_type) { case SCST_CMD_QUEUE_ORDERED: - TRACE(TRACE_ORDER, "ORDERED cmd %p (op %x)", cmd, cmd->cdb[0]); + TRACE(TRACE_ORDER, "ORDERED cmd %p (op %s)", cmd, + scst_get_opcode_name(cmd)); break; case SCST_CMD_QUEUE_HEAD_OF_QUEUE: - TRACE(TRACE_ORDER, "HQ cmd %p (op %x)", cmd, cmd->cdb[0]); + TRACE(TRACE_ORDER, "HQ cmd %p (op %s)", cmd, + scst_get_opcode_name(cmd)); break; default: break; @@ -1921,14 +2225,18 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd) if (unlikely((loff + data_len) > virt_dev->file_size) && (!(cmd->op_flags & SCST_LBA_NOT_VALID))) { - PRINT_INFO("Access beyond the end of device %s " - "(%lld of %lld, data len %lld)", - virt_dev->name, - (long long unsigned int)loff, - (long long unsigned int)virt_dev->file_size, - (long long unsigned int)data_len); - scst_set_cmd_error(cmd, SCST_LOAD_SENSE( + if (virt_dev->cdrom_empty) { + TRACE_DBG("%s", "CDROM empty"); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_no_medium)); + } else { + PRINT_INFO("Access beyond the end of device %s " + "(%lld of %lld, data len %lld)", virt_dev->name, + (long long unsigned int)loff, + (long long unsigned int)virt_dev->file_size, + (long long unsigned int)data_len); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE( scst_sense_block_out_range_error)); + } res = false; goto out; } @@ -2479,7 +2787,7 @@ out_thr: return res; out_invalid_opcode: - TRACE_DBG("Invalid opcode 0x%x", opcode); + TRACE_DBG("Invalid opcode %s", scst_get_opcode_name(cmd)); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); goto out_compl; } @@ -2923,17 +3231,18 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) if (cmd->cdb[1] & EVPD) { if (0 == cmd->cdb[2]) { /* supported vital product data pages */ - buf[3] = 3; + buf[3] = 4; buf[4] = 0x0; /* this page */ buf[5] = 0x80; /* unit serial number */ buf[6] = 0x83; /* device identification */ + buf[7] = 0x86; /* extended inquiry */ if (dev->type == TYPE_DISK) { buf[3] += 2; - buf[7] = 0xB0; /* block limits */ - buf[8] = 0xB1; /* block limits */ + buf[8] = 0xB0; /* block limits */ + buf[9] = 0xB1; /* block device charachteristics */ if (virt_dev->thin_provisioned) { buf[3] += 1; - buf[9] = 0xB2; /* thin provisioning */ + buf[10] = 0xB2; /* thin provisioning */ } } resp_len = buf[3] + 4; @@ -3044,6 +3353,14 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) resp_len = num; put_unaligned_be16(resp_len, &buf[2]); resp_len += 4; + } else if (0x86 == cmd->cdb[2]) { + /* Extended INQUIRY */ + buf[1] = 0x86; + buf[3] = 0x3C; + buf[5] = 7; /* HEADSUP=1, ORDSUP=1, SIMPSUP=1 */ + buf[6] = (virt_dev->wt_flag || virt_dev->nv_cache) ? 0 : 1; /* V_SUP */ + buf[7] = 1; /* LUICLR=1 */ + resp_len = buf[3] + 4; } else if ((0xB0 == cmd->cdb[2]) && (dev->type == TYPE_DISK)) { /* Block Limits */ int max_transfer; @@ -3383,55 +3700,92 @@ static int vdisk_format_pg(unsigned char *p, int pcontrol, static int vdisk_caching_pg(unsigned char *p, int pcontrol, struct scst_vdisk_dev *virt_dev) { /* Caching page for mode_sense */ - const unsigned char caching_pg[] = {0x8, 0x12, 0x0, 0, 0, 0, 0, 0, + unsigned char caching_pg[] = {0x8, 0x12, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x14, 0, 0, 0, 0, 0, 0}; - memcpy(p, caching_pg, sizeof(caching_pg)); - p[2] |= !(virt_dev->wt_flag || virt_dev->nv_cache) ? WCE : 0; - if (1 == pcontrol) + if (!virt_dev->nv_cache && vdev_saved_mode_pages_enabled) + caching_pg[0] |= 0x80; + + switch (pcontrol) { + case 0: /* current */ + memcpy(p, caching_pg, sizeof(caching_pg)); + p[2] |= (virt_dev->wt_flag || virt_dev->nv_cache) ? 0 : WCE; + break; + case 1: /* changeable */ memset(p + 2, 0, sizeof(caching_pg) - 2); + if (!virt_dev->nv_cache) + p[2] |= WCE; + break; + case 2: /* default */ + memcpy(p, caching_pg, sizeof(caching_pg)); + p[2] |= (DEF_WRITE_THROUGH || virt_dev->nv_cache) ? 0 : WCE; + break; + case 3: /* saved */ + memcpy(p, caching_pg, sizeof(caching_pg)); + p[2] |= (virt_dev->wt_flag_saved || virt_dev->nv_cache) ? 0 : WCE; + break; + default: + sBUG_ON(1); + break; + } + return sizeof(caching_pg); } static int vdisk_ctrl_m_pg(unsigned char *p, int pcontrol, struct scst_vdisk_dev *virt_dev) { /* Control mode page for mode_sense */ - const unsigned char ctrl_m_pg[] = {0xa, 0xa, 0, 0, 0, 0, 0, 0, + unsigned char ctrl_m_pg[] = {0xa, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0x2, 0x4b}; + if (vdev_saved_mode_pages_enabled) + ctrl_m_pg[0] |= 0x80; + memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg)); switch (pcontrol) { case 0: /* current */ p[2] |= virt_dev->dev->tst << 5; + p[2] |= virt_dev->dev->tmf_only << 4; p[2] |= virt_dev->dev->d_sense << 2; p[3] |= virt_dev->dev->queue_alg << 4; + p[3] |= virt_dev->dev->qerr << 1; p[4] |= virt_dev->dev->swp << 3; p[5] |= virt_dev->dev->tas << 6; break; case 1: /* changeable */ memset(p + 2, 0, sizeof(ctrl_m_pg) - 2); #if 0 /* - * It's too early to implement it, since we can't control the - * backstorage device parameters. ToDo + * See comment in struct scst_device definition. + * + * If enable it, fix the default and saved cases below! */ p[2] |= 7 << 5; /* TST */ - p[3] |= 0xF << 4; /* QUEUE ALGORITHM MODIFIER */ #endif p[2] |= 1 << 2; /* D_SENSE */ + p[2] |= 1 << 4; /* TMF_ONLY */ + p[3] |= 0xF << 4; /* QUEUE ALGORITHM MODIFIER */ + p[3] |= 3 << 1; /* QErr */ p[4] |= 1 << 3; /* SWP */ p[5] |= 1 << 6; /* TAS */ break; case 2: /* default */ - p[2] |= DEF_TST << 5; - p[2] |= DEF_DSENSE << 2; - if (virt_dev->wt_flag || virt_dev->nv_cache) - p[3] |= DEF_QUEUE_ALG_WT << 4; - else - p[3] |= DEF_QUEUE_ALG << 4; - p[4] |= DEF_SWP << 3; - p[5] |= DEF_TAS << 6; + p[2] |= virt_dev->tst << 5; + p[2] |= virt_dev->dev->d_sense_default << 2; + p[2] |= virt_dev->dev->tmf_only_default << 4; + p[3] |= virt_dev->dev->queue_alg_default << 4; + p[3] |= virt_dev->dev->qerr_default << 1; + p[4] |= virt_dev->dev->swp_default << 3; + p[5] |= virt_dev->dev->tas_default << 6; + break; + case 3: /* saved */ + p[2] |= virt_dev->dev->tst << 5; + p[2] |= virt_dev->dev->d_sense_saved << 2; + p[2] |= virt_dev->dev->tmf_only_default << 4; + p[3] |= virt_dev->dev->queue_alg_saved << 4; + p[3] |= virt_dev->dev->qerr_saved << 1; + p[4] |= virt_dev->dev->swp_saved << 3; + p[5] |= virt_dev->dev->tas_saved << 6; break; - case 3: /* saved, blocked by the caller */ default: sBUG(); } @@ -3491,7 +3845,7 @@ static enum compl_status_e vdisk_exec_mode_sense(struct vdisk_cmd_params *p) if (unlikely(length <= 0)) goto out_free; - if (0x3 == pcontrol) { + if (!vdev_saved_mode_pages_enabled && (0x3 == pcontrol)) { TRACE_DBG("%s", "MODE SENSE: Saving values not supported"); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_saving_params_unsup)); @@ -3648,31 +4002,198 @@ out: } static void vdisk_ctrl_m_pg_select(unsigned char *p, - struct scst_vdisk_dev *virt_dev, struct scst_cmd *cmd) + struct scst_vdisk_dev *virt_dev, struct scst_cmd *cmd, bool save, + int param_offset) { struct scst_device *dev = virt_dev->dev; int old_swp = dev->swp, old_tas = dev->tas, old_dsense = dev->d_sense; + int old_queue_alg = dev->queue_alg; + int rc, old_tmf_only = dev->tmf_only, old_qerr = dev->qerr; + int queue_alg, swp, tas, tmf_only, qerr, d_sense; -#if 0 /* Not implemented yet, see comment in vdisk_ctrl_m_pg() */ - dev->tst = (p[2] >> 5) & 1; - dev->queue_alg = p[3] >> 4; + TRACE_ENTRY(); + + if (save && !vdev_saved_mode_pages_enabled) { + TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: saved control page " + "not supported"); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 1); + goto out; + } + + /* + * MODE SELECT is a strictly serialized cmd, so it is safe to + * perform direct assignment here. + */ + +#if 0 /* Not implemented yet, see comment in struct scst_device */ + dev->tst = (p[2] >> 5) & 7; + /* ToDo: check validity of the new value */ #else - if ((dev->tst != ((p[2] >> 5) & 1)) || (dev->queue_alg != (p[3] >> 4))) { + if (dev->tst != ((p[2] >> 5) & 7)) { TRACE(TRACE_MINOR|TRACE_SCSI, "%s", "MODE SELECT: Changing of " - "TST and QUEUE ALGORITHM not supported"); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); - return; + "TST not supported"); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 5); + goto out; } #endif - dev->swp = (p[4] & 0x8) >> 3; - dev->tas = (p[5] & 0x40) >> 6; - dev->d_sense = (p[2] & 0x4) >> 2; + queue_alg = p[3] >> 4; + if ((queue_alg != SCST_QUEUE_ALG_0_RESTRICTED_REORDER) && + (queue_alg != SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER)) { + PRINT_WARNING("Attempt to set invalid Control mode page QUEUE " + "ALGORITHM MODIFIER value %d (initiator %s, dev %s)", + queue_alg, cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 3, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 4); + goto out; + } + + swp = (p[4] & 0x8) >> 3; + if (swp > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page SWP " + "value %d (initiator %s, dev %s)", swp, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 4, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 3); + goto out; + } + + tas = (p[5] & 0x40) >> 6; + if (tas > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page TAS " + "value %d (initiator %s, dev %s)", tas, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 5, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 6); + goto out; + } + + tmf_only = (p[2] & 0x10) >> 4; + if (tmf_only > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page " + "TMF_ONLY value %d (initiator %s, dev %s)", tmf_only, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 4); + goto out; + } + + qerr = (p[3] & 0x6) >> 1; + if ((qerr == SCST_QERR_2_RESERVED) || + (qerr > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) { + PRINT_WARNING("Attempt to set invalid Control mode page QErr " + "value %d (initiator %s, dev %s)", qerr, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 3, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 1); + goto out; + } + + d_sense = (p[2] & 0x4) >> 2; + if (d_sense > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page D_SENSE " + "value %d (initiator %s, dev %s)", d_sense, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 2); + goto out; + } + + dev->queue_alg = queue_alg; + dev->swp = swp; + dev->tas = tas; + dev->tmf_only = tmf_only; + dev->qerr = qerr; + dev->d_sense = d_sense; + + if ((dev->swp == old_swp) && (dev->tas == old_tas) && + (dev->d_sense == old_dsense) && (dev->queue_alg == old_queue_alg) && + (dev->qerr == old_qerr) && (dev->tmf_only == old_tmf_only)) + goto out; + + if (!save) + goto out_ok; + + rc = vdev_save_mode_pages(virt_dev); + if (rc != 0) { + dev->swp = old_swp; + dev->tas = old_tas; + dev->d_sense = old_dsense; + dev->queue_alg = old_queue_alg; + dev->tmf_only = old_tmf_only; + dev->qerr = old_qerr; + /* Hopefully, the error is temporary */ + scst_set_busy(cmd); + goto out; + } + + dev->swp_saved = dev->swp; + dev->tas_saved = dev->tas; + dev->d_sense_saved = dev->d_sense; + dev->queue_alg_saved = dev->queue_alg; + dev->tmf_only_saved = dev->tmf_only; + dev->qerr_saved = dev->qerr; + +out_ok: PRINT_INFO("Device %s: new control mode page parameters: SWP %x " - "(was %x), TAS %x (was %x), D_SENSE %d (was %d)", + "(was %x), TAS %x (was %x), TMF_ONLY %d (was %x), QErr %x " + "(was %x), D_SENSE %d (was %d), QUEUE ALG %d (was %d)", virt_dev->name, dev->swp, old_swp, dev->tas, old_tas, - dev->d_sense, old_dsense); + dev->tmf_only, old_tmf_only, dev->qerr, old_qerr, + dev->d_sense, old_dsense, dev->queue_alg, old_queue_alg); + +out: + TRACE_EXIT(); + return; +} + +static void vdisk_caching_m_pg_select(unsigned char *p, + struct scst_vdisk_dev *virt_dev, struct scst_cmd *cmd, bool save, + bool read_only) +{ + int old_wt = virt_dev->wt_flag, new_wt, rc; + + TRACE_ENTRY(); + + if (save && (!vdev_saved_mode_pages_enabled || virt_dev->nv_cache)) { + TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: saved cache page " + "not supported"); + scst_set_invalid_field_in_cdb(cmd, 1, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); + goto out; + } + + new_wt = (p[2] & WCE) ? 0 : 1; + + if (new_wt == old_wt) + goto out; + + if (vdisk_set_wt(virt_dev, new_wt, read_only) != 0) { + scst_set_busy(cmd); + goto out; + } + + if (!save) + goto out_ok; + + rc = vdev_save_mode_pages(virt_dev); + if (rc != 0) { + vdisk_set_wt(virt_dev, old_wt, read_only); + /* Hopefully, the error is temporary */ + scst_set_busy(cmd); + goto out; + } + + virt_dev->wt_flag_saved = virt_dev->wt_flag; + +out_ok: + PRINT_INFO("Device %s: new wt_flag: %x (was %x)", virt_dev->name, + virt_dev->wt_flag, old_wt); + +out: + TRACE_EXIT(); return; } @@ -3694,10 +4215,9 @@ static enum compl_status_e vdisk_exec_mode_select(struct vdisk_cmd_params *p) if (unlikely(length <= 0)) goto out; - if (!(cmd->cdb[1] & PF) || (cmd->cdb[1] & SP)) { + if (!(cmd->cdb[1] & PF)) { TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: Unsupported " - "value(s) of PF and/or SP bits (cdb[1]=%x)", - cmd->cdb[1]); + "PF bit zero (cdb[1]=%x)", cmd->cdb[1]); scst_set_invalid_field_in_cdb(cmd, 1, 0); goto out_put; } @@ -3730,11 +4250,8 @@ static enum compl_status_e vdisk_exec_mode_select(struct vdisk_cmd_params *p) scst_set_invalid_field_in_parm_list(cmd, offset+1, 0); goto out_put; } - if (vdisk_set_wt(virt_dev, (address[offset + 2] & WCE) ? 0 : 1, - cmd->tgt_dev->tgt_dev_rd_only) != 0) { - scst_set_busy(cmd); - goto out_put; - } + vdisk_caching_m_pg_select(&address[offset], virt_dev, + cmd, cmd->cdb[1] & SP, cmd->tgt_dev->tgt_dev_rd_only); break; } else if ((address[offset] & 0x3f) == 0xA) { /* Control page */ @@ -3744,7 +4261,8 @@ static enum compl_status_e vdisk_exec_mode_select(struct vdisk_cmd_params *p) scst_set_invalid_field_in_parm_list(cmd, offset+1, 0); goto out_put; } - vdisk_ctrl_m_pg_select(&address[offset], virt_dev, cmd); + vdisk_ctrl_m_pg_select(&address[offset], virt_dev, cmd, + cmd->cdb[1] & SP, offset); } else { TRACE(TRACE_MINOR, "MODE SELECT: Invalid request %x", address[offset] & 0x3f); @@ -3796,8 +4314,7 @@ static enum compl_status_e vdisk_exec_read_capacity(struct vdisk_cmd_params *p) uint32_t lba = get_unaligned_be32(&cmd->cdb[2]); if (lba != 0) { TRACE_DBG("PMI zero and LBA not zero (cmd %p)", cmd); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_cdb(cmd, 2, 0); goto out; } } @@ -3857,8 +4374,7 @@ static enum compl_status_e vdisk_exec_read_capacity16(struct vdisk_cmd_params *p uint32_t lba = get_unaligned_be32(&cmd->cdb[2]); if (lba != 0) { TRACE_DBG("PMI zero and LBA not zero (cmd %p)", cmd); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_cdb(cmd, 2, 0); goto out; } } @@ -5220,15 +5736,14 @@ static void vdisk_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd, struct scst_vdisk_dev *virt_dev = dev->dh_priv; int rc; - dev->tst = DEF_TST; - dev->d_sense = DEF_DSENSE; - dev->swp = DEF_SWP; - dev->tas = DEF_TAS; + dev->tmf_only = dev->tmf_only_saved; + dev->d_sense = dev->d_sense_saved; + dev->swp = dev->swp_saved; + dev->tas = dev->tas_saved; + dev->queue_alg = dev->queue_alg_saved; + dev->qerr = dev->qerr_saved; - if (virt_dev->wt_flag && !virt_dev->nv_cache) - dev->queue_alg = DEF_QUEUE_ALG_WT; - else - dev->queue_alg = DEF_QUEUE_ALG; + dev->tst = virt_dev->tst; rc = vdisk_set_wt(virt_dev, DEF_WRITE_THROUGH, tgt_dev->tgt_dev_rd_only); @@ -5288,6 +5803,10 @@ static void vdisk_report_registering(const struct scst_vdisk_dev *virt_dev) i += snprintf(&buf[i], sizeof(buf) - i, "%sREMOVABLE", (j == i) ? "(" : ", "); + if (virt_dev->tst != DEF_TST) + i += snprintf(&buf[i], sizeof(buf) - i, "%sTST %d", + (j == i) ? "(" : ", ", virt_dev->tst); + if (virt_dev->rotational) i += snprintf(&buf[i], sizeof(buf) - i, "%sROTATIONAL", (j == i) ? "(" : ", "); @@ -5383,6 +5902,7 @@ static int vdev_create(struct scst_dev_type *devt, virt_dev->removable = DEF_REMOVABLE; virt_dev->rotational = DEF_ROTATIONAL; virt_dev->thin_provisioned = DEF_THIN_PROVISIONED; + virt_dev->tst = DEF_TST; virt_dev->blk_shift = DEF_DISK_BLOCK_SHIFT; @@ -5571,6 +6091,15 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, } else if (!strcasecmp("rotational", p)) { virt_dev->rotational = val; TRACE_DBG("ROTATIONAL %d", virt_dev->rotational); + } else if (!strcasecmp("tst", p)) { + if ((val != SCST_TST_0_SINGLE_TASK_SET) && + (val != SCST_TST_1_SEP_TASK_SETS)) { + PRINT_ERROR("Invalid TST value %d", (int)val); + res = -EINVAL; + goto out; + } + virt_dev->tst = val; + TRACE_DBG("TST %d", virt_dev->tst); } else if (!strcasecmp("thin_provisioned", p)) { virt_dev->thin_provisioned = val; virt_dev->thin_provisioned_manually_set = 1; @@ -5676,7 +6205,7 @@ static int vdev_blockio_add_device(const char *device_name, char *params) int res = 0; const char *const allowed_params[] = { "filename", "read_only", "write_through", "removable", "blocksize", "nv_cache", - "rotational", "thin_provisioned", NULL }; + "rotational", "thin_provisioned", "tst", NULL }; struct scst_vdisk_dev *virt_dev; TRACE_ENTRY(); @@ -5734,7 +6263,7 @@ static int vdev_nullio_add_device(const char *device_name, char *params) int res = 0; static const char *const allowed_params[] = { "read_only", "dummy", "removable", "blocksize", "rotational", - "size", "size_mb", NULL + "size", "size_mb", "tst", NULL }; struct scst_vdisk_dev *virt_dev; @@ -5891,7 +6420,7 @@ out: static ssize_t __vcdrom_add_device(const char *device_name, char *params) { int res = 0; - const char *allowed_params[] = { NULL }; /* no params */ + const char *allowed_params[] = { "tst", NULL }; struct scst_vdisk_dev *virt_dev; TRACE_ENTRY(); @@ -6462,6 +6991,27 @@ static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj, return pos; } +static ssize_t vdisk_sysfs_tst_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + pos = sprintf(buf, "%d\n", virt_dev->tst); + + if (virt_dev->tst != DEF_TST) + pos += sprintf(&buf[pos], "%s\n", SCST_SYSFS_KEY_MARK); + + TRACE_EXIT_RES(pos); + return pos; +} + static ssize_t vdisk_sysfs_rotational_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -7848,6 +8398,46 @@ static void init_ops(vdisk_op_fn *ops, int count) return; } +static int __init vdev_check_mode_pages_path(void) +{ + int res; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) + struct nameidata nd; +#else + struct path path; +#endif + mm_segment_t old_fs = get_fs(); + + TRACE_ENTRY(); + + set_fs(KERNEL_DS); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) + res = path_lookup(VDEV_MODE_PAGES_DIR, 0, &nd); + if (res == 0) + scst_path_put(&nd); +#else + res = kern_path(VDEV_MODE_PAGES_DIR, 0, &path); + if (res == 0) + path_put(&path); +#endif + if (res != 0) { + PRINT_WARNING("Unable to find %s (err %d), saved mode pages " + "disabled. You should create this directory manually " + "or reinstall SCST", VDEV_MODE_PAGES_DIR, res); + vdev_saved_mode_pages_enabled = false; + goto out_setfs; + } + +out_setfs: + set_fs(old_fs); + + res = 0; /* always succeed */ + + TRACE_EXIT_RES(res); + return res; +} + static int __init init_scst_vdisk_driver(void) { int res; @@ -7856,6 +8446,10 @@ static int __init init_scst_vdisk_driver(void) init_ops(blockio_ops, ARRAY_SIZE(blockio_ops)); init_ops(nullio_ops, ARRAY_SIZE(nullio_ops)); + res = vdev_check_mode_pages_path(); + if (res != 0) + goto out; + vdisk_cmd_param_cachep = KMEM_CACHE(vdisk_cmd_params, SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN); if (vdisk_cmd_param_cachep == NULL) { diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index b90f82447..cd7f98eac 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -1595,8 +1595,8 @@ int scst_alloc_sense(struct scst_cmd *cmd, int atomic) cmd->sense = mempool_alloc(scst_sense_mempool, gfp_mask); if (cmd->sense == NULL) { - PRINT_CRIT_ERROR("Sense memory allocation failed (op %x). " - "The sense data will be lost!!", cmd->cdb[0]); + PRINT_CRIT_ERROR("Sense memory allocation failed (op %s). " + "The sense data will be lost!!", scst_get_opcode_name(cmd)); res = -ENOMEM; goto out; } @@ -1641,7 +1641,8 @@ int scst_alloc_set_sense(struct scst_cmd *cmd, int atomic, cmd->sense_valid_len = len; if (cmd->sense_buflen < len) { PRINT_WARNING("Sense truncated (needed %d), shall you increase " - "SCST_SENSE_BUFFERSIZE? Op: %x", len, cmd->cdb[0]); + "SCST_SENSE_BUFFERSIZE? Op: %s", len, + scst_get_opcode_name(cmd)); cmd->sense_valid_len = cmd->sense_buflen; } @@ -2894,9 +2895,10 @@ void scst_check_reassign_sessions(void) return; } -int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) +int scst_get_cmd_abnormal_done_state(struct scst_cmd *cmd) { int res; + bool trace = false; TRACE_ENTRY(); @@ -2907,12 +2909,14 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) if (cmd->preprocessing_only) { res = SCST_CMD_STATE_PREPROCESSING_DONE; break; - } /* else go through */ + } + trace = true; + /* go through */ case SCST_CMD_STATE_DEV_DONE: if (cmd->internal) res = SCST_CMD_STATE_FINISHED_INTERNAL; else - res = SCST_CMD_STATE_PRE_XMIT_RESP; + res = SCST_CMD_STATE_PRE_XMIT_RESP1; break; case SCST_CMD_STATE_PRE_DEV_DONE: @@ -2920,15 +2924,20 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) res = SCST_CMD_STATE_DEV_DONE; break; - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + res = SCST_CMD_STATE_PRE_XMIT_RESP2; + break; + + case SCST_CMD_STATE_PRE_XMIT_RESP2: res = SCST_CMD_STATE_XMIT_RESP; break; case SCST_CMD_STATE_PREPROCESSING_DONE: case SCST_CMD_STATE_PREPROCESSING_DONE_CALLED: - if (cmd->tgt_dev == NULL) - res = SCST_CMD_STATE_PRE_XMIT_RESP; - else + if (cmd->tgt_dev == NULL) { + trace = true; + res = SCST_CMD_STATE_PRE_XMIT_RESP1; + } else res = SCST_CMD_STATE_PRE_DEV_DONE; break; @@ -2949,11 +2958,29 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) break; default: - PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %x)", - cmd->state, cmd, cmd->cdb[0]); + PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %s)", + cmd->state, cmd, scst_get_opcode_name(cmd)); sBUG(); +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 < 6 /* Invalid state to suppress a compiler warning */ res = SCST_CMD_STATE_LAST_ACTIVE; +#endif + } + + if (trace) { + /* + * Little hack to trace completion of commands, which are + * going to bypass normal tracing on SCST_CMD_STATE_PRE_DEV_DONE + */ + TRACE(TRACE_SCSI, "cmd %p, status %x, msg_status %x, host_status %x, " + "driver_status %x, resp_data_len %d", cmd, cmd->status, + cmd->msg_status, cmd->host_status, cmd->driver_status, + cmd->resp_data_len); + if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION) && + scst_sense_valid(cmd->sense)) { + PRINT_BUFF_FLAG(TRACE_SCSI, "Sense", cmd->sense, + cmd->sense_valid_len); + } } TRACE_EXIT_RES(res); @@ -2977,8 +3004,8 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) case SCST_CMD_STATE_FINISHED: case SCST_CMD_STATE_FINISHED_INTERNAL: case SCST_CMD_STATE_XMIT_WAIT: - PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %x)", - cmd->state, cmd, cmd->cdb[0]); + PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %s)", + cmd->state, cmd, scst_get_opcode_name(cmd)); sBUG(); } #endif @@ -3006,22 +3033,23 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) case SCST_CMD_STATE_DEV_DONE: case SCST_CMD_STATE_PRE_DEV_DONE: case SCST_CMD_STATE_MODE_SELECT_CHECKS: - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + case SCST_CMD_STATE_PRE_XMIT_RESP2: case SCST_CMD_STATE_FINISHED_INTERNAL: break; default: - PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %x)", - cmd->state, cmd, cmd->cdb[0]); + PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %s)", + cmd->state, cmd, scst_get_opcode_name(cmd)); sBUG(); break; } #ifdef CONFIG_SCST_EXTRACHECKS - if (((cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP) && + if (((cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP1) && (cmd->state != SCST_CMD_STATE_PREPROCESSING_DONE)) && (cmd->tgt_dev == NULL) && !cmd->internal) { PRINT_CRIT_ERROR("Wrong not inited cmd state %d (cmd %p, " - "op %x)", cmd->state, cmd, cmd->cdb[0]); + "op %s)", cmd->state, cmd, scst_get_opcode_name(cmd)); sBUG(); } #endif @@ -3031,6 +3059,20 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) } EXPORT_SYMBOL_GPL(scst_set_cmd_abnormal_done_state); +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) +const char *scst_get_opcode_name(struct scst_cmd *cmd) +{ + if (cmd->op_name) + return cmd->op_name; + else { + scnprintf(cmd->not_parsed_op_name, + sizeof(cmd->not_parsed_op_name), "0x%x", cmd->cdb[0]); + return cmd->not_parsed_op_name; + } +} +EXPORT_SYMBOL(scst_get_opcode_name); +#endif + void scst_zero_write_rest(struct scst_cmd *cmd) { int len, offs = 0; @@ -3674,7 +3716,7 @@ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev) INIT_LIST_HEAD(&dev->dev_tgt_dev_list); INIT_LIST_HEAD(&dev->dev_acg_dev_list); dev->dev_double_ua_possible = 1; - dev->queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER; + dev->queue_alg = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER; mutex_init(&dev->dev_pr_mutex); dev->pr_generation = 0; @@ -4358,7 +4400,8 @@ out_deinit: /* * scst_mutex supposed to be held, there must not be parallel activity in this - * session. + * session. May be invoked from inside scst_check_reassign_sessions() which + * means that sess->acg can be NULL. */ static int scst_alloc_add_tgt_dev(struct scst_session *sess, struct scst_acg_dev *acg_dev, struct scst_tgt_dev **out_tgt_dev) @@ -4386,7 +4429,7 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess, tgt_dev->tgt_dev_rd_only = acg_dev->acg_dev_rd_only || dev->dev_rd_only; tgt_dev->sess = sess; atomic_set(&tgt_dev->tgt_dev_cmd_count, 0); - if (sess->acg->acg_black_hole_type != SCST_ACG_BLACK_HOLE_NONE) + if (acg_dev->acg->acg_black_hole_type != SCST_ACG_BLACK_HOLE_NONE) set_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags); else clear_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags); @@ -4419,7 +4462,7 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess, INIT_LIST_HEAD(&tgt_dev->UA_list); scst_init_order_data(&tgt_dev->tgt_dev_order_data); - if (dev->tst == SCST_CONTR_MODE_SEP_TASK_SETS) + if (dev->tst == SCST_TST_1_SEP_TASK_SETS) tgt_dev->curr_order_data = &tgt_dev->tgt_dev_order_data; else tgt_dev->curr_order_data = &dev->dev_order_data; @@ -4799,7 +4842,8 @@ static struct scst_cmd *scst_create_prepare_internal_cmd( scst_set_start_time(res); - TRACE(TRACE_SCSI, "New internal cmd %p (op 0x%x)", res, res->cdb[0]); + TRACE(TRACE_SCSI, "New internal cmd %p (op %s)", res, + scst_get_opcode_name(res)); rc = scst_pre_parse(res); sBUG_ON(rc != 0); @@ -4889,10 +4933,10 @@ static void scst_complete_request_sense(struct scst_cmd *req_cmd) if (scsi_status_is_good(req_cmd->status) && (len > 0) && scst_sense_valid(buf) && !scst_no_sense(buf)) { - PRINT_BUFF_FLAG(TRACE_SCSI, "REQUEST SENSE returned", + TRACE(TRACE_SCSI, "REQUEST SENSE %p returned valid sense", + req_cmd); + scst_alloc_set_sense(orig_cmd, scst_cmd_atomic(req_cmd), buf, len); - scst_alloc_set_sense(orig_cmd, scst_cmd_atomic(req_cmd), buf, - len); } else { PRINT_ERROR("%s", "Unable to get the sense via " "REQUEST SENSE, returning HARDWARE ERROR"); @@ -5325,7 +5369,7 @@ static void scst_send_release(struct scst_device *dev) , NULL #endif ); - TRACE_DBG("MODE_SENSE done: %x", rc); + TRACE_DBG("RELEASE done: %x", rc); if (scsi_status_is_good(rc)) { break; @@ -5759,8 +5803,8 @@ void scst_free_cmd(struct scst_cmd *cmd) } if (likely(cmd->tgt_dev != NULL)) { - EXTRACHECKS_BUG_ON(!test_bit(SCST_CMD_INC_EXPECTED_SN_PASSED, - &cmd->cmd_flags) && cmd->sn_set && !cmd->out_of_sn); + EXTRACHECKS_BUG_ON(cmd->sn_set && !cmd->out_of_sn && + !test_bit(SCST_CMD_INC_EXPECTED_SN_PASSED, &cmd->cmd_flags)); if (unlikely(cmd->out_of_sn)) { destroy = test_and_set_bit(SCST_CMD_CAN_BE_DESTROYED, &cmd->cmd_flags); @@ -5882,7 +5926,7 @@ void scst_free_mgmt_cmd(struct scst_mgmt_cmd *mcmd) scst_sess_put(mcmd->sess); - if (mcmd->mcmd_tgt_dev != NULL) + if ((mcmd->mcmd_tgt_dev != NULL) || mcmd->scst_get_called) scst_put(mcmd->cpu_cmd_counter); mempool_free(mcmd, scst_mgmt_mempool); @@ -6409,7 +6453,7 @@ int scst_get_buf_full(struct scst_cmd *cmd, uint8_t **buf) *buf = vmalloc(full_size); if (*buf == NULL) { TRACE(TRACE_OUT_OF_MEM, "vmalloc() failed for opcode " - "%x", cmd->cdb[0]); + "%s", scst_get_opcode_name(cmd)); res = -ENOMEM; goto out; } @@ -6594,7 +6638,8 @@ out: return res; out_inval_bufflen10: - PRINT_ERROR("Too big bufflen %d (op %x)", cmd->bufflen, cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", cmd->bufflen, + scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, 10, 0); res = 1; goto out; @@ -6796,8 +6841,8 @@ static int get_cdb_info_verify12(struct scst_cmd *cmd, if (cmd->cdb[1] & 2) { /* BYTCHK 01 */ cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { - PRINT_ERROR("Too big bufflen %d (op %x)", - cmd->bufflen, cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", + cmd->bufflen, scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, sdbops->info_len_off, 0); return 1; } @@ -6826,8 +6871,8 @@ static int get_cdb_info_verify16(struct scst_cmd *cmd, if (cmd->cdb[1] & 2) { /* BYTCHK 01 */ cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { - PRINT_ERROR("Too big bufflen %d (op %x)", - cmd->bufflen, cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", + cmd->bufflen, scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, sdbops->info_len_off, 0); return 1; } @@ -6900,8 +6945,8 @@ static int get_cdb_info_len_4(struct scst_cmd *cmd, cmd->lba = 0; cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { - PRINT_ERROR("Too big bufflen %d (op %x)", cmd->bufflen, - cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", cmd->bufflen, + scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, sdbops->info_len_off, 0); return 1; } @@ -6973,8 +7018,8 @@ static int get_cdb_info_lba_4_len_4(struct scst_cmd *cmd, cmd->lba = get_unaligned_be32(cmd->cdb + sdbops->info_lba_off); cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { - PRINT_ERROR("Too big bufflen %d (op %x)", cmd->bufflen, - cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", cmd->bufflen, + scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, sdbops->info_len_off, 0); return 1; } @@ -6988,8 +7033,8 @@ static int get_cdb_info_lba_8_len_4(struct scst_cmd *cmd, cmd->lba = get_unaligned_be64(cmd->cdb + sdbops->info_lba_off); cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { - PRINT_ERROR("Too big bufflen %d (op %x)", cmd->bufflen, - cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", cmd->bufflen, + scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, sdbops->info_len_off, 0); return 1; } @@ -7135,8 +7180,9 @@ static int get_cdb_info_min(struct scst_cmd *cmd, break; case MI_REPORT_SUPPORTED_OPERATION_CODES: cmd->op_name = "REPORT SUPPORTED OPERATION CODES"; - cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED | - SCST_LOCAL_CMD | SCST_FULLY_LOCAL_CMD; + cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED; + if (cmd->devt->get_supported_opcodes != NULL) + cmd->op_flags |= SCST_LOCAL_CMD | SCST_FULLY_LOCAL_CMD; break; case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: cmd->op_name = "REPORT SUPPORTED TASK MANAGEMENT FUNCTIONS"; @@ -7343,9 +7389,10 @@ EXPORT_SYMBOL_GPL(scst_calc_block_shift); #define shift_left_overflows(a, b) \ ({ \ typeof(a) _minus_one = -1LL; \ + typeof(a) _plus_one = 1; \ bool _a_is_signed = _minus_one < 0; \ - int _shift = sizeof(1ULL) * 8 - ((b) + _a_is_signed); \ - _shift < 0 || ((a) & ~((1ULL << _shift) - 1)) != 0; \ + int _shift = sizeof(a) * 8 - ((b) + _a_is_signed); \ + _shift < 0 || ((a) & ~((_plus_one << _shift) - 1)) != 0;\ }) /** @@ -7370,6 +7417,20 @@ static inline int scst_generic_parse(struct scst_cmd *cmd, const int timeout[3]) * No need for locks here, since *_detach() can not be * called, when there are existing commands. */ + bool overflow = shift_left_overflows(cmd->bufflen, block_shift) || + shift_left_overflows(cmd->data_len, block_shift) || + shift_left_overflows(cmd->out_bufflen, block_shift); + if (unlikely(overflow)) { + PRINT_WARNING("bufflen %u, data_len %llu or out_bufflen" + " %u too large for device %s (block size" + " %u)", cmd->bufflen, cmd->data_len, + cmd->out_bufflen, cmd->dev->virt_name, + 1 << block_shift); + PRINT_BUFFER("CDB", cmd->cdb, cmd->cdb_len); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE( + scst_sense_block_out_range_error)); + goto out; + } cmd->bufflen = cmd->bufflen << block_shift; cmd->data_len = cmd->data_len << block_shift; cmd->out_bufflen = cmd->out_bufflen << block_shift; @@ -8009,7 +8070,7 @@ void scst_process_reset(struct scst_device *dev, dev->dev_double_ua_possible = 1; list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, - dev_tgt_dev_list_entry) { + dev_tgt_dev_list_entry) { struct scst_session *sess = tgt_dev->sess; #if 0 /* Clearing UAs and last sense isn't required by SAM and it @@ -8044,6 +8105,17 @@ void scst_process_reset(struct scst_device *dev, uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; int sl = scst_set_sense(sense_buffer, sizeof(sense_buffer), dev->d_sense, SCST_LOAD_SENSE(scst_sense_reset_UA)); + /* + * Potentially, setting UA here, when the aborted commands are + * still running, can lead to a situation that one of them could + * take it, then that would be detected and the UA requeued. + * But, meanwhile, one or more subsequent, i.e. not aborted, + * commands can "leak" executed normally. So, as result, the + * UA would be delivered one or more commands "later". However, + * that should be OK, because, if multiple commands are being + * executed in parallel, you can't control exact order of UA + * delivery anyway. + */ scst_dev_check_set_local_UA(dev, exclude_cmd, sense_buffer, sl); } @@ -8401,7 +8473,7 @@ struct scst_cmd *__scst_check_deferred_commands_locked( restart: list_for_each_entry_safe(cmd, t, &order_data->deferred_cmd_list, - sn_cmd_list_entry) { + deferred_cmd_list_entry) { EXTRACHECKS_BUG_ON((cmd->queue_type != SCST_CMD_QUEUE_SIMPLE) && (cmd->queue_type != SCST_CMD_QUEUE_ORDERED)); if (cmd->sn == expected_sn) { @@ -8411,7 +8483,7 @@ restart: cmd, cmd->sn, cmd->sn_set); order_data->def_cmd_count--; - list_del(&cmd->sn_cmd_list_entry); + list_del(&cmd->deferred_cmd_list_entry); if (activate) { spin_lock(&cmd->cmd_threads->cmd_list_lock); @@ -8443,18 +8515,19 @@ restart: goto out; list_for_each_entry(cmd, &order_data->skipped_sn_list, - sn_cmd_list_entry) { + deferred_cmd_list_entry) { EXTRACHECKS_BUG_ON(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE); if (cmd->sn == expected_sn) { /* * !! At this point any pointer in cmd, except !! - * !! cur_order_data, sn_slot and sn_cmd_list_entry, !! - * !! could be already destroyed! !! + * !! cur_order_data, sn_slot and !! + * !! deferred_cmd_list_entry, could be already !! + * !! destroyed! !! */ TRACE_SN("cmd %p (tag %llu) with skipped sn %d found", cmd, (long long unsigned int)cmd->tag, cmd->sn); order_data->def_cmd_count--; - list_del(&cmd->sn_cmd_list_entry); + list_del(&cmd->deferred_cmd_list_entry); spin_unlock_irq(&order_data->sn_lock); scst_inc_expected_sn(cmd); if (test_and_set_bit(SCST_CMD_CAN_BE_DESTROYED, @@ -8505,7 +8578,7 @@ void scst_unblock_deferred(struct scst_order_data *order_data, out_of_sn_cmd->out_of_sn = 1; spin_lock_irq(&order_data->sn_lock); order_data->def_cmd_count++; - list_add_tail(&out_of_sn_cmd->sn_cmd_list_entry, + list_add_tail(&out_of_sn_cmd->deferred_cmd_list_entry, &order_data->skipped_sn_list); TRACE_SN("out_of_sn_cmd %p with sn %d added to skipped_sn_list" " (expected_sn %d)", out_of_sn_cmd, out_of_sn_cmd->sn, @@ -8551,15 +8624,15 @@ bool __scst_check_blocked_dev(struct scst_cmd *cmd) if (dev->block_count > 0) { TRACE_BLOCK("Delaying cmd %p due to blocking " - "(tag %llu, op %x, dev %s)", cmd, - (long long unsigned int)cmd->tag, cmd->cdb[0], - dev->virt_name); + "(tag %llu, op %s, dev %s)", cmd, + (long long unsigned int)cmd->tag, + scst_get_opcode_name(cmd), dev->virt_name); goto out_block; } else if ((cmd->op_flags & SCST_STRICTLY_SERIALIZED) == SCST_STRICTLY_SERIALIZED) { - TRACE_BLOCK("cmd %p (tag %llu, op %x): blocking further " + TRACE_BLOCK("cmd %p (tag %llu, op %s): blocking further " "cmds on dev %s due to strict serialization", cmd, - (long long unsigned int)cmd->tag, cmd->cdb[0], - dev->virt_name); + (long long unsigned int)cmd->tag, + scst_get_opcode_name(cmd), dev->virt_name); scst_block_dev(dev); if (dev->on_dev_cmd_count > 1) { TRACE_BLOCK("Delaying strictly serialized cmd %p " @@ -8572,9 +8645,9 @@ bool __scst_check_blocked_dev(struct scst_cmd *cmd) cmd->unblock_dev = 1; } else if ((dev->dev_double_ua_possible) || ((cmd->op_flags & SCST_SERIALIZED) != 0)) { - TRACE_BLOCK("cmd %p (tag %llu, op %x): blocking further cmds " + TRACE_BLOCK("cmd %p (tag %llu, op %s): blocking further cmds " "on dev %s due to %s", cmd, (long long unsigned int)cmd->tag, - cmd->cdb[0], dev->virt_name, + scst_get_opcode_name(cmd), dev->virt_name, dev->dev_double_ua_possible ? "possible double reset UA" : "serialized cmd"); scst_block_dev(dev); @@ -8707,12 +8780,16 @@ int scst_obtain_device_parameters(struct scst_device *dev, "page data", buffer, sizeof(buffer)); dev->tst = buffer[4+2] >> 5; + dev->tmf_only = (buffer[4+2] & 0x10) >> 4; q = buffer[4+3] >> 4; - if (q > SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER) { - PRINT_ERROR("Too big QUEUE ALG %x, dev %s", + if (q > SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER) { + PRINT_ERROR("Too big QUEUE ALG %x, dev %s, " + "using default: unrestricted reorder", dev->queue_alg, dev->virt_name); + q = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER; } dev->queue_alg = q; + dev->qerr = (buffer[4+3] & 0x6) >> 1; dev->swp = (buffer[4+4] & 0x8) >> 3; dev->tas = (buffer[4+5] & 0x40) >> 6; dev->d_sense = (buffer[4+2] & 0x4) >> 2; @@ -8725,10 +8802,11 @@ int scst_obtain_device_parameters(struct scst_device *dev, */ dev->has_own_order_mgmt = !dev->queue_alg; - PRINT_INFO("Device %s: TST %x, QUEUE ALG %x, SWP %x, " - "TAS %x, D_SENSE %d, has_own_order_mgmt %d", - dev->virt_name, dev->tst, dev->queue_alg, - dev->swp, dev->tas, dev->d_sense, + PRINT_INFO("Device %s: TST %x, TMF_ONLY %x, QUEUE ALG %x, " + "QErr %x, SWP %x, TAS %x, D_SENSE %d, " + "has_own_order_mgmt %d", dev->virt_name, + dev->tst, dev->tmf_only, dev->queue_alg, + dev->qerr, dev->swp, dev->tas, dev->d_sense, dev->has_own_order_mgmt); goto out; @@ -8745,9 +8823,8 @@ int scst_obtain_device_parameters(struct scst_device *dev, */ if (scst_sense_valid(sense_buffer)) { #endif - PRINT_BUFF_FLAG(TRACE_SCSI, "Returned sense " - "data", sense_buffer, - sizeof(sense_buffer)); + PRINT_BUFF_FLAG(TRACE_SCSI, "MODE SENSE returned " + "sense", sense_buffer, sizeof(sense_buffer)); if (scst_analyze_sense(sense_buffer, sizeof(sense_buffer), SCST_SENSE_KEY_VALID, @@ -8768,8 +8845,6 @@ int scst_obtain_device_parameters(struct scst_device *dev, PRINT_INFO("Internal MODE SENSE to " "device %s failed: %x", dev->virt_name, rc); - PRINT_BUFF_FLAG(TRACE_SCSI, "MODE SENSE sense", - sense_buffer, sizeof(sense_buffer)); switch (host_byte(rc)) { case DID_RESET: case DID_ABORT: @@ -8790,9 +8865,10 @@ int scst_obtain_device_parameters(struct scst_device *dev, } brk: PRINT_WARNING("Unable to get device's %s control mode page, using " - "existing values/defaults: TST %x, QUEUE ALG %x, SWP %x, " - "TAS %x, D_SENSE %d, has_own_order_mgmt %d", dev->virt_name, - dev->tst, dev->queue_alg, dev->swp, dev->tas, dev->d_sense, + "existing values/defaults: TST %x, TMF_ONLY %x, QUEUE ALG %x, " + "QErr %x, SWP %x, TAS %x, D_SENSE %d, has_own_order_mgmt %d", + dev->virt_name, dev->tst, dev->tmf_only, dev->queue_alg, + dev->qerr, dev->swp, dev->tas, dev->d_sense, dev->has_own_order_mgmt); out: @@ -8860,6 +8936,151 @@ void scst_store_sense(struct scst_cmd *cmd) return; } +/* dev_lock supposed to be locked and BHs off */ +static void scst_abort_cmds_tgt_dev(struct scst_tgt_dev *tgt_dev, + struct scst_cmd *exclude_cmd) +{ + struct scst_session *sess = tgt_dev->sess; + struct scst_cmd *cmd; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("QErr: aborting commands for tgt_dev %p " + "(exclude_cmd %p), if there are any", tgt_dev, exclude_cmd); + + spin_lock_irq(&sess->sess_list_lock); + + list_for_each_entry(cmd, &sess->sess_cmd_list, sess_cmd_list_entry) { + if (cmd == exclude_cmd) + continue; + if ((cmd->tgt_dev == tgt_dev) || + ((cmd->tgt_dev == NULL) && + (cmd->lun == tgt_dev->lun))) { + scst_abort_cmd(cmd, NULL, (tgt_dev != exclude_cmd->tgt_dev), 0); + } + } + spin_unlock_irq(&sess->sess_list_lock); + + TRACE_EXIT(); + return; +} + +/* dev_lock supposed to be locked and BHs off */ +static void scst_abort_cmds_dev(struct scst_device *dev, + struct scst_cmd *exclude_cmd) +{ + struct scst_tgt_dev *tgt_dev; + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; + int sl = 0; + bool set_ua = (dev->tas == 0); + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("QErr: Aborting commands for dev %p (exclude_cmd %p, " + "set_ua %d), if there are any", dev, exclude_cmd, set_ua); + + if (set_ua) + sl = scst_set_sense(sense_buffer, sizeof(sense_buffer), dev->d_sense, + SCST_LOAD_SENSE(scst_sense_cleared_by_another_ini_UA)); + + list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, dev_tgt_dev_list_entry) { + scst_abort_cmds_tgt_dev(tgt_dev, exclude_cmd); + /* + * Potentially, setting UA here, when the aborted commands are + * still running, can lead to a situation that one of them could + * take it, then that would be detected and the UA requeued. + * But, meanwhile, one or more subsequent, i.e. not aborted, + * commands can "leak" executed normally. So, as result, the + * UA would be delivered one or more commands "later". However, + * that should be OK, because, if multiple commands are being + * executed in parallel, you can't control exact order of UA + * delivery anyway. + */ + if (set_ua && (tgt_dev != exclude_cmd->tgt_dev)) + scst_check_set_UA(tgt_dev, sense_buffer, sl, 0); + } + + TRACE_EXIT(); + return; +} + +/* No locks */ +static void scst_process_qerr(struct scst_cmd *cmd) +{ + bool unblock = false; + struct scst_device *dev = cmd->dev; + unsigned int qerr, q; + + TRACE_ENTRY(); + + /* dev->qerr can be changed behind our back */ + q = dev->qerr; + qerr = ACCESS_ONCE(q); /* ACCESS_ONCE doesn't work for bit fields */ + + TRACE_DBG("Processing QErr %d for cmd %p", qerr, cmd); + + spin_lock_bh(&dev->dev_lock); + + switch (qerr) { + case SCST_QERR_2_RESERVED: + default: + PRINT_WARNING("Invalid QErr value %x for device %s, process as " + "0", qerr, dev->virt_name); + /* go through */ + case SCST_QERR_0_ALL_RESUME: + /* Nothing to do */ + break; + case SCST_QERR_1_ABORT_ALL: + if (dev->tst == SCST_TST_0_SINGLE_TASK_SET) + scst_abort_cmds_dev(dev, cmd); + else + scst_abort_cmds_tgt_dev(cmd->tgt_dev, cmd); + unblock = true; + break; + case SCST_QERR_3_ABORT_THIS_NEXUS_ONLY: + scst_abort_cmds_tgt_dev(cmd->tgt_dev, cmd); + unblock = true; + break; + } + + spin_unlock_bh(&dev->dev_lock); + + if (unblock) + scst_unblock_aborted_cmds(cmd->tgt, cmd->sess, dev, false); + + TRACE_EXIT(); + return; +} + +/* + * No locks. Returns -1, if processing should be switched to another cmd, 1 + * if cmd was aborted, 0 if cmd processing should continue. + */ +int scst_process_check_condition(struct scst_cmd *cmd) +{ + int res; + struct scst_order_data *order_data; + struct scst_device *dev; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags)); + + order_data = cmd->cur_order_data; + dev = cmd->dev; + + TRACE_DBG("CHECK CONDITION for cmd %p (tgt_dev %p)", cmd, cmd->tgt_dev); + + scst_process_qerr(cmd); + + scst_store_sense(cmd); + + res = 0; + + TRACE_EXIT_RES(res); + return res; +} + void scst_xmit_process_aborted_cmd(struct scst_cmd *cmd) { TRACE_ENTRY(); @@ -9139,8 +9360,7 @@ static int scst_parse_unmap_descriptors(struct scst_cmd *cmd) ((descriptor_len % 16) != 0))) { PRINT_ERROR("Bad descriptor length: %d < %d - 8", descriptor_len, total_len); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + scst_set_invalid_field_in_parm_list(cmd, 2, 0); goto out_abn_put; } @@ -9236,6 +9456,318 @@ static void scst_free_descriptors(struct scst_cmd *cmd) return; } +/** + ** We currently have only few saved parameters and it is impossible to get + ** pointer on a bit field, so let's have a simple straightforward + ** implementation. + **/ + +#define SCST_TAS_LABEL "TAS" +#define SCST_QERR_LABEL "QERR" +#define SCST_TMF_ONLY_LABEL "TMF_ONLY" +#define SCST_SWP_LABEL "SWP" +#define SCST_DSENSE_LABEL "D_SENSE" +#define SCST_QUEUE_ALG_LABEL "QUEUE_ALG" + +int scst_save_global_mode_pages(const struct scst_device *dev, + uint8_t *buf, int size) +{ + int res = 0; + + TRACE_ENTRY(); + + if (dev->tas != dev->tas_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_TAS_LABEL, dev->tas); + if (res >= size-1) + goto out_overflow; + } + + if (dev->qerr != dev->qerr_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_QERR_LABEL, dev->qerr); + if (res >= size-1) + goto out_overflow; + } + + if (dev->tmf_only != dev->tmf_only_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_TMF_ONLY_LABEL, dev->tmf_only); + if (res >= size-1) + goto out_overflow; + } + + if (dev->swp != dev->swp_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_SWP_LABEL, dev->swp); + if (res >= size-1) + goto out_overflow; + } + + if (dev->d_sense != dev->d_sense_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_DSENSE_LABEL, dev->d_sense); + if (res >= size-1) + goto out_overflow; + } + + if (dev->queue_alg != dev->queue_alg_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_QUEUE_ALG_LABEL, dev->queue_alg); + if (res >= size-1) + goto out_overflow; + } + +out: + TRACE_EXIT_RES(res); + return res; + +out_overflow: + PRINT_ERROR("Global mode pages buffer overflow (size %d)", size); + res = -EOVERFLOW; + goto out; +} +EXPORT_SYMBOL_GPL(scst_save_global_mode_pages); + +static int scst_restore_tas(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_TAS_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->tas = val; + dev->tas_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_TAS_LABEL, + dev->tas, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_qerr(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if ((val == SCST_QERR_2_RESERVED) || + (val > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_QERR_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->qerr = val; + dev->qerr_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_QERR_LABEL, + dev->qerr, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_tmf_only(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_TMF_ONLY_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->tmf_only = val; + dev->tmf_only_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_TMF_ONLY_LABEL, + dev->tmf_only, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_swp(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_SWP_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->swp = val; + dev->swp_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_SWP_LABEL, + dev->swp, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_dsense(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_DSENSE_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->d_sense = val; + dev->d_sense_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_DSENSE_LABEL, + dev->d_sense, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_queue_alg(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if ((val != SCST_QUEUE_ALG_0_RESTRICTED_REORDER) && + (val != SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER)) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_QUEUE_ALG_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->queue_alg = val; + dev->queue_alg_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_QUEUE_ALG_LABEL, + dev->queue_alg, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* Params are NULL-terminated */ +int scst_restore_global_mode_pages(struct scst_device *dev, char *params, + char **last_param) +{ + int res; + char *param, *p, *pp; + unsigned long val; + + TRACE_ENTRY(); + + while (1) { + param = scst_get_next_token_str(¶ms); + if (param == NULL) + break; + + p = scst_get_next_lexem(¶m); + if (*p == '\0') + break; + + pp = scst_get_next_lexem(¶m); + if (*pp == '\0') + goto out_need_param; + + if (scst_get_next_lexem(¶m)[0] != '\0') + goto out_too_many; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + res = kstrtoul(pp, 0, &val); +#else + res = strict_strtoul(pp, 0, &val); +#endif + if (res != 0) + goto out_strtoul_failed; + + if (strcasecmp(SCST_TAS_LABEL, p) == 0) + res = scst_restore_tas(dev, val); + else if (strcasecmp(SCST_QERR_LABEL, p) == 0) + res = scst_restore_qerr(dev, val); + else if (strcasecmp(SCST_TMF_ONLY_LABEL, p) == 0) + res = scst_restore_tmf_only(dev, val); + else if (strcasecmp(SCST_SWP_LABEL, p) == 0) + res = scst_restore_swp(dev, val); + else if (strcasecmp(SCST_DSENSE_LABEL, p) == 0) + res = scst_restore_dsense(dev, val); + else if (strcasecmp(SCST_QUEUE_ALG_LABEL, p) == 0) + res = scst_restore_queue_alg(dev, val); + else { + TRACE_DBG("Unknown parameter %s", p); + scst_restore_token_str(p, param); + *last_param = p; + goto out; + } + if (res != 0) + goto out; + } + + *last_param = NULL; + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; + +out_strtoul_failed: + PRINT_ERROR("strtoul() for %s failed: %d (device %s)", pp, res, + dev->virt_name); + goto out; + +out_need_param: + PRINT_ERROR("Parameter %s value missed for device %s", p, dev->virt_name); + res = -EINVAL; + goto out; + +out_too_many: + PRINT_ERROR("Too many parameter's %s values (device %s)", p, dev->virt_name); + res = -EINVAL; + goto out; +} +EXPORT_SYMBOL_GPL(scst_restore_global_mode_pages); + + /* Abstract vfs_unlink() for different kernel versions (as possible) */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) void scst_vfs_unlink_and_put(struct nameidata *nd) @@ -9272,6 +9804,7 @@ void scst_path_put(struct nameidata *nd) path_put(&nd->path); #endif } +EXPORT_SYMBOL(scst_path_put); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) @@ -9420,6 +9953,195 @@ int scst_remove_file(const char *name) TRACE_EXIT_RES(res); return res; } +EXPORT_SYMBOL_GPL(scst_remove_file); + +/* Returns 0 on success, error code otherwise */ +int scst_write_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, const uint8_t *buf, int size) +{ + int res; + struct file *file; + mm_segment_t old_fs = get_fs(); + loff_t pos = 0; + char n = '\n'; + + TRACE_ENTRY(); + + res = scst_copy_file(name, name1); + if ((res != 0) && (res != -ENOENT)) + goto out; + + set_fs(KERNEL_DS); + + file = filp_open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(file)) { + res = PTR_ERR(file); + PRINT_ERROR("Unable to (re)create file '%s' - error %d", + name, res); + goto out_set_fs; + } + + TRACE_DBG("Writing file '%s'", name); + + pos = signature_len+1; + + res = vfs_write(file, (void __force __user *)buf, size, &pos); + if (res != size) + goto write_error; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + res = scst_vfs_fsync(file, 0, pos); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + res = vfs_fsync(file, file->f_path.dentry, 1); +#else + res = vfs_fsync(file, 1); +#endif + if (res != 0) { + PRINT_ERROR("fsync() of file %s failed: %d", name, res); + goto write_error_close; + } + + pos = 0; + res = vfs_write(file, (void __force __user *)signature, signature_len, &pos); + if (res != signature_len) + goto write_error; + + res = vfs_write(file, (void __force __user *)&n, sizeof(n), &pos); + if (res != sizeof(n)) + goto write_error; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + res = scst_vfs_fsync(file, 0, sizeof(signature)); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + res = vfs_fsync(file, file->f_path.dentry, 1); +#else + res = vfs_fsync(file, 1); +#endif + if (res != 0) { + PRINT_ERROR("fsync() of file %s failed: %d", name, res); + goto write_error_close; + } + + res = 0; + + filp_close(file, NULL); + +out_set_fs: + set_fs(old_fs); + + if (res == 0) + scst_remove_file(name1); + else + scst_remove_file(name); + +out: + TRACE_EXIT_RES(res); + return res; + +write_error: + PRINT_ERROR("Error writing to '%s' - error %d", name, res); + +write_error_close: + filp_close(file, NULL); + if (res > 0) + res = -EIO; + goto out_set_fs; +} +EXPORT_SYMBOL_GPL(scst_write_file_transactional); + +static int __scst_read_file_transactional(const char *file_name, + const char *signature, int signature_len, uint8_t *buf, int size) +{ + int res; + struct file *file = NULL; + struct inode *inode; + loff_t file_size, pos; + mm_segment_t old_fs; + + TRACE_ENTRY(); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + TRACE_DBG("Loading file '%s'", file_name); + + file = filp_open(file_name, O_RDONLY, 0); + if (IS_ERR(file)) { + res = PTR_ERR(file); + TRACE_DBG("Unable to open file '%s' - error %d", file_name, res); + goto out; + } + + inode = file->f_dentry->d_inode; + + if (S_ISREG(inode->i_mode)) + /* Nothing to do */; + else if (S_ISBLK(inode->i_mode)) + inode = inode->i_bdev->bd_inode; + else { + PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode); + res = -EINVAL; + goto out_close; + } + + file_size = inode->i_size; + + if (file_size > size) { + PRINT_ERROR("Supplied buffer (%d) too small (need %d)", size, + (int)file_size); + res = -EOVERFLOW; + goto out_close; + } + + pos = 0; + res = vfs_read(file, (void __force __user *)buf, file_size, &pos); + if (res != file_size) { + PRINT_ERROR("Unable to read file '%s' - error %d", file_name, res); + if (res > 0) + res = -EIO; + goto out_close; + } + + if (memcmp(buf, signature, signature_len) != 0) { + res = -EINVAL; + PRINT_ERROR("Invalid signature in file %s", file_name); + goto out_close; + } + +out_close: + filp_close(file, NULL); + +out: + set_fs(old_fs); + + TRACE_EXIT_RES(res); + return res; +} + +/* + * Returns read data size on success, error code otherwise. The first + * signature_len+1 bytes of the read data contain signature, so should be + * skipped. + */ +int scst_read_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, uint8_t *buf, int size) +{ + int res; + + TRACE_ENTRY(); + + res = __scst_read_file_transactional(name, signature, signature_len, buf, size); + if (res <= 0) + res = __scst_read_file_transactional(name1, signature, + signature_len, buf, size); + + if (res > 0) + TRACE_BUFFER("Read data", buf, res); + + TRACE_EXIT_RES(res); + return res; +} +EXPORT_SYMBOL_GPL(scst_read_file_transactional); static void __init scst_scsi_op_list_init(void) { diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index d3e99cbac..d3f24e383 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -960,8 +960,8 @@ int scst_suspend_activity(unsigned long timeout) set_bit(SCST_FLAG_SUSPENDED, &scst_flags); /* * Assignment of SCST_FLAG_SUSPENDING and SCST_FLAG_SUSPENDED must be - * ordered with cpu_cmd_count in scst_get(). Otherwise lockless logic in - * scst_translate_lun() and scst_mgmt_translate_lun() won't work. + * ordered with cpu_cmd_count in scst_get(). Otherwise, lockless logic + * of scst_get() users won't work. */ smp_mb__after_set_bit(); diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index 48c246d3d..61017a4a8 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -565,6 +565,27 @@ static void scst_pr_abort_reg(struct scst_device *dev, */ PRINT_ERROR("SCST_PR_ABORT_ALL failed %d (sess %p)", rc, sess); + goto out; + } + + if ((reg->tgt_dev != pr_cmd->tgt_dev) && !dev->tas) { + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; + int sl; + sl = scst_set_sense(sense_buffer, sizeof(sense_buffer), + dev->d_sense, + SCST_LOAD_SENSE(scst_sense_cleared_by_another_ini_UA)); + /* + * Potentially, setting UA here, when the aborted commands are + * still running, can lead to a situation that one of them could + * take it, then that would be detected and the UA requeued. + * But, meanwhile, one or more subsequent, i.e. not aborted, + * commands can "leak" executed normally. So, as result, the + * UA would be delivered one or more commands "later". However, + * that should be OK, because, if multiple commands are being + * executed in parallel, you can't control exact order of UA + * delivery anyway. + */ + scst_check_set_UA(reg->tgt_dev, sense_buffer, sl, 0); } out: @@ -1559,8 +1580,8 @@ void scst_pr_register(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) } if (spec_i_pt) { TRACE_PR("%s", "spec_i_pt must be zero in this case"); - scst_set_cmd_error(cmd, SCST_LOAD_SENSE( - scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_parm_list(cmd, 20, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 3); goto out; } if (action_key == 0) { @@ -1700,8 +1721,7 @@ void scst_pr_register_and_move(struct scst_cmd *cmd, uint8_t *buffer, if (tid_buffer_size < 24) { TRACE_PR("%s", "Transport id buffer too small"); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + scst_set_invalid_field_in_parm_list(cmd, 20, 0); goto out; } @@ -1730,9 +1750,8 @@ void scst_pr_register_and_move(struct scst_cmd *cmd, uint8_t *buffer, */ if (!scst_pr_is_holder(dev, reg)) { TRACE_PR("Registrant %s/%d (%p) is not a holder (tgt_dev %p)", - debug_transport_id_to_initiator_name( - reg->transport_id), reg->rel_tgt_id, - reg, tgt_dev); + debug_transport_id_to_initiator_name(reg->transport_id), + reg->rel_tgt_id, reg, tgt_dev); scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); goto out; } @@ -2267,8 +2286,9 @@ bool scst_pr_crh_case(struct scst_cmd *cmd) TRACE_ENTRY(); - TRACE_DBG("Test if there is a CRH case for command %s (0x%x) from " - "%s", cmd->op_name, cmd->cdb[0], cmd->sess->initiator_name); + TRACE_DBG("Test if there is a CRH case for command %s (%s) from " + "%s", cmd->op_name, scst_get_opcode_name(cmd), + cmd->sess->initiator_name); if (!dev->pr_is_set) { TRACE_PR("%s", "PR not set"); @@ -2303,12 +2323,12 @@ bool scst_pr_crh_case(struct scst_cmd *cmd) } if (!allowed) - TRACE_PR("Command %s (0x%x) from %s rejected due to not CRH " - "reservation", cmd->op_name, cmd->cdb[0], + TRACE_PR("Command %s (%s) from %s rejected due to not CRH " + "reservation", cmd->op_name, scst_get_opcode_name(cmd), cmd->sess->initiator_name); else - TRACE_DBG("Command %s (0x%x) from %s is allowed to execute " - "due to CRH", cmd->op_name, cmd->cdb[0], + TRACE_DBG("Command %s (%s) from %s is allowed to execute " + "due to CRH", cmd->op_name, scst_get_opcode_name(cmd), cmd->sess->initiator_name); out: @@ -2330,8 +2350,8 @@ bool scst_pr_is_cmd_allowed(struct scst_cmd *cmd) scst_pr_read_lock(dev); - TRACE_DBG("Testing if command %s (0x%x) from %s allowed to execute", - cmd->op_name, cmd->cdb[0], cmd->sess->initiator_name); + TRACE_DBG("Testing if command %s (%s) from %s allowed to execute", + cmd->op_name, scst_get_opcode_name(cmd), cmd->sess->initiator_name); /* Recheck, because it can change while we were waiting for the lock */ if (unlikely(!dev->pr_is_set)) { @@ -2380,12 +2400,13 @@ bool scst_pr_is_cmd_allowed(struct scst_cmd *cmd) } if (!allowed) - TRACE_PR("Command %s (0x%x) from %s rejected due " - "to PR", cmd->op_name, cmd->cdb[0], + TRACE_PR("Command %s (%s) from %s rejected due " + "to PR", cmd->op_name, scst_get_opcode_name(cmd), cmd->sess->initiator_name); else - TRACE_DBG("Command %s (0x%x) from %s is allowed to execute", - cmd->op_name, cmd->cdb[0], cmd->sess->initiator_name); + TRACE_DBG("Command %s (%s) from %s is allowed to execute", + cmd->op_name, scst_get_opcode_name(cmd), + cmd->sess->initiator_name); out_unlock: scst_pr_read_unlock(dev); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index f56d185be..7df673ee8 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -375,6 +375,8 @@ int scst_set_cmd_error_sense(struct scst_cmd *cmd, uint8_t *sense, unsigned int len); void scst_store_sense(struct scst_cmd *cmd); +int scst_process_check_condition(struct scst_cmd *cmd); + int scst_assign_dev_handler(struct scst_device *dev, struct scst_dev_type *handler); @@ -770,16 +772,11 @@ void scst_vfs_unlink_and_put(struct nameidata *nd); void scst_vfs_unlink_and_put(struct path *path); #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) -void scst_path_put(struct nameidata *nd); -#endif - #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) int scst_vfs_fsync(struct file *file, loff_t loff, loff_t len); #endif int scst_copy_file(const char *src, const char *dest); -int scst_remove_file(const char *name); #ifdef CONFIG_SCST_DEBUG_TM extern void tm_dbg_check_released_cmds(void); diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index 91e4101c6..2a3bc1981 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -397,7 +397,12 @@ int scst_proc_log_entry_write(struct file *file, const char __user *buf, list_for_each_entry(dev, &scst_dev_list, dev_list_entry) { if (strcmp(dev->virt_name, p) == 0) { - scst_pr_dump_prs(dev, true); + if (mutex_lock_interruptible(&dev->dev_pr_mutex) == 0) { + scst_pr_dump_prs(dev, true); + mutex_unlock(&dev->dev_pr_mutex); + } else { + res = -EINTR; + } goto out_up; } } diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 8ca303484..5187d104d 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -2762,15 +2762,23 @@ static ssize_t scst_dev_sysfs_dump_prs(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { struct scst_device *dev; + int res; TRACE_ENTRY(); dev = container_of(kobj, struct scst_device, dev_kobj); + res = mutex_lock_interruptible(&dev->dev_pr_mutex); + if (res != 0) + goto out; scst_pr_dump_prs(dev, true); + mutex_unlock(&dev->dev_pr_mutex); - TRACE_EXIT_RES(count); - return count; + res = count; + +out: + TRACE_EXIT_RES(res); + return res; } static struct kobj_attribute dev_dump_prs_attr = @@ -5414,8 +5422,8 @@ static ssize_t scst_tg_preferred_show(struct kobject *kobj, struct scst_target_group *tg; tg = container_of(kobj, struct scst_target_group, kobj); - return scnprintf(buf, PAGE_SIZE, "%u\n%s", - tg->preferred, SCST_SYSFS_KEY_MARK "\n"); + return scnprintf(buf, PAGE_SIZE, "%u\n%s", tg->preferred, + tg->preferred ? SCST_SYSFS_KEY_MARK "\n" : ""); } static int scst_tg_preferred_store_work_fn(struct scst_sysfs_work_item *w) diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index ff80f55d1..bde560fa3 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -413,12 +413,12 @@ void scst_cmd_init_done(struct scst_cmd *cmd, scst_set_start_time(cmd); TRACE_DBG("Preferred context: %d (cmd %p)", pref_context, cmd); - TRACE(TRACE_SCSI, "lun=%lld, initiator %s, target %s, CDB len=%d, " - "queue_type=%x, tag=%llu (cmd %p, sess %p)", - (long long unsigned int)cmd->lun, cmd->sess->initiator_name, - cmd->tgt->tgt_name, cmd->cdb_len, cmd->queue_type, + TRACE(TRACE_SCSI, "NEW CDB: len %d, lun %lld, initiator %s, " + "target %s, queue_type %x, tag %llu (cmd %p, sess %p)", + cmd->cdb_len, (long long unsigned int)cmd->lun, + cmd->sess->initiator_name, cmd->tgt->tgt_name, cmd->queue_type, (long long unsigned int)cmd->tag, cmd, sess); - PRINT_BUFF_FLAG(TRACE_SCSI, "Receiving CDB", cmd->cdb, cmd->cdb_len); + PRINT_BUFF_FLAG(TRACE_SCSI, "CDB", cmd->cdb, cmd->cdb_len); #ifdef CONFIG_SCST_EXTRACHECKS if (unlikely((in_irq() || irqs_disabled())) && @@ -570,19 +570,19 @@ int scst_pre_parse(struct scst_cmd *cmd) #else cmd->inc_expected_sn_on_done = devt->exec_sync || (!dev->has_own_order_mgmt && - (dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER || + (dev->queue_alg == SCST_QUEUE_ALG_0_RESTRICTED_REORDER || cmd->queue_type == SCST_CMD_QUEUE_ORDERED)); #endif TRACE_DBG("op_name <%s> (cmd %p), direction=%d " "(expected %d, set %s), lba %lld, bufflen=%d, data_len %lld, " "out_bufflen=%d (expected len %d, out expected len %d), " - "flags=0x%x", cmd->op_name, cmd, cmd->data_direction, + "flags=0x%x, naca %d", cmd->op_name, cmd, cmd->data_direction, cmd->expected_data_direction, scst_cmd_is_expected_set(cmd) ? "yes" : "no", (long long)cmd->lba, cmd->bufflen, (long long)cmd->data_len, cmd->out_bufflen, cmd->expected_transfer_len, - cmd->expected_out_transfer_len, cmd->op_flags); + cmd->expected_out_transfer_len, cmd->op_flags, cmd->cmd_naca); res = 0; @@ -700,8 +700,8 @@ static int scst_parse_cmd(struct scst_cmd *cmd) if (unlikely(cmd->cdb_len == 0)) { PRINT_ERROR("Unable to get CDB length for " - "opcode 0x%02x. Returning INVALID " - "OPCODE", cmd->cdb[0]); + "opcode %s. Returning INVALID " + "OPCODE", scst_get_opcode_name(cmd)); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); goto out_done; @@ -726,8 +726,9 @@ static int scst_parse_cmd(struct scst_cmd *cmd) } else { if (cmd->bufflen == 0) { PRINT_ERROR("Unknown data transfer length for opcode " - "0x%x (handler %s, target %s)", cmd->cdb[0], - devt->name, cmd->tgtt->name); + "%s (handler %s, target %s)", + scst_get_opcode_name(cmd), devt->name, + cmd->tgtt->name); PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_message)); @@ -747,7 +748,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd) if (unlikely(cmd->cmd_linked)) { PRINT_ERROR("Linked commands are not supported " - "(opcode 0x%02x)", cmd->cdb[0]); + "(opcode %s)", scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, cmd->cdb_len-1, SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); goto out_done; @@ -768,9 +769,9 @@ static int scst_parse_cmd(struct scst_cmd *cmd) ((cmd->sg == NULL) && (state > SCST_CMD_STATE_PREPARE_SPACE)))) { PRINT_ERROR("Dev handler %s parse() returned " "invalid cmd data_direction %d, bufflen %d, state %d " - "or sg %p (opcode 0x%x)", devt->name, + "or sg %p (opcode %s)", devt->name, cmd->data_direction, cmd->bufflen, state, cmd->sg, - cmd->cdb[0]); + scst_get_opcode_name(cmd)); PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len); goto out_hw_error; } @@ -805,10 +806,10 @@ static int scst_parse_cmd(struct scst_cmd *cmd) (cmd->bufflen != 0)) && !scst_is_allowed_to_mismatch_cmd(cmd)) { PRINT_ERROR("Expected data direction %d for " - "opcode 0x%02x (handler %s, target %s) " + "opcode %s (handler %s, target %s) " "doesn't match decoded value %d", cmd->expected_data_direction, - cmd->cdb[0], devt->name, + scst_get_opcode_name(cmd), devt->name, cmd->tgtt->name, cmd->data_direction); PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len); @@ -819,10 +820,10 @@ static int scst_parse_cmd(struct scst_cmd *cmd) } if (unlikely(cmd->bufflen != cmd->expected_transfer_len)) { TRACE(TRACE_MINOR, "Warning: expected " - "transfer length %d for opcode 0x%02x " + "transfer length %d for opcode %s " "(handler %s, target %s) doesn't match " "decoded value %d", - cmd->expected_transfer_len, cmd->cdb[0], + cmd->expected_transfer_len, scst_get_opcode_name(cmd), devt->name, cmd->tgtt->name, cmd->bufflen); PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", cmd->cdb, cmd->cdb_len); @@ -832,12 +833,12 @@ static int scst_parse_cmd(struct scst_cmd *cmd) } if (unlikely(cmd->out_bufflen != cmd->expected_out_transfer_len)) { TRACE(TRACE_MINOR, "Warning: expected bidirectional OUT " - "transfer length %d for opcode 0x%02x " + "transfer length %d for opcode %s " "(handler %s, target %s) doesn't match " "decoded value %d", - cmd->expected_out_transfer_len, cmd->cdb[0], - devt->name, cmd->tgtt->name, - cmd->out_bufflen); + cmd->expected_out_transfer_len, + scst_get_opcode_name(cmd), devt->name, + cmd->tgtt->name, cmd->out_bufflen); PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", cmd->cdb, cmd->cdb_len); cmd->resid_possible = 1; @@ -846,16 +847,17 @@ static int scst_parse_cmd(struct scst_cmd *cmd) } if (unlikely(cmd->data_direction == SCST_DATA_UNKNOWN)) { - PRINT_ERROR("Unknown data direction. Opcode 0x%x, handler %s, " - "target %s", cmd->cdb[0], devt->name, + PRINT_ERROR("Unknown data direction (opcode %s, handler %s, " + "target %s)", scst_get_opcode_name(cmd), devt->name, cmd->tgtt->name); PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len); goto out_hw_error; } if (unlikely(cmd->op_flags & SCST_UNKNOWN_LBA)) { - PRINT_ERROR("Unknown LBA (opcode 0x%x, handler %s, " - "target %s)", cmd->cdb[0], devt->name, cmd->tgtt->name); + PRINT_ERROR("Unknown LBA (opcode %s, handler %s, " + "target %s)", scst_get_opcode_name(cmd), devt->name, + cmd->tgtt->name); PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len); goto out_hw_error; } @@ -872,13 +874,13 @@ set_res: TRACE(TRACE_SCSI, "op_name <%s> (cmd %p), direction=%d " "(expected %d, set %s), lba=%lld, bufflen=%d, data len %lld, " "out_bufflen=%d, (expected len %d, out expected len %d), " - "flags=0x%x, internal %d", cmd->op_name, cmd, + "flags=0x%x, internal %d, naca %d", cmd->op_name, cmd, cmd->data_direction, cmd->expected_data_direction, scst_cmd_is_expected_set(cmd) ? "yes" : "no", (unsigned long long)cmd->lba, cmd->bufflen, (long long)cmd->data_len, cmd->out_bufflen, cmd->expected_transfer_len, cmd->expected_out_transfer_len, - cmd->op_flags, cmd->internal); + cmd->op_flags, cmd->internal, cmd->cmd_naca); #ifdef CONFIG_SCST_EXTRACHECKS switch (state) { @@ -893,7 +895,8 @@ set_res: case SCST_CMD_STATE_REAL_EXEC: case SCST_CMD_STATE_PRE_DEV_DONE: case SCST_CMD_STATE_DEV_DONE: - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + case SCST_CMD_STATE_PRE_XMIT_RESP2: case SCST_CMD_STATE_XMIT_RESP: case SCST_CMD_STATE_FINISHED: case SCST_CMD_STATE_FINISHED_INTERNAL: @@ -906,12 +909,12 @@ set_res: default: if (state >= 0) { PRINT_ERROR("Dev handler %s parse() returned " - "invalid cmd state %d (opcode %d)", - devt->name, state, cmd->cdb[0]); + "invalid cmd state %d (opcode %s)", + devt->name, state, scst_get_opcode_name(cmd)); } else { PRINT_ERROR("Dev handler %s parse() returned " - "error %d (opcode %d)", devt->name, - state, cmd->cdb[0]); + "error %d (opcode %s)", devt->name, + state, scst_get_opcode_name(cmd)); } goto out_hw_error; } @@ -989,12 +992,10 @@ out: break; } if (abort) { - TRACE_MGMT_DBG("Black hole: aborting cmd %p (op %x, " - "initiator %s)", cmd, cmd->cdb[0], + TRACE_MGMT_DBG("Black hole: aborting cmd %p (op %s, " + "initiator %s)", cmd, scst_get_opcode_name(cmd), sess->initiator_name); - spin_lock_irq(&sess->sess_list_lock); scst_abort_cmd(cmd, NULL, false, false); - spin_unlock_irq(&sess->sess_list_lock); } } @@ -1304,7 +1305,9 @@ void scst_restart_cmd(struct scst_cmd *cmd, int status, break; case SCST_PREPROCESS_STATUS_ERROR_FATAL: + set_bit(SCST_CMD_ABORTED, &cmd->cmd_flags); set_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags); + cmd->delivery_status = SCST_CMD_DELIVERY_FAILED; /* go through */ case SCST_PREPROCESS_STATUS_ERROR: if (cmd->sense != NULL) @@ -1515,7 +1518,6 @@ void scst_rx_data(struct scst_cmd *cmd, int status, scst_set_rdy_to_xfer_time(cmd); TRACE_DBG("Preferred context: %d", pref_context); - TRACE(TRACE_SCSI, "cmd %p, status %#x", cmd, status); cmd->cmd_hw_pending = 0; @@ -1540,7 +1542,10 @@ void scst_rx_data(struct scst_cmd *cmd, int status, break; #endif - /* Small context optimization */ + /* + * Make sure that the exec phase runs in thread context since + * invoking I/O functions from atomic context is not allowed. + */ if ((pref_context == SCST_CONTEXT_TASKLET) || (pref_context == SCST_CONTEXT_DIRECT_ATOMIC) || ((pref_context == SCST_CONTEXT_SAME) && @@ -1549,6 +1554,7 @@ void scst_rx_data(struct scst_cmd *cmd, int status, break; case SCST_RX_STATUS_ERROR_SENSE_SET: + TRACE(TRACE_SCSI, "cmd %p, RX data error status %#x", cmd, status); if (!cmd->write_not_received_set) scst_cmd_set_write_no_data_received(cmd); scst_set_cmd_abnormal_done_state(cmd); @@ -1556,9 +1562,12 @@ void scst_rx_data(struct scst_cmd *cmd, int status, break; case SCST_RX_STATUS_ERROR_FATAL: + set_bit(SCST_CMD_ABORTED, &cmd->cmd_flags); set_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags); + cmd->delivery_status = SCST_CMD_DELIVERY_FAILED; /* go through */ case SCST_RX_STATUS_ERROR: + TRACE(TRACE_SCSI, "cmd %p, RX data error status %#x", cmd, status); if (!cmd->write_not_received_set) scst_cmd_set_write_no_data_received(cmd); scst_set_cmd_error(cmd, @@ -1676,7 +1685,9 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd) scst_set_cmd_abnormal_done_state(cmd); goto out; case SCST_PREPROCESS_STATUS_ERROR_FATAL: + set_bit(SCST_CMD_ABORTED, &cmd->cmd_flags); set_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags); + cmd->delivery_status = SCST_CMD_DELIVERY_FAILED; /* go through */ case SCST_PREPROCESS_STATUS_ERROR: scst_set_cmd_error(cmd, @@ -1718,7 +1729,7 @@ static void scst_do_cmd_done(struct scst_cmd *cmd, int result, scst_set_resp_data_len(cmd, cmd->resp_data_len - resid); /* * We ignore write direction residue, because from the - * initiator's POV we already transferred all the data. + * initiator's POV we have already transferred all the data. */ } @@ -1805,11 +1816,12 @@ static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state, #ifdef CONFIG_SCST_EXTRACHECKS if ((next_state != SCST_CMD_STATE_PRE_DEV_DONE) && - (next_state != SCST_CMD_STATE_PRE_XMIT_RESP) && + (next_state != SCST_CMD_STATE_PRE_XMIT_RESP1) && + (next_state != SCST_CMD_STATE_PRE_XMIT_RESP2) && (next_state != SCST_CMD_STATE_FINISHED) && (next_state != SCST_CMD_STATE_FINISHED_INTERNAL)) { - PRINT_ERROR("%s() received invalid cmd state %d (opcode %d)", - __func__, next_state, cmd->cdb[0]); + PRINT_ERROR("%s() received invalid cmd state %d (opcode %s)", + __func__, next_state, scst_get_opcode_name(cmd)); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); scst_set_cmd_abnormal_done_state(cmd); @@ -1842,15 +1854,18 @@ static int scst_report_luns_local(struct scst_cmd *cmd) if ((cmd->cdb[2] != 0) && (cmd->cdb[2] != 2)) { PRINT_ERROR("Unsupported SELECT REPORT value %x in REPORT " "LUNS command", cmd->cdb[2]); - goto out_err; + scst_set_invalid_field_in_cdb(cmd, 2, 0); + goto out_compl; } buffer_size = scst_get_buf_full_sense(cmd, &buffer); if (unlikely(buffer_size <= 0)) goto out_compl; - if (buffer_size < 16) + if (buffer_size < 16) { + scst_set_invalid_field_in_cdb(cmd, 6, 0); goto out_put_err; + } memset(buffer, 0, buffer_size); offs = 8; @@ -1928,10 +1943,6 @@ out_compl: out_put_err: scst_put_buf_full(cmd, buffer); - -out_err: - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); goto out_compl; } @@ -2025,8 +2036,8 @@ static int scst_request_sense_local(struct scst_cmd *cmd) sl = tgt_dev->tgt_dev_valid_sense_len; else { sl = buffer_size; - TRACE(TRACE_MINOR, "%s: Being returned sense truncated " - "to size %d (needed %d)", cmd->op_name, + TRACE(TRACE_SCSI|TRACE_MINOR, "%s: Being returned sense " + "truncated to size %d (needed %d)", cmd->op_name, buffer_size, tgt_dev->tgt_dev_valid_sense_len); } memcpy(buffer, tgt_dev->tgt_dev_sense, sl); @@ -2124,20 +2135,15 @@ static int scst_report_supported_opcodes(struct scst_cmd *cmd) int req_sa = get_unaligned_be16(&cmd->cdb[4]); const struct scst_opcode_descriptor *op = NULL; const struct scst_opcode_descriptor **supp_opcodes = NULL; - int supp_opcodes_cnt; + int supp_opcodes_cnt, rc; TRACE_ENTRY(); - if (cmd->devt->get_supported_opcodes == NULL) { - TRACE(TRACE_MINOR, "Unknown opcode 0x%02x", cmd->cdb[0]); - scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + /* get_cdb_info_min() ensures that get_supported_opcodes is not NULL here */ + + rc = cmd->devt->get_supported_opcodes(cmd, &supp_opcodes, &supp_opcodes_cnt); + if (rc != 0) goto out_compl; - } else { - int rc = cmd->devt->get_supported_opcodes(cmd, &supp_opcodes, - &supp_opcodes_cnt); - if (rc != 0) - goto out_compl; - } TRACE_DBG("cmd %p, options %d, req_opcode %x, req_sa %x, rctd %d", cmd, options, req_opcode, req_sa, rctd); @@ -2243,7 +2249,7 @@ static int scst_report_supported_opcodes(struct scst_cmd *cmd) switch (options) { case 0: /* all */ - put_unaligned_be32(buf_len - 3, &buf[0]); + put_unaligned_be32(buf_len - 4, &buf[0]); offs = 4; for (i = 0; i < supp_opcodes_cnt; i++) { op = supp_opcodes[i]; @@ -2354,7 +2360,7 @@ static int scst_reserve_local(struct scst_cmd *cmd) /* * There's no need to block this device, even for - * SCST_CONTR_MODE_ONE_TASK_SET, or anyhow else protect reservations + * SCST_TST_0_SINGLE_TASK_SET, or anyhow else protect reservations * changes, because: * * 1. The reservation changes are (rather) atomic, i.e., in contrast @@ -2363,7 +2369,7 @@ static int scst_reserve_local(struct scst_cmd *cmd) * * 2. It's a duty of initiators to ensure order of regular commands * around the reservation command either by ORDERED attribute, or by - * queue draining, or etc. For case of SCST_CONTR_MODE_ONE_TASK_SET + * queue draining, or etc. For case of SCST_TST_0_SINGLE_TASK_SET * there are no target drivers which can ensure even for ORDERED * commands order of their delivery, so, because initiators know * it, also there's no point to do any extra protection actions. @@ -2487,9 +2493,10 @@ static int scst_persistent_reserve_in_local(struct scst_cmd *cmd) session = cmd->sess; if (unlikely(dev->not_pr_supporting_tgt_devs_num != 0)) { - PRINT_WARNING("Persistent Reservation command %x refused for " + PRINT_WARNING("Persistent Reservation command %s refused for " "device %s, because the device has not supporting PR " - "transports connected", cmd->cdb[0], dev->virt_name); + "transports connected", scst_get_opcode_name(cmd), + dev->virt_name); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); goto out_done; @@ -2524,8 +2531,8 @@ static int scst_persistent_reserve_in_local(struct scst_cmd *cmd) action = cmd->cdb[1] & 0x1f; - TRACE(TRACE_SCSI, "PR action %x for '%s' (LUN %llx) from '%s'", action, - dev->virt_name, tgt_dev->lun, session->initiator_name); + TRACE(TRACE_SCSI, "PR IN action %x for '%s' (LUN %llx) from '%s'", + action, dev->virt_name, tgt_dev->lun, session->initiator_name); switch (action) { case PR_READ_KEYS: @@ -2586,18 +2593,18 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) session = cmd->sess; if (unlikely(dev->not_pr_supporting_tgt_devs_num != 0)) { - PRINT_WARNING("Persistent Reservation command %x refused for " + PRINT_WARNING("Persistent Reservation command %s refused for " "device %s, because the device has not supporting PR " - "transports connected", cmd->cdb[0], dev->virt_name); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + "transports connected", scst_get_opcode_name(cmd), + dev->virt_name); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); goto out_done; } action = cmd->cdb[1] & 0x1f; - TRACE(TRACE_SCSI, "PR action %x for '%s' (LUN %llx) from '%s'", action, - dev->virt_name, tgt_dev->lun, session->initiator_name); + TRACE(TRACE_SCSI, "PR OUT action %x for '%s' (LUN %llx) from '%s'", + action, dev->virt_name, tgt_dev->lun, session->initiator_name); if (scst_dev_reserved(dev)) { TRACE_PR("PR command rejected, because device %s holds regular " @@ -2630,8 +2637,8 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) if ((action != PR_REGISTER) && (action != PR_REGISTER_AND_IGNORE) && (action != PR_CLEAR) && (cmd->cdb[2] >> 4) != SCOPE_LU) { TRACE_PR("Scope must be SCOPE_LU for action %x", action); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 4); goto out_unlock; } @@ -2639,8 +2646,8 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) if ((action != PR_REGISTER) && (action != PR_REGISTER_AND_MOVE) && ((buffer[20] >> 3) & 0x01)) { TRACE_PR("SPEC_I_PT must be zero for action %x", action); - scst_set_cmd_error(cmd, SCST_LOAD_SENSE( - scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_parm_list(cmd, 20, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 3); goto out_unlock; } @@ -2648,8 +2655,8 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) if ((action != PR_REGISTER) && (action != PR_REGISTER_AND_IGNORE) && (action != PR_REGISTER_AND_MOVE) && ((buffer[20] >> 2) & 0x01)) { TRACE_PR("ALL_TG_PT must be zero for action %x", action); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_parm_list(cmd, 20, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 2); goto out_unlock; } @@ -3000,10 +3007,11 @@ static int scst_do_real_exec(struct scst_cmd *cmd) if (rc == -EINVAL && (cmd->bufflen >> 9) > queue_max_hw_sectors(scsi_dev->request_queue)) PRINT_ERROR("Too low max_hw_sectors %d sectors on %s " - "to serve command %#x with bufflen %d bytes." + "to serve command %s with bufflen %d bytes." "See README for more details.", queue_max_hw_sectors(scsi_dev->request_queue), - dev->virt_name, cmd->cdb[0], cmd->bufflen); + dev->virt_name, scst_get_opcode_name(cmd), + cmd->bufflen); goto out_error; } @@ -3090,8 +3098,9 @@ static int scst_do_local_exec(struct scst_cmd *cmd) if ((cmd->op_flags & SCST_WRITE_MEDIUM) && (tgt_dev->tgt_dev_rd_only || cmd->dev->swp)) { PRINT_WARNING("Attempt of write access to read-only device: " - "initiator %s, LUN %lld, op %x", - cmd->sess->initiator_name, cmd->lun, cmd->cdb[0]); + "initiator %s, LUN %lld, op %s", + cmd->sess->initiator_name, cmd->lun, + scst_get_opcode_name(cmd)); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_data_protect)); goto out_done; @@ -3294,7 +3303,7 @@ static int scst_exec_check_sn(struct scst_cmd **active_cmd) if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)) goto exec; - sBUG_ON(!cmd->sn_set); + EXTRACHECKS_BUG_ON(!cmd->sn_set); expected_sn = ACCESS_ONCE(order_data->expected_sn); /* Optimized for lockless fast path */ @@ -3330,7 +3339,7 @@ static int scst_exec_check_sn(struct scst_cmd **active_cmd) TRACE_SN("Deferring cmd %p (sn=%d, set %d, " "expected_sn=%d)", cmd, cmd->sn, cmd->sn_set, expected_sn); - list_add_tail(&cmd->sn_cmd_list_entry, + list_add_tail(&cmd->deferred_cmd_list_entry, &order_data->deferred_cmd_list); res = SCST_CMD_STATE_RES_CONT_NEXT; } @@ -3360,8 +3369,11 @@ static int scst_check_sense(struct scst_cmd *cmd) TRACE_ENTRY(); - if (unlikely(cmd->ua_ignore)) + if (unlikely(cmd->ua_ignore)) { + PRINT_BUFF_FLAG(TRACE_SCSI, "Local UA sense", cmd->sense, + cmd->sense_valid_len); goto out; + } /* If we had internal bus reset behind us, set the command error UA */ if ((dev->scsi_dev != NULL) && @@ -3466,8 +3478,8 @@ static bool scst_check_auto_sense(struct scst_cmd *cmd) } else { TRACE(TRACE_SCSI|TRACE_MINOR_AND_MGMT_DBG, "Host " "status 0x%x received, returning HARDWARE ERROR " - "instead (cmd %p, op 0x%x, target %s, device " - "%s)", cmd->host_status, cmd, cmd->cdb[0], + "instead (cmd %p, op %s, target %s, device " + "%s)", cmd->host_status, cmd, scst_get_opcode_name(cmd), cmd->tgt->tgt_name, cmd->dev->virt_name); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); @@ -3484,10 +3496,11 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) TRACE_ENTRY(); - if (unlikely(scst_check_auto_sense(cmd))) { + rc = scst_check_auto_sense(cmd); + if (unlikely(rc)) { PRINT_INFO("Command finished with CHECK CONDITION, but " - "without sense data (opcode 0x%x), issuing " - "REQUEST SENSE", cmd->cdb[0]); + "without sense data (opcode %s), issuing " + "REQUEST SENSE", scst_get_opcode_name(cmd)); rc = scst_prepare_request_sense(cmd); if (rc == 0) res = SCST_CMD_STATE_RES_CONT_NEXT; @@ -3498,7 +3511,10 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) SCST_LOAD_SENSE(scst_sense_hardw_error)); } goto out; - } else if (unlikely(scst_check_sense(cmd))) { + } + + rc = scst_check_sense(cmd); + if (unlikely(rc)) { /* * We can't allow atomic command on the exec stages, so * restart to the thread @@ -3507,7 +3523,8 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) goto out; } - if (likely(scsi_status_is_good(cmd->status))) { + rc = scsi_status_is_good(cmd->status); + if (likely(rc)) { unsigned char type = cmd->dev->type; if (unlikely((cmd->cdb[0] == MODE_SENSE || cmd->cdb[0] == MODE_SENSE_10)) && @@ -3580,33 +3597,12 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) if (unlikely((cmd->cdb[0] == MODE_SELECT) || (cmd->cdb[0] == MODE_SELECT_10) || (cmd->cdb[0] == LOG_SELECT))) { - TRACE(TRACE_SCSI, - "MODE/LOG SELECT succeeded (LUN %lld)", + TRACE(TRACE_SCSI, "MODE/LOG SELECT succeeded (LUN %lld)", (long long unsigned int)cmd->lun); cmd->state = SCST_CMD_STATE_MODE_SELECT_CHECKS; goto out; } } else { - TRACE(TRACE_SCSI, "cmd %p not succeeded with status %x", - cmd, cmd->status); - - if ((cmd->cdb[0] == RESERVE) || (cmd->cdb[0] == RESERVE_10)) { - struct scst_device *dev = cmd->dev; - - if (scst_is_reservation_holder(dev, cmd->sess)) { - TRACE(TRACE_SCSI, "RESERVE failed lun=%lld, " - "status=%x", - (long long unsigned int)cmd->lun, - cmd->status); - PRINT_BUFF_FLAG(TRACE_SCSI, "Sense", cmd->sense, - cmd->sense_valid_len); - - spin_lock_bh(&dev->dev_lock); - scst_clear_dev_reservation(dev); - spin_unlock_bh(&dev->dev_lock); - } - } - /* Check for MODE PARAMETERS CHANGED UA */ if ((cmd->dev->scsi_dev != NULL) && (cmd->status == SAM_STAT_CHECK_CONDITION) && @@ -3719,7 +3715,7 @@ static int scst_dev_done(struct scst_cmd *cmd) TRACE_ENTRY(); - state = SCST_CMD_STATE_PRE_XMIT_RESP; + state = SCST_CMD_STATE_PRE_XMIT_RESP1; if (likely((cmd->op_flags & SCST_FULLY_LOCAL_CMD) == 0) && likely(devt->dev_done != NULL)) { @@ -3750,7 +3746,8 @@ static int scst_dev_done(struct scst_cmd *cmd) switch (state) { #ifdef CONFIG_SCST_EXTRACHECKS - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + case SCST_CMD_STATE_PRE_XMIT_RESP2: case SCST_CMD_STATE_PARSE: case SCST_CMD_STATE_PREPARE_SPACE: case SCST_CMD_STATE_RDY_TO_XFER: @@ -3805,7 +3802,8 @@ static int scst_dev_done(struct scst_cmd *cmd) cmd->state = SCST_CMD_STATE_FINISHED_INTERNAL; #ifndef CONFIG_SCST_TEST_IO_IN_SIRQ - if (cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP) { +#ifdef CONFIG_SCST_EXTRACHECKS + if (cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP1) { /* We can't allow atomic command on the exec stages */ if (scst_cmd_atomic(cmd)) { switch (state) { @@ -3822,13 +3820,58 @@ static int scst_dev_done(struct scst_cmd *cmd) } } #endif +#endif out: TRACE_EXIT_HRES(res); return res; } -static int scst_pre_xmit_response(struct scst_cmd *cmd) +static int scst_pre_xmit_response2(struct scst_cmd *cmd) +{ + int res; + + TRACE_ENTRY(); + +again: + if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) + scst_xmit_process_aborted_cmd(cmd); + else if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION)) { + if (cmd->tgt_dev != NULL) { + int rc = scst_process_check_condition(cmd); + /* !! At this point cmd can be already dead !! */ + if (rc == -1) { + res = SCST_CMD_STATE_RES_CONT_NEXT; + goto out; + } else if (rc == 1) + goto again; + } + } + + if (unlikely(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags))) { + EXTRACHECKS_BUG_ON(!test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)); + TRACE_MGMT_DBG("Flag NO_RESP set for cmd %p (tag %llu), " + "skipping", cmd, (long long unsigned int)cmd->tag); + cmd->state = SCST_CMD_STATE_FINISHED; + goto out_same; + } + + if (unlikely(cmd->resid_possible)) + scst_adjust_resp_data_len(cmd); + else + cmd->adjusted_resp_data_len = cmd->resp_data_len; + + cmd->state = SCST_CMD_STATE_XMIT_RESP; + +out_same: + res = SCST_CMD_STATE_RES_CONT_SAME; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_pre_xmit_response1(struct scst_cmd *cmd) { int res; @@ -3838,7 +3881,7 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd) #ifdef CONFIG_SCST_DEBUG_TM if (cmd->tm_dbg_delayed && - !test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) { + !test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) { if (scst_cmd_atomic(cmd)) { TRACE_MGMT_DBG("%s", "DEBUG_TM delayed cmd needs a thread"); @@ -3876,29 +3919,10 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd) cmd->done = 1; smp_mb(); /* to sync with scst_abort_cmd() */ - if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) - scst_xmit_process_aborted_cmd(cmd); - else if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION)) - scst_store_sense(cmd); + cmd->state = SCST_CMD_STATE_PRE_XMIT_RESP2; + res = scst_pre_xmit_response2(cmd); - if (unlikely(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags))) { - TRACE_MGMT_DBG("Flag NO_RESP set for cmd %p (tag %llu), " - "skipping", cmd, (long long unsigned int)cmd->tag); - cmd->state = SCST_CMD_STATE_FINISHED; - res = SCST_CMD_STATE_RES_CONT_SAME; - goto out; - } - - if (unlikely(cmd->resid_possible)) - scst_adjust_resp_data_len(cmd); - else - cmd->adjusted_resp_data_len = cmd->resp_data_len; - - cmd->state = SCST_CMD_STATE_XMIT_RESP; - res = SCST_CMD_STATE_RES_CONT_SAME; - -out: - TRACE_EXIT_HRES(res); + TRACE_EXIT_RES(res); return res; } @@ -4069,6 +4093,7 @@ static int scst_finish_cmd(struct scst_cmd *cmd) if (unlikely(cmd->delivery_status != SCST_CMD_DELIVERY_SUCCESS)) { if ((cmd->tgt_dev != NULL) && + (cmd->status == SAM_STAT_CHECK_CONDITION) && scst_is_ua_sense(cmd->sense, cmd->sense_valid_len)) { /* This UA delivery failed, so we need to requeue it */ if (scst_cmd_atomic(cmd) && @@ -4173,7 +4198,7 @@ static void scst_cmd_set_sn(struct scst_cmd *cmd) cmd->queue_type = SCST_CMD_QUEUE_ORDERED; #endif - if (cmd->dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER) { + if (cmd->dev->queue_alg == SCST_QUEUE_ALG_0_RESTRICTED_REORDER) { if (likely(cmd->queue_type != SCST_CMD_QUEUE_HEAD_OF_QUEUE)) { /* * Not the best way, but good enough until there is a @@ -4228,7 +4253,7 @@ again: goto again; case SCST_CMD_QUEUE_ORDERED: - TRACE_SN("ORDERED cmd %p (op %x)", cmd, cmd->cdb[0]); + TRACE_SN("ORDERED cmd %p (op %s)", cmd, scst_get_opcode_name(cmd)); ordered: order_data->curr_sn++; TRACE_SN("Incremented curr_sn %d", order_data->curr_sn); @@ -4271,7 +4296,7 @@ ordered: break; case SCST_CMD_QUEUE_HEAD_OF_QUEUE: - TRACE_SN("HQ cmd %p (op %x)", cmd, cmd->cdb[0]); + TRACE_SN("HQ cmd %p (op %s)", cmd, scst_get_opcode_name(cmd)); spin_lock_irqsave(&order_data->sn_lock, flags); order_data->hq_cmd_count++; spin_unlock_irqrestore(&order_data->sn_lock, flags); @@ -4762,10 +4787,14 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic) res = scst_dev_done(cmd); break; - case SCST_CMD_STATE_PRE_XMIT_RESP: - res = scst_pre_xmit_response(cmd); - EXTRACHECKS_BUG_ON(res == - SCST_CMD_STATE_RES_NEED_THREAD); + case SCST_CMD_STATE_PRE_XMIT_RESP1: + res = scst_pre_xmit_response1(cmd); + EXTRACHECKS_BUG_ON(res == SCST_CMD_STATE_RES_NEED_THREAD); + break; + + case SCST_CMD_STATE_PRE_XMIT_RESP2: + res = scst_pre_xmit_response2(cmd); + EXTRACHECKS_BUG_ON(res == SCST_CMD_STATE_RES_NEED_THREAD); break; case SCST_CMD_STATE_XMIT_RESP: @@ -4784,8 +4813,11 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic) PRINT_CRIT_ERROR("cmd (%p) in state %d, but shouldn't " "be", cmd, cmd->state); sBUG(); +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 < 6 + /* For suppressing a gcc compiler warning */ res = SCST_CMD_STATE_RES_CONT_NEXT; break; +#endif } } while (res == SCST_CMD_STATE_RES_CONT_SAME); @@ -4817,8 +4849,10 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic) cmd->state); spin_unlock_irq(&cmd->cmd_threads->cmd_list_lock); sBUG(); +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 < 6 spin_lock_irq(&cmd->cmd_threads->cmd_list_lock); break; +#endif } #endif wake_up(&cmd->cmd_threads->cmd_list_waitQ); @@ -4918,21 +4952,16 @@ void scst_cmd_tasklet(long p) } /* - * Returns 0 on success, < 0 if there is no device handler or - * > 0 if SCST_FLAG_SUSPENDED set and SCST_FLAG_SUSPENDING - not. - * No locks, protection is done by the suspended activity. + * Returns 0 on success, or > 0 if SCST_FLAG_SUSPENDED set and + * SCST_FLAG_SUSPENDING - not. No locks, protection is done by the + * suspended activity. */ -static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd) +static int scst_get_mgmt(struct scst_mgmt_cmd *mcmd) { - struct scst_tgt_dev *tgt_dev; - struct list_head *head; - int res = -1; + int res = 0; TRACE_ENTRY(); - TRACE_DBG("Finding tgt_dev for mgmt cmd %p (lun %lld)", mcmd, - (long long unsigned int)mcmd->lun); - mcmd->cpu_cmd_counter = scst_get(); if (unlikely(test_bit(SCST_FLAG_SUSPENDED, &scst_flags) && @@ -4943,6 +4972,33 @@ static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd) goto out; } +out: + TRACE_EXIT_HRES(res); + return res; +} + +/* + * Returns 0 on success, < 0 if there is no device handler or + * > 0 if SCST_FLAG_SUSPENDED set and SCST_FLAG_SUSPENDING - not. + * No locks, protection is done by the suspended activity. + */ +static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd) +{ + struct scst_tgt_dev *tgt_dev; + struct list_head *head; + int res; + + TRACE_ENTRY(); + + TRACE_DBG("Finding tgt_dev for mgmt cmd %p (lun %lld)", mcmd, + (long long unsigned int)mcmd->lun); + + res = scst_get_mgmt(mcmd); + if (unlikely(res != 0)) + goto out; + + res = -1; + head = &mcmd->sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(mcmd->lun)]; list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { if (tgt_dev->lun == mcmd->lun) { @@ -5208,8 +5264,8 @@ static inline int scst_is_strict_mgmt_fn(int mgmt_fn) } /* - * Must be called under sess_list_lock to sync with finished flag assignment in - * scst_finish_cmd() + * If mcmd != NULL, must be called under sess_list_lock to sync with "finished" + * flag assignment in scst_finish_cmd() */ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, bool other_ini, bool call_dev_task_mgmt_fn_received) @@ -5226,8 +5282,8 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, if (call_dev_task_mgmt_fn_received) EXTRACHECKS_BUG_ON(!mcmd); - TRACE(TRACE_SCSI|TRACE_MGMT_DEBUG, "Aborting cmd %p (tag %llu, op %x)", - cmd, (long long unsigned int)cmd->tag, cmd->cdb[0]); + TRACE(TRACE_SCSI|TRACE_MGMT_DEBUG, "Aborting cmd %p (tag %llu, op %s)", + cmd, (long long unsigned int)cmd->tag, scst_get_opcode_name(cmd)); /* To protect from concurrent aborts */ spin_lock_irqsave(&other_ini_lock, flags); @@ -5331,12 +5387,12 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, t = TRACE_MGMT_DEBUG; TRACE(t, "cmd %p (tag %llu, " "sn %u) being executed/xmitted (state %d, " - "op %x, proc time %ld sec., timeout %d sec.), " + "op %s, proc time %ld sec., timeout %d sec.), " "deferring ABORT (cmd_done_wait_count %d, " "cmd_finish_wait_count %d, internal %d, mcmd " "fn %d (mcmd %p))", cmd, (long long unsigned int)cmd->tag, - cmd->sn, cmd->state, cmd->cdb[0], + cmd->sn, cmd->state, scst_get_opcode_name(cmd), (long)(jiffies - cmd->start_time) / HZ, cmd->timeout / HZ, mcmd->cmd_done_wait_count, mcmd->cmd_finish_wait_count, cmd->internal, @@ -5484,7 +5540,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt, spin_lock(&order_data->sn_lock); list_for_each_entry_safe(cmd, tcmd, &order_data->deferred_cmd_list, - sn_cmd_list_entry) { + deferred_cmd_list_entry) { if ((tgt != NULL) && (tgt != cmd->tgt)) continue; @@ -5492,7 +5548,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt, continue; if (__scst_check_unblock_aborted_cmd(cmd, - &cmd->sn_cmd_list_entry)) { + &cmd->deferred_cmd_list_entry)) { TRACE_MGMT_DBG("Unblocked aborted SN " "cmd %p (sn %u)", cmd, cmd->sn); order_data->def_cmd_count--; @@ -5679,6 +5735,18 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd) list_for_each_entry(tgt_dev, &UA_tgt_devs, extra_tgt_dev_list_entry) { + /* + * Potentially, setting UA here, when the aborted + * commands are still running, can lead to a situation + * that one of them could take it, then that would be + * detected and the UA requeued. But, meanwhile, one or + * more subsequent, i.e. not aborted, commands can + * "leak" executed normally. So, as result, the + * UA would be delivered one or more commands "later". + * However, that should be OK, because, if multiple + * commands are being executed in parallel, you can't + * control exact order of UA delivery anyway. + */ scst_check_set_UA(tgt_dev, sense_buffer, sl, 0); } } @@ -5742,6 +5810,21 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd) } case SCST_TARGET_RESET: + /* + * Needed to protect against race, when a device added after + * blocking, so unblocking then will make dev->block_count + * of the new device negative. + */ + rc = scst_get_mgmt(mcmd); + if (rc == 0) { + mcmd->state = SCST_MCMD_STATE_EXEC; + mcmd->scst_get_called = 1; + } else { + EXTRACHECKS_BUG_ON(rc < 0); + res = rc; + } + break; + case SCST_NEXUS_LOSS_SESS: case SCST_ABORT_ALL_TASKS_SESS: case SCST_NEXUS_LOSS: @@ -5950,7 +6033,7 @@ static void scst_do_nexus_loss_sess(struct scst_mgmt_cmd *mcmd) /* Returns 0 if the command processing should be continued, <0 otherwise */ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd, - int nexus_loss) + int nexus_loss_unreg_sess) { int res; int i; @@ -5959,8 +6042,8 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd, TRACE_ENTRY(); - if (nexus_loss) { - TRACE_MGMT_DBG("Nexus loss for sess %p (mcmd %p)", + if (nexus_loss_unreg_sess) { + TRACE_MGMT_DBG("Nexus loss or UNREG SESS for sess %p (mcmd %p)", sess, mcmd); } else { TRACE_MGMT_DBG("Aborting all from sess %p (mcmd %p)", @@ -6125,8 +6208,7 @@ static int scst_mgmt_cmd_exec(struct scst_mgmt_cmd *mcmd) break; case SCST_CLEAR_TASK_SET: - if (mcmd->mcmd_tgt_dev->dev->tst == - SCST_CONTR_MODE_SEP_TASK_SETS) + if (mcmd->mcmd_tgt_dev->dev->tst == SCST_TST_1_SEP_TASK_SETS) res = scst_abort_task_set(mcmd); else res = scst_clear_task_set(mcmd); @@ -6416,8 +6498,11 @@ static int scst_process_mgmt_cmd(struct scst_mgmt_cmd *mcmd) mcmd->cmd_finish_wait_count, mcmd->cmd_done_wait_count); sBUG(); +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 < 6 + /* For suppressing a gcc compiler warning */ res = -1; goto out; +#endif } } diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t index 28db54d06..10f88b048 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t @@ -274,10 +274,10 @@ sub lunTest { Dumper(undef, "luns(): Target 'no-such-target' is not available")); ok(Dumper($SCST->luns('scst_local', 'local1')), Dumper({ '0' => 'disk01', '1' => 'disk02', '2' => 'disk01', - '3' => 'disk02' }, undef)); + '3' => 'disk02' })); ok(Dumper($SCST->luns('scst_local', 'local1', 'group1')), Dumper({ '0' => 'disk01', '2' => 'disk02', '4' => 'disk01', - '6' => 'disk02' }, undef)); + '6' => 'disk02' })); ok(lunReadOnly($SCST, 'scst_local', 'local1', 0), '0'); ok($SCST->setLunAttribute(), $SCST->SCST_C_LUN_SETATTR_FAIL); @@ -302,11 +302,11 @@ sub lunTest { ok($SCST->replaceLun('scst_local', 'local1', 0, 'disk02', {}), 0); ok(Dumper($SCST->luns('scst_local', 'local1')), Dumper({ '0' => 'disk02', '1' => 'disk02', '2' => 'disk01', - '3' => 'disk02' }, undef)); + '3' => 'disk02' })); ok($SCST->replaceLun('scst_local', 'local1', 0, 'disk01', {}), 0); ok(Dumper($SCST->luns('scst_local', 'local1')), Dumper({ '0' => 'disk01', '1' => 'disk02', '2' => 'disk01', - '3' => 'disk02' }, undef)); + '3' => 'disk02' })); ok($SCST->clearLuns(undef, undef), $SCST->SCST_C_TGT_CLR_LUN_FAIL); ok($SCST->clearLuns(undef, undef, 'group1'), @@ -322,16 +322,16 @@ sub lunTest { ok($SCST->removeLun('scst_local', 'local1', '8', 'group1'), $SCST->SCST_C_GRP_NO_LUN); ok($SCST->clearLuns('scst_local', 'local1'), 0); - ok(Dumper($SCST->luns('scst_local', 'local1')), Dumper({ }, undef)); + ok(Dumper($SCST->luns('scst_local', 'local1')), Dumper({ })); ok(Dumper($SCST->luns('scst_local', 'local1', 'group1')), Dumper({ '0' => 'disk01', '2' => 'disk02', '4' => 'disk01', - '6' => 'disk02' }, undef)); + '6' => 'disk02' })); ok($SCST->removeLun('scst_local', 'local1', '4', 'group1'), 0); ok(Dumper($SCST->luns('scst_local', 'local1', 'group1')), - Dumper({ '0' => 'disk01', '2' => 'disk02', '6' => 'disk02' }, undef)); + Dumper({ '0' => 'disk01', '2' => 'disk02', '6' => 'disk02' })); ok($SCST->clearLuns('scst_local', 'local1', 'group1'), 0); ok(Dumper($SCST->luns('scst_local', 'local1', 'group1')), - Dumper({ }, undef)); + Dumper({ })); ok($SCST->removeInitiator('scst_local', 'local1', 'group1', 'ini1'), 0); ok($SCST->removeGroup('scst_local', 'local1', 'group1'), 0); diff --git a/srpt/Makefile b/srpt/Makefile index 2f0e28c96..a97d930df 100644 --- a/srpt/Makefile +++ b/srpt/Makefile @@ -47,19 +47,30 @@ SRC_FILES=$(wildcard */*.[ch]) MODULE_SYMVERS:=$(shell if [ -e $(KDIR)/Module.symvers ]; then \ echo Module.symvers; else echo Modules.symvers; fi) -# Whether or not the OFED kernel modules have been installed. -OFED_KERNEL_IB_RPM_INSTALLED:=$(shell if rpm -q kernel-ib 2>/dev/null | grep -q $$(uname -r | sed 's/-/_/g'); then echo true; else echo false; fi) +# Name of the OFED kernel RPM. +OFED_KERNEL_IB_RPM:=$(shell for r in kernel-ib mlnx-ofa_kernel compat-rdma; do rpm -q $$r 2>/dev/null | grep -q "^$$r" && echo $$r && break; done) -# Whether or not the OFED kernel-ib-devel RPM has been installed. -OFED_KERNEL_IB_DEVEL_RPM_INSTALLED:=$(shell if rpm -q kernel-ib-devel 2>/dev/null | grep -q $$(uname -r | sed 's/-/_/g'); then echo true; else echo false; fi) +# Name of the OFED kernel development RPM. +OFED_KERNEL_IB_DEVEL_RPM:=$(shell for r in kernel-ib-devel mlnx-ofa_kernel-devel compat-rdma-devel; do rpm -q $$r 2>/dev/null | grep -q "^$$r" && echo $$r && break; done) -ifeq ($(OFED_KERNEL_IB_DEVEL_RPM_INSTALLED),true) -# Read OFED's config.mk, which contains the definition of the variable +ifeq ($(OFED_KERNEL_IB_RPM),kernel-ib) +OFED_KERNEL_DIR:=/usr/src/ofa_kernel +# Read OFED 1.x's config.mk, which contains the definition of the variable # BACKPORT_INCLUDES. -include /usr/src/ofa_kernel/config.mk +include $(OFED_KERNEL_DIR)/config.mk +OFED_CFLAGS:=$(BACKPORT_INCLUDES) -I$(OFED_KERNEL_DIR)/include +endif +ifeq ($(OFED_KERNEL_IB_RPM),mlnx-ofa_kernel) +OFED_KERNEL_DIR:=/usr/src/ofa_kernel/default +OFED_CFLAGS:=-I$(OFED_KERNEL_DIR)/default/include +endif +ifeq ($(OFED_KERNEL_IB_RPM),compat-rdma) +OFED_KERNEL_DIR:=/usr/src/compat-rdma +OFED_CFLAGS:=-I$(OFED_KERNEL_DIR)/include +endif +ifneq ($(OFED_KERNEL_IB_RPM),) +OFED_MODULE_SYMVERS:=$(OFED_KERNEL_DIR)/Module.symvers endif - -OFED_CFLAGS:=$(shell if $(OFED_KERNEL_IB_DEVEL_RPM_INSTALLED); then echo $(BACKPORT_INCLUDES) -I/usr/src/ofa_kernel/include; fi) # Path of the OFED ib_srpt.ko kernel module. OFED_SRPT_PATH:=/lib/modules/$(KVER)/updates/kernel/drivers/infiniband/ulp/srpt/ib_srpt.ko @@ -82,14 +93,15 @@ uninstall: -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) src/Module.symvers src/Modules.symvers: $(SCST_SYMVERS_DIR)/$(MODULE_SYMVERS) - @if $(OFED_KERNEL_IB_RPM_INSTALLED); then \ - if ! $(OFED_KERNEL_IB_DEVEL_RPM_INSTALLED); then \ - echo "Error: the OFED package kernel-ib-devel has not yet been" \ - "installed."; \ + @if [ -n "$(OFED_KERNEL_IB_RPM)" ]; then \ + if [ -z "$(OFED_KERNEL_IB_DEVEL_RPM)" ]; then \ + echo "Error: the OFED package $(OFED_KERNEL_IB_RPM)-devel has" \ + "not yet been installed."; \ false; \ elif [ -e /lib/modules/$(KVER)/kernel/drivers/infiniband ]; then \ echo "Error: the distro-provided InfiniBand kernel drivers" \ - "must be removed first."; \ + "must be removed first" \ + " (/lib/modules/$(KVER)/kernel/drivers/infiniband)."; \ false; \ elif $(OFED_SRPT_INSTALLED); then \ echo "Error: OFED has been built with srpt=y in ofed.conf."; \ @@ -97,21 +109,22 @@ src/Module.symvers src/Modules.symvers: $(SCST_SYMVERS_DIR)/$(MODULE_SYMVERS) false; \ elif [ -e $(KDIR)/scripts/Makefile.lib ] \ && ! grep -wq '^c_flags .*PRE_CFLAGS' \ - $(KDIR)/scripts/Makefile.lib \ + $(KDIR)/scripts/Makefile.lib \ && ! grep -wq '^LINUXINCLUDE .*PRE_CFLAGS' \ - $(KDIR)/Makefile; then \ + $(KDIR)/Makefile; then \ echo "Error: the kernel build system has not yet been patched.";\ false; \ else \ - echo " Building against OFED InfiniBand kernel headers."; \ + echo " Building against $(OFED_KERNEL_IB_RPM) InfiniBand" \ + "kernel headers."; \ ( \ grep -v drivers/infiniband/ $<; \ - cat /usr/src/ofa_kernel/Module.symvers \ + cat $(OFED_MODULE_SYMVERS) \ ) >$@; \ fi \ else \ - if $(OFED_KERNEL_IB_DEVEL_RPM_INSTALLED); then \ - echo "Error: the OFED package kernel-ib has not yet been" \ + if [ -n "$(OFED_KERNEL_IB_DEVEL_RPM)" ]; then \ + echo "Error: the OFED kernel package has not yet been" \ "installed."; \ false; \ else \ diff --git a/srpt/README b/srpt/README index f605758b8..f5940962c 100644 --- a/srpt/README +++ b/srpt/README @@ -156,7 +156,7 @@ When using RoCE or iWARP, log in to the target system to determine the id_ext and ioc_guid parameters and use these to log in. An example: [ target system ] - # sed 's/,\(pkey\|dgid\|ioc_guid\)=[^,]*//g' $(find /sys/kernel/scst_tgt/targets/ib_srpt -name login_info) | uniq + # sed 's/,\(pkey\|dgid\|service_id\)=[^,]*//g' $(find /sys/kernel/scst_tgt/targets/ib_srpt -name login_info) | uniq id_ext=0002c90300a34270,ioc_guid=0002c90300a34270 [ initiator system ] diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 17ad77e70..62acead56 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -335,6 +335,8 @@ static const char *get_ch_state_name(enum rdma_ch_state s) return "live"; case CH_DISCONNECTING: return "disconnecting"; + case CH_DRAINING: + return "draining"; case CH_DISCONNECTED: return "disconnected"; } @@ -2175,10 +2177,57 @@ static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch) ib_destroy_cq(ch->cq); } +/** + * srpt_close_ch() - Close an RDMA channel. + * + * Make sure all resources associated with the channel will be deallocated at + * an appropriate time. + * + * Returns true if and only if the channel state has been modified into + * CH_DRAINING. + */ +static bool srpt_close_ch(struct srpt_rdma_ch *ch) +{ + int ret; + + if (!srpt_set_ch_state(ch, CH_DRAINING)) + return false; + + kref_get(&ch->kref); + + ret = srpt_ch_qp_err(ch); + if (ret < 0) + PRINT_ERROR("%s: changing queue pair into error state" + " failed: %d", ch->sess_name, ret); + + ret = srpt_zerolength_write(ch); + if (ret < 0) { + PRINT_ERROR("%s: queuing zero-length write failed: %d", + ch->sess_name, ret); + WARN_ON_ONCE(!srpt_set_ch_state(ch, CH_DISCONNECTED)); + } + + kref_put(&ch->kref, srpt_free_ch); + + return true; +} + +/* + * Change the channel state into CH_DISCONNECTING. If a channel has not yet + * reached the connected state, close it. If a channel is in the connected + * state, send a DREQ. If a DREQ has been received, send a DREP. Note: it is + * the responsibility of the caller to ensure that this function is not + * invoked concurrently with the code that accepts a connection. This means + * that this function must either be invoked from inside a CM callback + * function or that it must be invoked with the srpt_tgt.mutex held. + */ static int srpt_disconnect_ch(struct srpt_rdma_ch *ch) { int ret; + if (!srpt_set_ch_state(ch, CH_DISCONNECTING)) + return -ENOTCONN; + if (ch->using_rdma_cm) { ret = rdma_disconnect(ch->rdma_cm.cm_id); } else { @@ -2187,45 +2236,12 @@ static int srpt_disconnect_ch(struct srpt_rdma_ch *ch) ret = ib_send_cm_drep(ch->ib_cm.cm_id, NULL, 0); } + if (ret < 0 && srpt_close_ch(ch)) + ret = 0; + return ret; } -/** - * srpt_close_ch() - Close an RDMA channel. - * - * Make sure all resources associated with the channel will be deallocated at - * an appropriate time. - * - * Returns true if and only if the channel state has been modified from - * CH_CONNECTING or CH_LIVE into CH_DISCONNECTING. - */ -static bool srpt_close_ch(struct srpt_rdma_ch *ch) -{ - int ret; - bool was_live; - - was_live = srpt_set_ch_state(ch, CH_DISCONNECTING); - if (was_live) { - kref_get(&ch->kref); - - ret = srpt_ch_qp_err(ch); - if (ret < 0) - PRINT_ERROR("%s: changing queue pair into error state" - " failed: %d", ch->sess_name, ret); - - ret = srpt_zerolength_write(ch); - if (ret < 0) { - PRINT_ERROR("%s: queuing zero-length write failed: %d", - ch->sess_name, ret); - WARN_ON_ONCE(!srpt_set_ch_state(ch, CH_DISCONNECTED)); - } - - kref_put(&ch->kref, srpt_free_ch); - } - - return was_live; -} - static void __srpt_close_all_ch(struct srpt_tgt *srpt_tgt) { struct srpt_nexus *nexus; @@ -2354,7 +2370,8 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, struct ib_cm_id *ib_cm_id, struct rdma_cm_id *rdma_cm_id, u8 port_num, __be16 pkey, - const struct srp_login_req *req) + const struct srp_login_req *req, + const char *src_addr) { struct srpt_port *const sport = &sdev->port[port_num - 1]; const __be16 *const raw_port_gid = (__be16 *)sport->gid.raw; @@ -2514,16 +2531,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, } if (one_target_per_port) { - snprintf(ch->sess_name, sizeof(ch->sess_name), - "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", - be16_to_cpu(raw_port_gid[0]), - be16_to_cpu(raw_port_gid[1]), - be16_to_cpu(raw_port_gid[2]), - be16_to_cpu(raw_port_gid[3]), - be16_to_cpu(raw_port_gid[4]), - be16_to_cpu(raw_port_gid[5]), - be16_to_cpu(raw_port_gid[6]), - be16_to_cpu(raw_port_gid[7])); + strlcpy(ch->sess_name, src_addr, sizeof(ch->sess_name)); } else if (use_port_guid_in_session_name) { /* * If the kernel module parameter use_port_guid_in_session_name @@ -2644,10 +2652,20 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, rep_param->ib_cm.initiator_depth = 4; } - if (ch->using_rdma_cm) - ret = rdma_accept(rdma_cm_id, &rep_param->rdma_cm); - else - ret = ib_send_cm_rep(ib_cm_id, &rep_param->ib_cm); + /* + * Hold the srpt_tgt mutex while accepting a connection to avoid that + * srpt_disconnect_ch() is invoked concurrently with this code. + */ + mutex_lock(&srpt_tgt->mutex); + if (srpt_tgt->enabled && ch->state == CH_CONNECTING) { + if (ch->using_rdma_cm) + ret = rdma_accept(rdma_cm_id, &rep_param->rdma_cm); + else + ret = ib_send_cm_rep(ib_cm_id, &rep_param->ib_cm); + } else { + ret = -EINVAL; + } + mutex_unlock(&srpt_tgt->mutex); switch (ret) { case 0: @@ -2717,9 +2735,36 @@ static int srpt_ib_cm_req_recv(struct ib_cm_id *cm_id, struct ib_cm_req_event_param *param, void *private_data) { + __be16 *const raw_sgid = (__be16 *)param->primary_path->dgid.raw; + char sgid[40]; + + scnprintf(sgid, sizeof(sgid), "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", + be16_to_cpu(raw_sgid[0]), be16_to_cpu(raw_sgid[1]), + be16_to_cpu(raw_sgid[2]), be16_to_cpu(raw_sgid[3]), + be16_to_cpu(raw_sgid[4]), be16_to_cpu(raw_sgid[5]), + be16_to_cpu(raw_sgid[6]), be16_to_cpu(raw_sgid[7])); + return srpt_cm_req_recv(cm_id->context, cm_id, NULL, param->port, param->primary_path->pkey, - private_data); + private_data, sgid); +} + +static const char *inet_ntop(const void *sa, char *dst, unsigned size) +{ + switch (((struct sockaddr *)sa)->sa_family) { + case AF_INET: + snprintf(dst, size, "%pI4", + &((struct sockaddr_in *)sa)->sin_addr); + break; + case AF_INET6: + snprintf(dst, size, "%pI6", + &((struct sockaddr_in6 *)sa)->sin6_addr); + break; + default: + snprintf(dst, size, "???"); + break; + } + return dst; } static int srpt_rdma_cm_req_recv(struct rdma_cm_id *cm_id, @@ -2728,6 +2773,7 @@ static int srpt_rdma_cm_req_recv(struct rdma_cm_id *cm_id, struct srpt_device *sdev; struct srp_login_req req; const struct srp_login_req_rdma *req_rdma; + char src_addr[40]; sdev = ib_get_client_data(cm_id->device, &srpt_client); if (!sdev) @@ -2747,13 +2793,15 @@ static int srpt_rdma_cm_req_recv(struct rdma_cm_id *cm_id, memcpy(req.initiator_port_id, req_rdma->initiator_port_id, 16); memcpy(req.target_port_id, req_rdma->target_port_id, 16); + inet_ntop(&cm_id->route.addr.src_addr, src_addr, sizeof(src_addr)); + return srpt_cm_req_recv(sdev, NULL, cm_id, cm_id->port_num, - cm_id->route.path_rec->pkey, &req); + cm_id->route.path_rec->pkey, &req, src_addr); } -static void srpt_cm_rej_recv(struct ib_cm_id *cm_id) +static void srpt_cm_rej_recv(struct srpt_rdma_ch *ch) { - PRINT_INFO("Received InfiniBand REJ packet for cm_id %p.", cm_id); + PRINT_INFO("Received CM REJ for ch %s.", ch->sess_name); } static void srpt_check_timeout(struct srpt_rdma_ch *ch) @@ -2825,13 +2873,13 @@ static void srpt_cm_rtu_recv(struct srpt_rdma_ch *ch) static void srpt_cm_timewait_exit(struct srpt_rdma_ch *ch) { - PRINT_INFO("Received InfiniBand TimeWait exit for ch %p.", ch); + PRINT_INFO("Received CM TimeWait exit for ch %s.", ch->sess_name); srpt_close_ch(ch); } -static void srpt_cm_rep_error(struct ib_cm_id *cm_id) +static void srpt_cm_rep_error(struct srpt_rdma_ch *ch) { - PRINT_INFO("Received InfiniBand REP error for cm_id %p.", cm_id); + PRINT_INFO("Received CM REP error for ch %s.", ch->sess_name); } /** @@ -2848,7 +2896,7 @@ static int srpt_cm_dreq_recv(struct srpt_rdma_ch *ch) */ static void srpt_cm_drep_recv(struct srpt_rdma_ch *ch) { - PRINT_INFO("Received InfiniBand DREP message for ch %p.", ch); + PRINT_INFO("Received CM DREP message for ch %s.", ch->sess_name); srpt_close_ch(ch); } @@ -2864,6 +2912,7 @@ static void srpt_cm_drep_recv(struct srpt_rdma_ch *ch) */ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) { + struct srpt_rdma_ch *ch = cm_id->context; int ret; BUG_ON(!cm_id->context); @@ -2875,29 +2924,29 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) event->private_data); break; case IB_CM_REJ_RECEIVED: - srpt_cm_rej_recv(cm_id); + srpt_cm_rej_recv(ch); break; case IB_CM_RTU_RECEIVED: case IB_CM_USER_ESTABLISHED: - srpt_cm_rtu_recv((struct srpt_rdma_ch *)cm_id->context); + srpt_cm_rtu_recv(ch); break; case IB_CM_DREQ_RECEIVED: - ret = srpt_cm_dreq_recv((struct srpt_rdma_ch *)cm_id->context); + ret = srpt_cm_dreq_recv(ch); break; case IB_CM_DREP_RECEIVED: - srpt_cm_drep_recv((struct srpt_rdma_ch *)cm_id->context); + srpt_cm_drep_recv(ch); break; case IB_CM_TIMEWAIT_EXIT: - srpt_cm_timewait_exit((struct srpt_rdma_ch *)cm_id->context); + srpt_cm_timewait_exit(ch); break; case IB_CM_REP_ERROR: - srpt_cm_rep_error(cm_id); + srpt_cm_rep_error(ch); break; case IB_CM_DREQ_ERROR: - PRINT_INFO("Received IB DREQ ERROR event."); + PRINT_INFO("Received CM DREQ ERROR event."); break; case IB_CM_MRA_RECEIVED: - PRINT_INFO("Received IB MRA event"); + PRINT_INFO("Received CM MRA event"); break; default: PRINT_ERROR("received unrecognized IB CM event %d", @@ -2911,23 +2960,32 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) static int srpt_rdma_cm_handler(struct rdma_cm_id *cm_id, struct rdma_cm_event *event) { + struct srpt_rdma_ch *ch = cm_id->context; int ret = 0; switch (event->event) { case RDMA_CM_EVENT_CONNECT_REQUEST: ret = srpt_rdma_cm_req_recv(cm_id, event); break; + case RDMA_CM_EVENT_REJECTED: + srpt_cm_rej_recv(ch); + break; case RDMA_CM_EVENT_ESTABLISHED: - srpt_cm_rtu_recv(cm_id->context); + srpt_cm_rtu_recv(ch); break; case RDMA_CM_EVENT_DISCONNECTED: - srpt_cm_dreq_recv(cm_id->context); + if (ch->state < CH_DISCONNECTING) + srpt_cm_dreq_recv(ch); + else + srpt_cm_drep_recv(ch); break; case RDMA_CM_EVENT_TIMEWAIT_EXIT: - srpt_cm_timewait_exit(cm_id->context); + srpt_cm_timewait_exit(ch); + break; + case RDMA_CM_EVENT_UNREACHABLE: + srpt_cm_rep_error(ch); break; case RDMA_CM_EVENT_DEVICE_REMOVAL: - break; case RDMA_CM_EVENT_ADDR_CHANGE: break; default: @@ -3579,8 +3637,11 @@ static int srpt_detect(struct scst_tgt_template *tp) static int srpt_close_session(struct scst_session *sess) { struct srpt_rdma_ch *ch = scst_sess_get_tgt_priv(sess); + struct srpt_tgt *srpt_tgt = ch->srpt_tgt; + mutex_lock(&srpt_tgt->mutex); srpt_disconnect_ch(ch); + mutex_unlock(&srpt_tgt->mutex); return 0; } @@ -4339,7 +4400,8 @@ static void __exit srpt_cleanup_module(void) { TRACE_ENTRY(); - rdma_destroy_id(rdma_cm_id); + if (rdma_cm_id) + rdma_destroy_id(rdma_cm_id); ib_unregister_client(&srpt_client); #ifdef CONFIG_SCST_PROC srpt_unregister_procfs_entry(&srpt_template); diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index e9b2b4913..96267bf5c 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -41,7 +41,6 @@ #include #include #include -#include #include #if defined(INSIDE_KERNEL_TREE) #include @@ -49,6 +48,14 @@ #include #include #endif +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 == 5 +#define vlan_dev_vlan_id(dev) (panic("RHEL 5 misses vlan_dev_vlan_id()"),0) +#endif +#if defined(RHEL_MAJOR) +#define __ethtool_get_settings(dev, cmd) (panic("RHEL misses __ethtool_get_settings()"),0) +#endif +#include +#include #include "ib_dm_mad.h" /* @@ -139,7 +146,8 @@ enum { LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 76)) && \ !(defined(RHEL_MAJOR) && \ (RHEL_MAJOR -0 > 6 || \ - RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 >= 5)) + RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 >= 5 || \ + RHEL_MAJOR -0 == 5 && RHEL_MINOR -0 >= 9)) /* See also patch "IB/core: Add GID change event" (commit 761d90ed4). */ enum { IB_EVENT_GID_CHANGE = 18 }; #endif @@ -284,15 +292,17 @@ struct srpt_send_ioctx { * enum rdma_ch_state - SRP channel state. * @CH_CONNECTING: QP is in RTR state; waiting for RTU. * @CH_LIVE: QP is in RTS state. - * @CH_DISCONNECTING: DREQ has been received and waiting for DREP or DREQ has - * been sent and waiting for DREP or channel is being closed - * for another reason. - * @CH_DISCONNECTED: Last WQE has been received. + * @CH_DISCONNECTING: DREQ has been sent and waiting for DREP or DREQ has + * been received. + * @CH_DRAINING: DREP has been received or waiting for DREP timed out + * and last work request has been queued. + * @CH_DISCONNECTED: Last completion has been received. */ enum rdma_ch_state { CH_CONNECTING, CH_LIVE, CH_DISCONNECTING, + CH_DRAINING, CH_DISCONNECTED, }; diff --git a/usr/fileio/fileio.c b/usr/fileio/fileio.c index 7a30acebf..9d583c4e5 100644 --- a/usr/fileio/fileio.c +++ b/usr/fileio/fileio.c @@ -383,9 +383,11 @@ int start(int argc, char **argv) desc.opt.on_free_cmd_type = on_free_cmd_type; desc.opt.memory_reuse_type = memory_reuse_type; - desc.opt.tst = SCST_CONTR_MODE_SEP_TASK_SETS; - desc.opt.queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER; - desc.opt.d_sense = SCST_CONTR_MODE_FIXED_SENSE; + desc.opt.tst = SCST_TST_1_SEP_TASK_SETS; + desc.opt.tmf_only = 0; + desc.opt.queue_alg = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER; + desc.opt.qerr = SCST_QERR_0_ALL_RESUME; + desc.opt.d_sense = SCST_D_SENSE_0_FIXED_SENSE; res = ioctl(devs[i].scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc); if (res != 0) {