Add the CAM Target Layer (CTL).

CTL is a disk and processor device emulation subsystem originally written
for Copan Systems under Linux starting in 2003.  It has been shipping in
Copan (now SGI) products since 2005.

It was ported to FreeBSD in 2008, and thanks to an agreement between SGI
(who acquired Copan's assets in 2010) and Spectra Logic in 2010, CTL is
available under a BSD-style license.  The intent behind the agreement was
that Spectra would work to get CTL into the FreeBSD tree.

Some CTL features:

 - Disk and processor device emulation.
 - Tagged queueing
 - SCSI task attribute support (ordered, head of queue, simple tags)
 - SCSI implicit command ordering support.  (e.g. if a read follows a mode
   select, the read will be blocked until the mode select completes.)
 - Full task management support (abort, LUN reset, target reset, etc.)
 - Support for multiple ports
 - Support for multiple simultaneous initiators
 - Support for multiple simultaneous backing stores
 - Persistent reservation support
 - Mode sense/select support
 - Error injection support
 - High Availability support (1)
 - All I/O handled in-kernel, no userland context switch overhead.

(1) HA Support is just an API stub, and needs much more to be fully
    functional.

ctl.c:			The core of CTL.  Command handlers and processing,
			character driver, and HA support are here.

ctl.h:			Basic function declarations and data structures.

ctl_backend.c,
ctl_backend.h:		The basic CTL backend API.

ctl_backend_block.c,
ctl_backend_block.h:	The block and file backend.  This allows for using
			a disk or a file as the backing store for a LUN.
			Multiple threads are started to do I/O to the
			backing device, primarily because the VFS API
			requires that to get any concurrency.

ctl_backend_ramdisk.c:	A "fake" ramdisk backend.  It only allocates a
			small amount of memory to act as a source and sink
			for reads and writes from an initiator.  Therefore
			it cannot be used for any real data, but it can be
			used to test for throughput.  It can also be used
			to test initiators' support for extremely large LUNs.

ctl_cmd_table.c:	This is a table with all 256 possible SCSI opcodes,
			and command handler functions defined for supported
			opcodes.

ctl_debug.h:		Debugging support.

ctl_error.c,
ctl_error.h:		CTL-specific wrappers around the CAM sense building
			functions.

ctl_frontend.c,
ctl_frontend.h:		These files define the basic CTL frontend port API.

ctl_frontend_cam_sim.c:	This is a CTL frontend port that is also a CAM SIM.
			This frontend allows for using CTL without any
			target-capable hardware.  So any LUNs you create in
			CTL are visible in CAM via this port.

ctl_frontend_internal.c,
ctl_frontend_internal.h:
			This is a frontend port written for Copan to do
			some system-specific tasks that required sending
			commands into CTL from inside the kernel.  This
			isn't entirely relevant to FreeBSD in general,
			but can perhaps be repurposed.

ctl_ha.h:		This is a stubbed-out High Availability API.  Much
			more is needed for full HA support.  See the
			comments in the header and the description of what
			is needed in the README.ctl.txt file for more
			details.

ctl_io.h:		This defines most of the core CTL I/O structures.
			union ctl_io is conceptually very similar to CAM's
			union ccb.

ctl_ioctl.h:		This defines all ioctls available through the CTL
			character device, and the data structures needed
			for those ioctls.

ctl_mem_pool.c,
ctl_mem_pool.h:		Generic memory pool implementation used by the
			internal frontend.

ctl_private.h:		Private data structres (e.g. CTL softc) and
			function prototypes.  This also includes the SCSI
			vendor and product names used by CTL.

ctl_scsi_all.c,
ctl_scsi_all.h:		CTL wrappers around CAM sense printing functions.

ctl_ser_table.c:	Command serialization table.  This defines what
			happens when one type of command is followed by
			another type of command.

ctl_util.c,
ctl_util.h:		CTL utility functions, primarily designed to be
			used from userland.  See ctladm for the primary
			consumer of these functions.  These include CDB
			building functions.

scsi_ctl.c:		CAM target peripheral driver and CTL frontend port.
			This is the path into CTL for commands from
			target-capable hardware/SIMs.

README.ctl.txt:		CTL code features, roadmap, to-do list.

usr.sbin/Makefile:	Add ctladm.

ctladm/Makefile,
ctladm/ctladm.8,
ctladm/ctladm.c,
ctladm/ctladm.h,
ctladm/util.c:		ctladm(8) is the CTL management utility.
			It fills a role similar to camcontrol(8).
			It allow configuring LUNs, issuing commands,
			injecting errors and various other control
			functions.

usr.bin/Makefile:	Add ctlstat.

ctlstat/Makefile
ctlstat/ctlstat.8,
ctlstat/ctlstat.c:	ctlstat(8) fills a role similar to iostat(8).
			It reports I/O statistics for CTL.

sys/conf/files:		Add CTL files.

sys/conf/NOTES:		Add device ctl.

sys/cam/scsi_all.h:	To conform to more recent specs, the inquiry CDB
			length field is now 2 bytes long.

			Add several mode page definitions for CTL.

sys/cam/scsi_all.c:	Handle the new 2 byte inquiry length.

sys/dev/ciss/ciss.c,
sys/dev/ata/atapi-cam.c,
sys/cam/scsi/scsi_targ_bh.c,
scsi_target/scsi_cmds.c,
mlxcontrol/interface.c:	Update for 2 byte inquiry length field.

scsi_da.h:		Add versions of the format and rigid disk pages
			that are in a more reasonable format for CTL.

amd64/conf/GENERIC,
i386/conf/GENERIC,
ia64/conf/GENERIC,
sparc64/conf/GENERIC:	Add device ctl.

i386/conf/PAE:		The CTL frontend SIM at least does not compile
			cleanly on PAE.

Sponsored by:	Copan Systems, SGI and Spectra Logic
MFC after:	1 month
This commit is contained in:
Kenneth D. Merry 2012-01-12 00:34:33 +00:00
parent ec3fc72f94
commit 130f4520cb
54 changed files with 34231 additions and 19 deletions

View File

@ -328,7 +328,7 @@ tcmd_inquiry(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
bcopy(&inq_data, ctio->data_ptr, sizeof(inq_data));
ctio->dxfer_len = inq_data.additional_length + 4;
ctio->dxfer_len = min(ctio->dxfer_len,
SCSI_CDB6_LEN(inq->length));
scsi_2btoul(inq->length));
ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
ctio->scsi_status = SCSI_STATUS_OK;
}

View File

@ -131,7 +131,8 @@ device da # Direct Access (disks)
device sa # Sequential Access (tape etc)
device cd # CD
device pass # Passthrough device (direct ATA/SCSI access)
device ses # SCSI Environmental Services (and SAF-TE)
device ses # Enclosure Services (SES and SAF-TE)
device ctl # CAM Target Layer
# RAID controllers interfaced to the SCSI subsystem
device amr # AMI MegaRAID

449
sys/cam/ctl/README.ctl.txt Normal file
View File

@ -0,0 +1,449 @@
/* $FreeBSD$ */
CTL - CAM Target Layer Description
Revision 1.4 (December 29th, 2011)
Ken Merry <ken@FreeBSD.org>
Table of Contents:
=================
Introduction
Features
Configuring and Running CTL
Revision 1.N Changes
To Do List
Code Roadmap
Userland Commands
Introduction:
============
CTL is a disk and processor device emulation subsystem originally written
for Copan Systems under Linux starting in 2003. It has been shipping in
Copan (now SGI) products since 2005.
It was ported to FreeBSD in 2008, and thanks to an agreement between SGI
(who acquired Copan's assets in 2010) and Spectra Logic in 2010, CTL is
available under a BSD-style license. The intent behind the agreement was
that Spectra would work to get CTL into the FreeBSD tree.
Features:
========
- Disk and processor device emulation.
- Tagged queueing
- SCSI task attribute support (ordered, head of queue, simple tags)
- SCSI implicit command ordering support. (e.g. if a read follows a mode
select, the read will be blocked until the mode select completes.)
- Full task management support (abort, LUN reset, target reset, etc.)
- Support for multiple ports
- Support for multiple simultaneous initiators
- Support for multiple simultaneous backing stores
- Persistent reservation support
- Mode sense/select support
- Error injection support
- High Availability support (1)
- All I/O handled in-kernel, no userland context switch overhead.
(1) HA Support is just an API stub, and needs much more to be fully
functional. See the to-do list below.
Configuring and Running CTL:
===========================
- After applying the CTL patchset to your tree, build world and install it
on your target system.
- Add 'device ctl' to your kernel configuration file.
- If you're running with a 8Gb or 4Gb Qlogic FC board, add
'options ISP_TARGET_MODE' to your kernel config file. Keep in mind that
the isp(4) driver can run in target or initiator mode, but not both on
the same machine. 'device ispfw' or loading the ispfw module is also
recommended.
- Rebuild and install a new kernel.
- Reboot with the new kernel.
- To add a LUN with the RAM disk backend:
ctladm create -b ramdisk -s 10485760000000000000
ctladm port -o on
- You should now see the CTL disk LUN through camcontrol devlist:
scbus6 on ctl2cam0 bus 0:
<FREEBSD CTLDISK 0001> at scbus6 target 1 lun 0 (da24,pass32)
<> at scbus6 target -1 lun -1 ()
This is visible through the CTL CAM SIM. This allows using CTL without
any physical hardware. You should be able to issue any normal SCSI
commands to the device via the pass(4)/da(4) devices.
If any target-capable HBAs are in the system (e.g. isp(4)), and have
target mode enabled, you should now also be able to see the CTL LUNs via
that target interface.
Note that all CTL LUNs are presented to all frontends. There is no
LUN masking, or separate, per-port configuration.
- Note that the ramdisk backend is a "fake" ramdisk. That is, it is
backed by a small amount of RAM that is used for all I/O requests. This
is useful for performance testing, but not for any data integrity tests.
- To add a LUN with the block/file backend:
truncate -s +1T myfile
ctladm create -b block -o file=myfile
ctladm port -o on
- You can also see a list of LUNs and their backends like this:
# ctladm devlist
LUN Backend Size (Blocks) BS Serial Number Device ID
0 block 2147483648 512 MYSERIAL 0 MYDEVID 0
1 block 2147483648 512 MYSERIAL 1 MYDEVID 1
2 block 2147483648 512 MYSERIAL 2 MYDEVID 2
3 block 2147483648 512 MYSERIAL 3 MYDEVID 3
4 block 2147483648 512 MYSERIAL 4 MYDEVID 4
5 block 2147483648 512 MYSERIAL 5 MYDEVID 5
6 block 2147483648 512 MYSERIAL 6 MYDEVID 6
7 block 2147483648 512 MYSERIAL 7 MYDEVID 7
8 block 2147483648 512 MYSERIAL 8 MYDEVID 8
9 block 2147483648 512 MYSERIAL 9 MYDEVID 9
10 block 2147483648 512 MYSERIAL 10 MYDEVID 10
11 block 2147483648 512 MYSERIAL 11 MYDEVID 11
- You can see the LUN type and backing store for block/file backend LUNs
like this:
# ctladm devlist -v
LUN Backend Size (Blocks) BS Serial Number Device ID
0 block 2147483648 512 MYSERIAL 0 MYDEVID 0
lun_type=0
num_threads=14
file=testdisk0
1 block 2147483648 512 MYSERIAL 1 MYDEVID 1
lun_type=0
num_threads=14
file=testdisk1
2 block 2147483648 512 MYSERIAL 2 MYDEVID 2
lun_type=0
num_threads=14
file=testdisk2
3 block 2147483648 512 MYSERIAL 3 MYDEVID 3
lun_type=0
num_threads=14
file=testdisk3
4 block 2147483648 512 MYSERIAL 4 MYDEVID 4
lun_type=0
num_threads=14
file=testdisk4
5 block 2147483648 512 MYSERIAL 5 MYDEVID 5
lun_type=0
num_threads=14
file=testdisk5
6 block 2147483648 512 MYSERIAL 6 MYDEVID 6
lun_type=0
num_threads=14
file=testdisk6
7 block 2147483648 512 MYSERIAL 7 MYDEVID 7
lun_type=0
num_threads=14
file=testdisk7
8 block 2147483648 512 MYSERIAL 8 MYDEVID 8
lun_type=0
num_threads=14
file=testdisk8
9 block 2147483648 512 MYSERIAL 9 MYDEVID 9
lun_type=0
num_threads=14
file=testdisk9
10 ramdisk 0 0 MYSERIAL 0 MYDEVID 0
lun_type=3
11 ramdisk 204800000000000 512 MYSERIAL 1 MYDEVID 1
lun_type=0
Revision 1.4 Changes
====================
- Added in the second HA mode (where CTL does the data transfers instead
of having data transfers done below CTL), and abstracted out the Copan
HA API.
- Fixed the phantom device problem in the CTL CAM SIM and improved the
CAM SIM to automatically trigger a rescan when the port is enabled and
disabled.
- Made the number of threads in the block backend configurable via sysctl,
loader tunable and the ctladm command line. (You can now specify
-o num_threads=4 when creating a LUN with ctladm create.)
- Fixed some LUN selection issues in ctlstat(8) and allowed for selection
of LUN numbers up to 1023.
- General cleanup.
- This version intended for public release.
Revision 1.3 Changes
====================
- Added descriptor sense support to CTL. It can be enabled through the
control mode page (10), but is disabled by default.
- Improved error injection support. The number of errors that can be
injected with 'ctladm inject' has been increased, and any arbitrary
sense data may now be injected as well.
- The port infrastructure has been revamped. Individual ports and types
of ports may now be enabled and disabled from the command line. ctladm
now has the ability to set the WWNN and WWPN for each port.
- The block backend can now send multiple I/Os to backing files. Multiple
writes are only allowed for ZFS, but multiple readers are allowed for
any filesystem.
- The block and ramdisk backends now support setting the LUN blocksize.
There are some restrictions when the backing device is a block device,
but otherwise the blocksize may be set to anything.
Revision 1.2 Changes
====================
- CTL initialization process has been revamped. Instead of using an
ad-hoc method, it is now sequenced through SYSINIT() calls.
- A block/file backend has been added. This allows using arbitrary files
or block devices as a backing store.
- The userland LUN configuration interface has been completely rewritten.
Configuration is now done out of band.
- The ctladm(8) command line interface has been revamped, and is now
similar to camcontrol(8).
To Do List:
==========
- Make CTL buildable as a module. Work needs to be done on initialization,
and on freeing resources and LUNs when it is built as a module.
- Use devstat(9) for CTL's statistics collection. CTL uses a home-grown
statistics collection system that is similar to devstat(9). ctlstat
should be retired in favor of iostat, etc., once aggregation modes are
available in iostat to match the behavior of ctlstat -t and dump modes
are available to match the behavior of ctlstat -d/ctlstat -J.
- ZFS ARC backend for CTL. Since ZFS copies all I/O into the ARC
(Adaptive Replacement Cache), running the block/file backend on top of a
ZFS-backed zdev or file will involve an extra set of copies. The
optimal solution for backing targets served by CTL with ZFS would be to
allocate buffers out of the ARC directly, and DMA to/from them directly.
That would eliminate an extra data buffer allocation and copy.
- Switch CTL over to using CAM CCBs instead of its own union ctl_io. This
will likely require a significant amount of work, but will eliminate
another data structure in the stack, more memory allocations, etc. This
will also require changes to the CAM CCB structure to support CTL.
- Full-featured High Availability support. The HA API that is in ctl_ha.h
is essentially a renamed version of Copan's HA API. There is no
substance to it, but it remains in CTL to show what needs to be done to
implement active/active HA from a CTL standpoint. The things that would
need to be done include:
- A kernel level software API for message passing as well as DMA
between at least two nodes.
- Hardware support and drivers for inter-node communication. This
could be as simples as ethernet hardware and drivers.
- A "supervisor", or startup framework to control and coordinate
HA startup, failover (going from active/active to single mode),
and failback (going from single mode to active/active).
- HA support in other components of the stack. The goal behind HA
is that one node can fail and another node can seamlessly take
over handling I/O requests. This requires support from pretty
much every component in the storage stack, from top to bottom.
CTL is one piece of it, but you also need support in the RAID
stack/filesystem/backing store. You also need full configuration
mirroring, and all peer nodes need to be able to talk to the
underlying storage hardware.
Code Roadmap:
============
CTL has the concept of pluggable frontend ports and backends. All
frontends and backends can be active at the same time. You can have a
ramdisk-backed LUN present along side a file backed LUN.
ctl.c:
-----
This is the core of CTL, where all of the command handlers and a lot of
other things live. Yes, it is large. It started off small and grew to its
current size over time. Perhaps it can be split into more files at some
point.
Here is a roadmap of some of the primary functions in ctl.c. Starting here
and following the various leaf functions will show the command flow.
ctl_queue() This is where commands from the frontend ports come
in.
ctl_queue_sense() This is only used for non-packetized SCSI. i.e.
parallel SCSI prior to U320 and perhaps U160.
ctl_work_thread() This is the primary work thread, and everything gets
executed from there.
ctl_scsiio_precheck() This where all of the initial checks are done, and I/O
is either queued for execution or blocked.
ctl_scsiio() This is where the command handler is actually
executed. (See ctl_cmd_table.c for the mapping of
SCSI opcode to command handler function.)
ctl_done() This is the routine called (or ctl_done_lock()) to
initiate the command completion process.
ctl_process_done() This is where command completion actually happens.
ctl.h:
-----
Basic function declarations and data structures.
ctl_backend.c,
ctl_backend.h:
-------------
These files define the basic CTL backend API. The comments in the header
explain the API.
ctl_backend_block.c
ctl_backend_block.h:
-------------------
The block and file backend. This allows for using a disk or a file as the
backing store for a LUN. Multiple threads are started to do I/O to the
backing device, primarily because the VFS API requires that to get any
concurrency.
ctl_backend_ramdisk.c:
---------------------
A "fake" ramdisk backend. It only allocates a small amount of memory to
act as a source and sink for reads and writes from an initiator. Therefore
it cannot be used for any real data, but it can be used to test for
throughput. It can also be used to test initiators' support for extremely
large LUNs.
ctl_cmd_table.c:
---------------
This is a table with all 256 possible SCSI opcodes, and command handler
functions defined for supported opcodes. It is included in ctl.c.
ctl_debug.h:
-----------
Simplistic debugging support.
ctl_error.c,
ctl_error.h:
-----------
CTL-specific wrappers around the CAM sense building functions.
ctl_frontend.c,
ctl_frontend.h:
--------------
These files define the basic CTL frontend port API. The comments in the
header explain the API.
ctl_frontend_cam_sim.c:
----------------------
This is a CTL frontend port that is also a CAM SIM. The idea is that this
frontend allows for using CTL without any target-capable hardware. So any
LUNs you create in CTL are visible via this port.
ctl_frontend_internal.c
ctl_frontend_internal.h:
-----------------------
This is a frontend port written for Copan to do some system-specific tasks
that required sending commands into CTL from inside the kernel. This isn't
entirely relevant to FreeBSD in general, but can perhaps be repurposed or
removed later.
ctl_ha.h:
--------
This is a stubbed-out High Availability API. See the comments in the
header and the description of what is needed as far as HA support above.
ctl_io.h:
--------
This defines most of the core CTL I/O structures. union ctl_io is
conceptually very similar to CAM's union ccb.
ctl_ioctl.h:
-----------
This defines all ioctls available through the CTL character device, and
the data structures needed for those ioctls.
ctl_mem_pool.c
ctl_mem_pool.h:
--------------
Generic memory pool implementation. This is currently only used by the
internal frontend. The internal frontend can probably be rewritten to use
UMA zones and this can be removed.
ctl_private.h:
-------------
Private data structres (e.g. CTL softc) and function prototypes. This also
includes the SCSI vendor and product names used by CTL.
ctl_scsi_all.c
ctl_scsi_all.h:
--------------
CTL wrappers around CAM sense printing functions.
ctl_ser_table.c:
---------------
Command serialization table. This defines what happens when one type of
command is followed by another type of command. e.g., what do you do when
you have a mode select followed by a write? You block the write until the
mode select is complete. That is defined in this table.
ctl_util.c
ctl_util.h:
----------
CTL utility functions, primarily designed to be used from userland. See
ctladm for the primary consumer of these functions. These include CDB
building functions.
scsi_ctl.c:
----------
CAM target peripheral driver and CTL frontend port. This is the path into
CTL for commands from target-capable hardware/SIMs.
Userland Commands:
=================
ctladm(8) fills a role similar to camcontrol(8). It allow configuring LUNs,
issuing commands, injecting errors and various other control functions.
ctlstat(8) fills a role similar to iostat(8). It reports I/O statistics
for CTL.

13082
sys/cam/ctl/ctl.c Normal file

File diff suppressed because it is too large Load Diff

216
sys/cam/ctl/ctl.h Normal file
View File

@ -0,0 +1,216 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl.h#5 $
* $FreeBSD$
*/
/*
* Function definitions used both within CTL and potentially in various CTL
* clients.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#ifndef _CTL_H_
#define _CTL_H_
#define ctl_min(x,y) (((x) < (y)) ? (x) : (y))
#define CTL_RETVAL_COMPLETE 0
#define CTL_RETVAL_QUEUED 1
#define CTL_RETVAL_ALLOCATED 2
#define CTL_RETVAL_ERROR 3
typedef enum {
CTL_PORT_NONE = 0x00,
CTL_PORT_FC = 0x01,
CTL_PORT_SCSI = 0x02,
CTL_PORT_IOCTL = 0x04,
CTL_PORT_INTERNAL = 0x08,
CTL_PORT_ALL = 0xff,
CTL_PORT_ISC = 0x100 // FC port for inter-shelf communication
} ctl_port_type;
struct ctl_port_entry {
ctl_port_type port_type;
char port_name[64];
int32_t targ_port;
int physical_port;
int virtual_port;
u_int flags;
#define CTL_PORT_WWNN_VALID 0x01
#define CTL_PORT_WWPN_VALID 0x02
uint64_t wwnn;
uint64_t wwpn;
int online;
};
struct ctl_modepage_header {
uint8_t page_code;
uint8_t subpage;
int32_t len_used;
int32_t len_left;
};
struct ctl_modepage_aps {
struct ctl_modepage_header header;
uint8_t lock_active;
};
union ctl_modepage_info {
struct ctl_modepage_header header;
struct ctl_modepage_aps aps;
};
/*
* Serial number length, for VPD page 0x80.
*/
#define CTL_SN_LEN 16
/*
* Device ID length, for VPD page 0x83.
*/
#define CTL_DEVID_LEN 16
/*
* WWPN length, for VPD page 0x83.
*/
#define CTL_WWPN_LEN 8
/*
* Unit attention types. ASC/ASCQ values for these should be placed in
* ctl_build_ua. These are also listed in order of reporting priority.
* i.e. a poweron UA is reported first, bus reset second, etc.
*/
typedef enum {
CTL_UA_NONE = 0x0000,
CTL_UA_POWERON = 0x0001,
CTL_UA_BUS_RESET = 0x0002,
CTL_UA_TARG_RESET = 0x0004,
CTL_UA_LUN_RESET = 0x0008,
CTL_UA_LUN_CHANGE = 0x0010,
CTL_UA_MODE_CHANGE = 0x0020,
CTL_UA_LOG_CHANGE = 0x0040,
CTL_UA_LVD = 0x0080,
CTL_UA_SE = 0x0100,
CTL_UA_RES_PREEMPT = 0x0200,
CTL_UA_RES_RELEASE = 0x0400,
CTL_UA_REG_PREEMPT = 0x0800,
CTL_UA_ASYM_ACC_CHANGE = 0x1000
} ctl_ua_type;
#ifdef _KERNEL
MALLOC_DECLARE(M_CTL);
typedef enum {
CTL_THREAD_NONE = 0x00,
CTL_THREAD_WAKEUP = 0x01
} ctl_thread_flags;
struct ctl_thread {
void (*thread_func)(void *arg);
void *arg;
struct cv wait_queue;
const char *thread_name;
ctl_thread_flags thread_flags;
struct completion *thread_event;
struct task_struct *task;
};
struct ctl_page_index;
#ifdef SYSCTL_DECL /* from sysctl.h */
SYSCTL_DECL(_kern_cam_ctl);
#endif
/*
* Call these routines to enable or disable front end ports.
*/
int ctl_port_enable(ctl_port_type port_type);
int ctl_port_disable(ctl_port_type port_type);
/*
* This routine grabs a list of frontend ports.
*/
int ctl_port_list(struct ctl_port_entry *entries, int num_entries_alloced,
int *num_entries_filled, int *num_entries_dropped,
ctl_port_type port_type, int no_virtual);
/*
* Put a string into an sbuf, escaping characters that are illegal or not
* recommended in XML. Note this doesn't escape everything, just > < and &.
*/
int ctl_sbuf_printf_esc(struct sbuf *sb, char *str);
int ctl_ffz(uint32_t *mask, uint32_t size);
int ctl_set_mask(uint32_t *mask, uint32_t bit);
int ctl_clear_mask(uint32_t *mask, uint32_t bit);
int ctl_is_set(uint32_t *mask, uint32_t bit);
int ctl_control_page_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index,
uint8_t *page_ptr);
/**
int ctl_failover_sp_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index,
uint8_t *page_ptr);
**/
int ctl_power_sp_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index, uint8_t *page_ptr);
int ctl_power_sp_sense_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index, int pc);
int ctl_aps_sp_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index, uint8_t *page_ptr);
int ctl_debugconf_sp_sense_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index,
int pc);
int ctl_debugconf_sp_select_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index,
uint8_t *page_ptr);
int ctl_config_move_done(union ctl_io *io);
void ctl_datamove(union ctl_io *io);
void ctl_done(union ctl_io *io);
void ctl_config_write_done(union ctl_io *io);
#if 0
int ctl_thread(void *arg);
#endif
void ctl_wakeup_thread(void);
#if 0
struct ctl_thread *ctl_create_thread(void (*thread_func)
(void *thread_arg), void *thread_arg, const char *thread_name);
void ctl_signal_thread(struct ctl_thread *thread);
void ctl_shutdown_thread(struct ctl_thread *thread);
#endif
void ctl_portDB_changed(int portnum);
void ctl_init_isc_msg(void);
#endif /* _KERNEL */
#endif /* _CTL_H_ */
/*
* vim: ts=8
*/

177
sys/cam/ctl/ctl_backend.c Normal file
View File

@ -0,0 +1,177 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_backend.c#3 $
*/
/*
* CTL backend driver registration routines
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/queue.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_da.h>
#include <cam/ctl/ctl_io.h>
#include <cam/ctl/ctl.h>
#include <cam/ctl/ctl_frontend.h>
#include <cam/ctl/ctl_backend.h>
#include <cam/ctl/ctl_frontend_internal.h>
#include <cam/ctl/ctl_ioctl.h>
#include <cam/ctl/ctl_ha.h>
#include <cam/ctl/ctl_private.h>
#include <cam/ctl/ctl_debug.h>
extern struct ctl_softc *control_softc;
int
ctl_backend_register(struct ctl_backend_driver *be)
{
struct ctl_softc *ctl_softc;
struct ctl_backend_driver *be_tmp;
ctl_softc = control_softc;
mtx_lock(&ctl_softc->ctl_lock);
/*
* Sanity check, make sure this isn't a duplicate registration.
*/
STAILQ_FOREACH(be_tmp, &ctl_softc->be_list, links) {
if (strcmp(be_tmp->name, be->name) == 0) {
mtx_unlock(&ctl_softc->ctl_lock);
return (-1);
}
}
mtx_unlock(&ctl_softc->ctl_lock);
/*
* Call the backend's initialization routine.
*/
be->init();
mtx_lock(&ctl_softc->ctl_lock);
STAILQ_INSERT_TAIL(&ctl_softc->be_list, be, links);
ctl_softc->num_backends++;
/*
* Don't want to increment the usage count for internal consumers,
* we won't be able to unload otherwise.
*/
/* XXX KDM find a substitute for this? */
#if 0
if ((be->flags & CTL_BE_FLAG_INTERNAL) == 0)
MOD_INC_USE_COUNT;
#endif
#ifdef CS_BE_CONFIG_MOVE_DONE_IS_NOT_USED
be->config_move_done = ctl_config_move_done;
#endif
/* XXX KDM fix this! */
be->num_luns = 0;
#if 0
atomic_set(&be->num_luns, 0);
#endif
mtx_unlock(&ctl_softc->ctl_lock);
return (0);
}
int
ctl_backend_deregister(struct ctl_backend_driver *be)
{
struct ctl_softc *ctl_softc;
ctl_softc = control_softc;
mtx_lock(&ctl_softc->ctl_lock);
#if 0
if (atomic_read(&be->num_luns) != 0) {
#endif
/* XXX KDM fix this! */
if (be->num_luns != 0) {
mtx_unlock(&ctl_softc->ctl_lock);
return (-1);
}
STAILQ_REMOVE(&ctl_softc->be_list, be, ctl_backend_driver, links);
ctl_softc->num_backends--;
/* XXX KDM find a substitute for this? */
#if 0
if ((be->flags & CTL_BE_FLAG_INTERNAL) == 0)
MOD_DEC_USE_COUNT;
#endif
mtx_unlock(&ctl_softc->ctl_lock);
return (0);
}
struct ctl_backend_driver *
ctl_backend_find(char *backend_name)
{
struct ctl_softc *ctl_softc;
struct ctl_backend_driver *be_tmp;
ctl_softc = control_softc;
mtx_lock(&ctl_softc->ctl_lock);
STAILQ_FOREACH(be_tmp, &ctl_softc->be_list, links) {
if (strcmp(be_tmp->name, backend_name) == 0) {
mtx_unlock(&ctl_softc->ctl_lock);
return (be_tmp);
}
}
mtx_unlock(&ctl_softc->ctl_lock);
return (NULL);
}
/*
* vim: ts=8
*/

288
sys/cam/ctl/ctl_backend.h Normal file
View File

@ -0,0 +1,288 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_backend.h#2 $
* $FreeBSD$
*/
/*
* CTL backend driver definitions
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#ifndef _CTL_BACKEND_H_
#define _CTL_BACKEND_H_
/*
* XXX KDM move this to another header file?
*/
#define CTL_BE_NAME_LEN 32
/*
* The ID_REQ flag is used to say that the caller has requested a
* particular LUN ID in the req_lun_id field. If we cannot allocate that
* LUN ID, the ctl_add_lun() call will fail.
*
* The POWERED_OFF flag tells us that the LUN should default to the powered
* off state. It will return 0x04,0x02 until it is powered up. ("Logical
* unit not ready, initializing command required.")
*
* The INOPERABLE flag tells us that this LUN is not operable for whatever
* reason. This means that user data may have been (or has been?) lost.
* We will return 0x31,0x00 ("Medium format corrupted") until the host
* issues a FORMAT UNIT command to clear the error.
*
* The PRIMARY flag tells us that this LUN is registered as a Primary LUN
* which is accessible via the Master shelf controller in an HA. This flag
* being set indicates a Primary LUN. This flag being reset represents a
* Secondary LUN controlled by the Secondary controller in an HA
* configuration. Flag is applicable at this time to T_DIRECT types.
*
* The SERIAL_NUM flag tells us that the serial_num field is filled in and
* valid for use in SCSI INQUIRY VPD page 0x80.
*
* The DEVID flag tells us that the device_id field is filled in and
* valid for use in SCSI INQUIRY VPD page 0x83.
*
* The DEV_TYPE flag tells us that the device_type field is filled in.
*/
typedef enum {
CTL_LUN_FLAG_ID_REQ = 0x01,
CTL_LUN_FLAG_POWERED_OFF = 0x02,
CTL_LUN_FLAG_INOPERABLE = 0x04,
CTL_LUN_FLAG_PRIMARY = 0x08,
CTL_LUN_FLAG_SERIAL_NUM = 0x10,
CTL_LUN_FLAG_DEVID = 0x20,
CTL_LUN_FLAG_DEV_TYPE = 0x40
} ctl_backend_lun_flags;
#ifdef _KERNEL
#define CTL_BACKEND_DECLARE(name, driver) \
static int name ## _modevent(module_t mod, int type, void *data) \
{ \
switch (type) { \
case MOD_LOAD: \
ctl_backend_register( \
(struct ctl_backend_driver *)data); \
break; \
case MOD_UNLOAD: \
printf(#name " module unload - not possible for this module type\n"); \
return EINVAL; \
default: \
return EOPNOTSUPP; \
} \
return 0; \
} \
static moduledata_t name ## _mod = { \
#name, \
name ## _modevent, \
(void *)&driver \
}; \
DECLARE_MODULE(name, name ## _mod, SI_SUB_CONFIGURE, SI_ORDER_FOURTH); \
MODULE_DEPEND(name, ctl, 1, 1, 1); \
MODULE_DEPEND(name, cam, 1, 1, 1)
typedef enum {
CTL_LUN_CONFIG_OK,
CTL_LUN_CONFIG_FAILURE
} ctl_lun_config_status;
typedef void (*be_callback_t)(void *be_lun);
typedef void (*be_lun_config_t)(void *be_lun,
ctl_lun_config_status status);
/*
* The lun_type field is the SCSI device type of this particular LUN. In
* general, this should be T_DIRECT, although backends will want to create
* a processor LUN, typically at LUN 0. See scsi_all.h for the defines for
* the various SCSI device types.
*
* The flags are described above.
*
* The be_lun field is the backend driver's own context that will get
* passsed back so that it can tell which LUN CTL is referencing.
*
* maxlba is the maximum accessible LBA on the LUN. Note that this is
* different from the capacity of the array. capacity = maxlba + 1
*
* blocksize is the size, in bytes, of each LBA on the LUN. In general
* this should be 512. In theory CTL should be able to handle other block
* sizes. Host application software may not deal with it very well, though.
*
* req_lun_id is the requested LUN ID. CTL only pays attention to this
* field if the CTL_LUN_FLAG_ID_REQ flag is set. If the requested LUN ID is
* not available, the LUN addition will fail. If a particular LUN ID isn't
* requested, the first available LUN ID will be allocated.
*
* serial_num is the device serial number returned in the SCSI INQUIRY VPD
* page 0x80. This should be a unique, per-shelf value. The data inside
* this field should be ASCII only, left aligned, and any unused space
* should be padded out with ASCII spaces. This field should NOT be NULL
* terminated.
*
* device_id is the T10 device identifier returned in the SCSI INQUIRY VPD
* page 0x83. This should be a unique, per-LUN value. The data inside
* this field should be ASCII only, left aligned, and any unused space
* should be padded with ASCII spaces. This field should NOT be NULL
* terminated.
*
* The lun_shutdown() method is the callback for the ctl_invalidate_lun()
* call. It is called when all outstanding I/O for that LUN has been
* completed and CTL has deleted the resources for that LUN. When the CTL
* backend gets this call, it can safely free its per-LUN resources.
*
* The lun_config_status() method is the callback for the ctl_add_lun()
* call. It is called when the LUN is successfully added, or when LUN
* addition fails. If the LUN is successfully added, the backend may call
* the ctl_enable_lun() method to enable the LUN.
*
* The be field is a pointer to the ctl_backend_driver structure, which
* contains the backend methods to be called by CTL.
*
* The ctl_lun field is for CTL internal use only, and should not be used
* by the backend.
*
* The links field is for CTL internal use only, and should not be used by
* the backend.
*/
struct ctl_be_lun {
uint8_t lun_type; /* passed to CTL */
ctl_backend_lun_flags flags; /* passed to CTL */
void *be_lun; /* passed to CTL */
uint64_t maxlba; /* passed to CTL */
uint32_t blocksize; /* passed to CTL */
uint32_t req_lun_id; /* passed to CTL */
uint32_t lun_id; /* returned from CTL */
uint8_t serial_num[CTL_SN_LEN]; /* passed to CTL */
uint8_t device_id[CTL_DEVID_LEN];/* passed to CTL */
be_callback_t lun_shutdown; /* passed to CTL */
be_lun_config_t lun_config_status; /* passed to CTL */
struct ctl_backend_driver *be; /* passed to CTL */
void *ctl_lun; /* used by CTL */
STAILQ_ENTRY(ctl_be_lun) links; /* used by CTL */
};
typedef enum {
CTL_BE_FLAG_NONE = 0x00, /* no flags */
CTL_BE_FLAG_HAS_CONFIG = 0x01, /* can do config reads, writes */
CTL_BE_FLAG_INTERNAL = 0x02 /* don't inc mod refcount */
} ctl_backend_flags;
typedef int (*be_init_t)(void);
typedef int (*be_func_t)(union ctl_io *io);
typedef void (*be_vfunc_t)(union ctl_io *io);
typedef int (*be_ioctl_t)(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
struct thread *td);
typedef int (*be_luninfo_t)(void *be_lun, struct sbuf *sb);
struct ctl_backend_driver {
char name[CTL_BE_NAME_LEN]; /* passed to CTL */
ctl_backend_flags flags; /* passed to CTL */
be_init_t init; /* passed to CTL */
be_func_t data_submit; /* passed to CTL */
be_func_t data_move_done; /* passed to CTL */
be_func_t config_read; /* passed to CTL */
be_func_t config_write; /* passed to CTL */
be_ioctl_t ioctl; /* passed to CTL */
be_luninfo_t lun_info; /* passed to CTL */
#ifdef CS_BE_CONFIG_MOVE_DONE_IS_NOT_USED
be_func_t config_move_done; /* passed to backend */
#endif
#if 0
be_vfunc_t config_write_done; /* passed to backend */
#endif
u_int num_luns; /* used by CTL */
STAILQ_ENTRY(ctl_backend_driver) links; /* used by CTL */
};
int ctl_backend_register(struct ctl_backend_driver *be);
int ctl_backend_deregister(struct ctl_backend_driver *be);
struct ctl_backend_driver *ctl_backend_find(char *backend_name);
/*
* To add a LUN, first call ctl_add_lun(). You will get the lun_config_status()
* callback when the LUN addition has either succeeded or failed.
*
* Once you get that callback, you can then call ctl_enable_lun() to enable
* the LUN.
*/
int ctl_add_lun(struct ctl_be_lun *be_lun);
int ctl_enable_lun(struct ctl_be_lun *be_lun);
/*
* To delete a LUN, first call ctl_disable_lun(), then
* ctl_invalidate_lun(). You will get the lun_shutdown() callback when all
* I/O to the LUN has completed and the LUN has been deleted.
*/
int ctl_disable_lun(struct ctl_be_lun *be_lun);
int ctl_invalidate_lun(struct ctl_be_lun *be_lun);
/*
* To start a LUN (transition from powered off to powered on state) call
* ctl_start_lun(). To stop a LUN (transition from powered on to powered
* off state) call ctl_stop_lun().
*/
int ctl_start_lun(struct ctl_be_lun *be_lun);
int ctl_stop_lun(struct ctl_be_lun *be_lun);
/*
* If a LUN is inoperable, call ctl_lun_inoperable(). Generally the LUN
* will become operable once again when the user issues the SCSI FORMAT UNIT
* command. (CTL will automatically clear the inoperable flag.) If we
* need to re-enable the LUN, we can call ctl_lun_operable() to enable it
* without a SCSI command.
*/
int ctl_lun_inoperable(struct ctl_be_lun *be_lun);
int ctl_lun_operable(struct ctl_be_lun *be_lun);
/*
* If a LUN is locked on or unlocked from a power/APS standpoint, call
* ctl_lun_power_lock() to update the current status in CTL's APS subpage.
* Set the lock flag to 1 to lock the LUN, set it to 0 to unlock the LUN.
*/
int ctl_lun_power_lock(struct ctl_be_lun *be_lun, struct ctl_nexus *nexus,
int lock);
/*
* To take a LUN offline, call ctl_lun_offline(). Generally the LUN will
* be online again once the user sends a SCSI START STOP UNIT command with
* the start and on/offline bits set. The backend can bring the LUN back
* online via the ctl_lun_online() function, if necessary.
*/
int ctl_lun_offline(struct ctl_be_lun *be_lun);
int ctl_lun_online(struct ctl_be_lun *be_lun);
#endif /* _KERNEL */
#endif /* _CTL_BACKEND_H_ */
/*
* vim: ts=8
*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_backend_block.h#1 $
* $FreeBSD$
*/
/*
* CAM Target Layer driver backend interface for block devices.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#ifndef _CTL_BACKEND_BLOCK_H_
#define _CTL_BACKEND_BLOCK_H_
struct ctl_block_disk {
uint32_t version; /* interface version */
uint32_t disknum; /* returned device number */
STAILQ_ENTRY(ctl_block_disk) links; /* linked list pointer */
char disk_name[MAXPATHLEN]; /* name of this device */
int allocated; /* disk is allocated to a LUN */
uint64_t size_blocks; /* disk size in blocks */
uint64_t size_bytes; /* disk size in bytes */
};
typedef enum {
CTL_BLOCK_DEVLIST_MORE,
CTL_BLOCK_DEVLIST_DONE
} ctl_block_devlist_status;
struct ctl_block_devlist {
uint32_t version; /* interface version */
uint32_t buf_len; /* passed in, buffer length */
uint32_t ctl_disk_size; /* size of adddev, passed in */
struct ctl_block_disk *devbuf; /* buffer passed in/filled out*/
uint32_t num_bufs; /* number passed out */
uint32_t buf_used; /* bytes passed out */
uint32_t total_disks; /* number of disks in system */
ctl_block_devlist_status status; /* did we get the whole list? */
};
#define CTL_BLOCK_ADDDEV _IOWR(COPAN_ARRAY_BE_BLOCK, 0x00, struct ctl_block_disk)
#define CTL_BLOCK_DEVLIST _IOWR(COPAN_ARRAY_BE_BLOCK, 0x01, struct ctl_block_devlist)
#define CTL_BLOCK_RMDEV _IOW(COPAN_ARRAY_BE_BLOCK, 0x02, struct ctl_block_disk)
#endif /* _CTL_BACKEND_BLOCK_H_ */

View File

@ -0,0 +1,835 @@
/*-
* Copyright (c) 2003, 2008 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_backend_ramdisk.c#3 $
*/
/*
* CAM Target Layer backend for a "fake" ramdisk.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/condvar.h>
#include <sys/types.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/malloc.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <sys/conf.h>
#include <sys/ioccom.h>
#include <sys/module.h>
#include <cam/scsi/scsi_all.h>
#include <cam/ctl/ctl_io.h>
#include <cam/ctl/ctl.h>
#include <cam/ctl/ctl_util.h>
#include <cam/ctl/ctl_backend.h>
#include <cam/ctl/ctl_frontend_internal.h>
#include <cam/ctl/ctl_debug.h>
#include <cam/ctl/ctl_ioctl.h>
#include <cam/ctl/ctl_error.h>
typedef enum {
CTL_BE_RAMDISK_LUN_UNCONFIGURED = 0x01,
CTL_BE_RAMDISK_LUN_CONFIG_ERR = 0x02,
CTL_BE_RAMDISK_LUN_WAITING = 0x04
} ctl_be_ramdisk_lun_flags;
struct ctl_be_ramdisk_lun {
uint64_t size_bytes;
uint64_t size_blocks;
struct ctl_be_ramdisk_softc *softc;
ctl_be_ramdisk_lun_flags flags;
STAILQ_ENTRY(ctl_be_ramdisk_lun) links;
struct ctl_be_lun ctl_be_lun;
};
struct ctl_be_ramdisk_softc {
struct mtx lock;
int rd_size;
#ifdef CTL_RAMDISK_PAGES
uint8_t **ramdisk_pages;
int num_pages;
#else
uint8_t *ramdisk_buffer;
#endif
int num_luns;
STAILQ_HEAD(, ctl_be_ramdisk_lun) lun_list;
};
static struct ctl_be_ramdisk_softc rd_softc;
int ctl_backend_ramdisk_init(void);
void ctl_backend_ramdisk_shutdown(void);
static int ctl_backend_ramdisk_move_done(union ctl_io *io);
static int ctl_backend_ramdisk_submit(union ctl_io *io);
static int ctl_backend_ramdisk_ioctl(struct cdev *dev, u_long cmd,
caddr_t addr, int flag, struct thread *td);
static int ctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc,
struct ctl_lun_req *req);
static int ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,
struct ctl_lun_req *req, int do_wait);
static void ctl_backend_ramdisk_lun_shutdown(void *be_lun);
static void ctl_backend_ramdisk_lun_config_status(void *be_lun,
ctl_lun_config_status status);
static int ctl_backend_ramdisk_config_write(union ctl_io *io);
static int ctl_backend_ramdisk_config_read(union ctl_io *io);
static struct ctl_backend_driver ctl_be_ramdisk_driver =
{
name: "ramdisk",
flags: CTL_BE_FLAG_HAS_CONFIG,
init: ctl_backend_ramdisk_init,
data_submit: ctl_backend_ramdisk_submit,
data_move_done: ctl_backend_ramdisk_move_done,
config_read: ctl_backend_ramdisk_config_read,
config_write: ctl_backend_ramdisk_config_write,
ioctl: ctl_backend_ramdisk_ioctl
};
MALLOC_DEFINE(M_RAMDISK, "ramdisk", "Memory used for CTL RAMdisk");
CTL_BACKEND_DECLARE(cbr, ctl_be_ramdisk_driver);
int
ctl_backend_ramdisk_init(void)
{
struct ctl_be_ramdisk_softc *softc;
#ifdef CTL_RAMDISK_PAGES
int i, j;
#endif
softc = &rd_softc;
memset(softc, 0, sizeof(*softc));
mtx_init(&softc->lock, "ramdisk", NULL, MTX_DEF);
STAILQ_INIT(&softc->lun_list);
softc->rd_size = 4 * 1024 * 1024;
#ifdef CTL_RAMDISK_PAGES
softc->num_pages = softc->rd_size / PAGE_SIZE;
softc->ramdisk_pages = (uint8_t **)malloc(sizeof(uint8_t *) *
softc->num_pages, M_RAMDISK,
M_WAITOK);
for (i = 0; i < softc->num_pages; i++) {
softc->ramdisk_pages[i] = malloc(PAGE_SIZE, M_RAMDISK,M_WAITOK);
if (softc->ramdisk_pages[i] == NULL) {
for (j = 0; j < i; j++) {
free(softc->ramdisk_pages[j], M_RAMDISK);
}
free(softc->ramdisk_pages, M_RAMDISK);
panic("RAMDisk initialization failed\n");
return (1); /* NOTREACHED */
}
}
#else
softc->ramdisk_buffer = (uint8_t *)malloc(softc->rd_size, M_RAMDISK,
M_WAITOK);
#endif
return (0);
}
void
ctl_backend_ramdisk_shutdown(void)
{
struct ctl_be_ramdisk_softc *softc;
struct ctl_be_ramdisk_lun *lun, *next_lun;
#ifdef CTL_RAMDISK_PAGES
int i;
#endif
softc = &rd_softc;
mtx_lock(&softc->lock);
for (lun = STAILQ_FIRST(&softc->lun_list); lun != NULL; lun = next_lun){
/*
* Grab the next LUN. The current LUN may get removed by
* ctl_invalidate_lun(), which will call our LUN shutdown
* routine, if there is no outstanding I/O for this LUN.
*/
next_lun = STAILQ_NEXT(lun, links);
/*
* Drop our lock here. Since ctl_invalidate_lun() can call
* back into us, this could potentially lead to a recursive
* lock of the same mutex, which would cause a hang.
*/
mtx_unlock(&softc->lock);
ctl_disable_lun(&lun->ctl_be_lun);
ctl_invalidate_lun(&lun->ctl_be_lun);
mtx_lock(&softc->lock);
}
mtx_unlock(&softc->lock);
#ifdef CTL_RAMDISK_PAGES
for (i = 0; i < softc->num_pages; i++)
free(softc->ramdisk_pages[i], M_RAMDISK);
free(softc->ramdisk_pages, M_RAMDISK);
#else
free(softc->ramdisk_buffer, M_RAMDISK);
#endif
if (ctl_backend_deregister(&ctl_be_ramdisk_driver) != 0) {
printf("ctl_backend_ramdisk_shutdown: "
"ctl_backend_deregister() failed!\n");
}
}
static int
ctl_backend_ramdisk_move_done(union ctl_io *io)
{
#ifdef CTL_TIME_IO
struct bintime cur_bt;
#endif
CTL_DEBUG_PRINT(("ctl_backend_ramdisk_move_done\n"));
if ((io->io_hdr.port_status == 0)
&& ((io->io_hdr.flags & CTL_FLAG_ABORT) == 0)
&& ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE))
io->io_hdr.status = CTL_SUCCESS;
else if ((io->io_hdr.port_status != 0)
&& ((io->io_hdr.flags & CTL_FLAG_ABORT) == 0)
&& ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE)){
/*
* For hardware error sense keys, the sense key
* specific value is defined to be a retry count,
* but we use it to pass back an internal FETD
* error code. XXX KDM Hopefully the FETD is only
* using 16 bits for an error code, since that's
* all the space we have in the sks field.
*/
ctl_set_internal_failure(&io->scsiio,
/*sks_valid*/ 1,
/*retry_count*/
io->io_hdr.port_status);
}
#ifdef CTL_TIME_IO
getbintime(&cur_bt);
bintime_sub(&cur_bt, &io->io_hdr.dma_start_bt);
bintime_add(&io->io_hdr.dma_bt, &cur_bt);
io->io_hdr.num_dmas++;
#endif
if (io->scsiio.kern_sg_entries > 0)
free(io->scsiio.kern_data_ptr, M_RAMDISK);
ctl_done(io);
return(0);
}
static int
ctl_backend_ramdisk_submit(union ctl_io *io)
{
struct ctl_lba_len lbalen;
#ifdef CTL_RAMDISK_PAGES
struct ctl_sg_entry *sg_entries;
int len_filled;
int i;
#endif
int num_sg_entries, len;
struct ctl_be_ramdisk_softc *softc;
struct ctl_be_lun *ctl_be_lun;
struct ctl_be_ramdisk_lun *be_lun;
softc = &rd_softc;
ctl_be_lun = (struct ctl_be_lun *)io->io_hdr.ctl_private[
CTL_PRIV_BACKEND_LUN].ptr;
be_lun = (struct ctl_be_ramdisk_lun *)ctl_be_lun->be_lun;
memcpy(&lbalen, io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN].bytes,
sizeof(lbalen));
len = lbalen.len * ctl_be_lun->blocksize;
/*
* Kick out the request if it's bigger than we can handle.
*/
if (len > softc->rd_size) {
ctl_set_internal_failure(&io->scsiio,
/*sks_valid*/ 0,
/*retry_count*/ 0);
ctl_done(io);
return (CTL_RETVAL_COMPLETE);
}
/*
* Kick out the request if it's larger than the device size that
* the user requested.
*/
if (((lbalen.lba * ctl_be_lun->blocksize) + len) > be_lun->size_bytes) {
ctl_set_lba_out_of_range(&io->scsiio);
ctl_done(io);
return (CTL_RETVAL_COMPLETE);
}
#ifdef CTL_RAMDISK_PAGES
num_sg_entries = len >> PAGE_SHIFT;
if ((len & (PAGE_SIZE - 1)) != 0)
num_sg_entries++;
if (num_sg_entries > 1) {
io->scsiio.kern_data_ptr = malloc(sizeof(struct ctl_sg_entry) *
num_sg_entries, M_RAMDISK,
M_WAITOK);
if (io->scsiio.kern_data_ptr == NULL) {
ctl_set_internal_failure(&io->scsiio,
/*sks_valid*/ 0,
/*retry_count*/ 0);
ctl_done(io);
return (CTL_RETVAL_COMPLETE);
}
sg_entries = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr;
for (i = 0, len_filled = 0; i < num_sg_entries;
i++, len_filled += PAGE_SIZE) {
sg_entries[i].addr = softc->ramdisk_pages[i];
sg_entries[i].len = ctl_min(PAGE_SIZE,
len - len_filled);
}
} else {
#endif /* CTL_RAMDISK_PAGES */
/*
* If this is less than 1 page, don't bother allocating a
* scatter/gather list for it. This saves time/overhead.
*/
num_sg_entries = 0;
#ifdef CTL_RAMDISK_PAGES
io->scsiio.kern_data_ptr = softc->ramdisk_pages[0];
#else
io->scsiio.kern_data_ptr = softc->ramdisk_buffer;
#endif
#ifdef CTL_RAMDISK_PAGES
}
#endif
io->scsiio.be_move_done = ctl_backend_ramdisk_move_done;
io->scsiio.kern_data_len = len;
io->scsiio.kern_total_len = len;
io->scsiio.kern_rel_offset = 0;
io->scsiio.kern_data_resid = 0;
io->scsiio.kern_sg_entries = num_sg_entries;
io->io_hdr.flags |= CTL_FLAG_ALLOCATED | CTL_FLAG_KDPTR_SGLIST;
#ifdef CTL_TIME_IO
getbintime(&io->io_hdr.dma_start_bt);
#endif
ctl_datamove(io);
return (CTL_RETVAL_COMPLETE);
}
static int
ctl_backend_ramdisk_ioctl(struct cdev *dev, u_long cmd, caddr_t addr,
int flag, struct thread *td)
{
struct ctl_be_ramdisk_softc *softc;
int retval;
retval = 0;
softc = &rd_softc;
switch (cmd) {
case CTL_LUN_REQ: {
struct ctl_lun_req *lun_req;
lun_req = (struct ctl_lun_req *)addr;
switch (lun_req->reqtype) {
case CTL_LUNREQ_CREATE:
retval = ctl_backend_ramdisk_create(softc, lun_req,
/*do_wait*/ 1);
break;
case CTL_LUNREQ_RM:
retval = ctl_backend_ramdisk_rm(softc, lun_req);
break;
default:
lun_req->status = CTL_LUN_ERROR;
snprintf(lun_req->error_str, sizeof(lun_req->error_str),
"%s: invalid LUN request type %d", __func__,
lun_req->reqtype);
break;
}
break;
}
default:
retval = ENOTTY;
break;
}
return (retval);
}
static int
ctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc,
struct ctl_lun_req *req)
{
struct ctl_be_ramdisk_lun *be_lun;
struct ctl_lun_rm_params *params;
int retval;
retval = 0;
params = &req->reqdata.rm;
be_lun = NULL;
mtx_lock(&softc->lock);
STAILQ_FOREACH(be_lun, &softc->lun_list, links) {
if (be_lun->ctl_be_lun.lun_id == params->lun_id)
break;
}
mtx_unlock(&softc->lock);
if (be_lun == NULL) {
snprintf(req->error_str, sizeof(req->error_str),
"%s: LUN %u is not managed by the ramdisk backend",
__func__, params->lun_id);
goto bailout_error;
}
retval = ctl_disable_lun(&be_lun->ctl_be_lun);
if (retval != 0) {
snprintf(req->error_str, sizeof(req->error_str),
"%s: error %d returned from ctl_disable_lun() for "
"LUN %d", __func__, retval, params->lun_id);
goto bailout_error;
}
/*
* Set the waiting flag before we invalidate the LUN. Our shutdown
* routine can be called any time after we invalidate the LUN,
* and can be called from our context.
*
* This tells the shutdown routine that we're waiting, or we're
* going to wait for the shutdown to happen.
*/
mtx_lock(&softc->lock);
be_lun->flags |= CTL_BE_RAMDISK_LUN_WAITING;
mtx_unlock(&softc->lock);
retval = ctl_invalidate_lun(&be_lun->ctl_be_lun);
if (retval != 0) {
snprintf(req->error_str, sizeof(req->error_str),
"%s: error %d returned from ctl_invalidate_lun() for "
"LUN %d", __func__, retval, params->lun_id);
goto bailout_error;
}
mtx_lock(&softc->lock);
while ((be_lun->flags & CTL_BE_RAMDISK_LUN_UNCONFIGURED) == 0) {
retval = msleep(be_lun, &softc->lock, PCATCH, "ctlram", 0);
if (retval == EINTR)
break;
}
be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING;
/*
* We only remove this LUN from the list and free it (below) if
* retval == 0. If the user interrupted the wait, we just bail out
* without actually freeing the LUN. We let the shutdown routine
* free the LUN if that happens.
*/
if (retval == 0) {
STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_ramdisk_lun,
links);
softc->num_luns--;
}
mtx_unlock(&softc->lock);
if (retval == 0)
free(be_lun, M_RAMDISK);
req->status = CTL_LUN_OK;
return (retval);
bailout_error:
/*
* Don't leave the waiting flag set.
*/
mtx_lock(&softc->lock);
be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING;
mtx_unlock(&softc->lock);
req->status = CTL_LUN_ERROR;
return (0);
}
static int
ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,
struct ctl_lun_req *req, int do_wait)
{
struct ctl_be_ramdisk_lun *be_lun;
struct ctl_lun_create_params *params;
uint32_t blocksize;
char tmpstr[32];
int retval;
retval = 0;
params = &req->reqdata.create;
if (params->blocksize_bytes != 0)
blocksize = params->blocksize_bytes;
else
blocksize = 512;
be_lun = malloc(sizeof(*be_lun), M_RAMDISK, M_ZERO | (do_wait ?
M_WAITOK : M_NOWAIT));
if (be_lun == NULL) {
snprintf(req->error_str, sizeof(req->error_str),
"%s: error allocating %zd bytes", __func__,
sizeof(*be_lun));
goto bailout_error;
}
if (params->flags & CTL_LUN_FLAG_DEV_TYPE)
be_lun->ctl_be_lun.lun_type = params->device_type;
else
be_lun->ctl_be_lun.lun_type = T_DIRECT;
if (be_lun->ctl_be_lun.lun_type == T_DIRECT) {
if (params->lun_size_bytes < blocksize) {
snprintf(req->error_str, sizeof(req->error_str),
"%s: LUN size %ju < blocksize %u", __func__,
params->lun_size_bytes, blocksize);
goto bailout_error;
}
be_lun->size_blocks = params->lun_size_bytes / blocksize;
be_lun->size_bytes = be_lun->size_blocks * blocksize;
be_lun->ctl_be_lun.maxlba = be_lun->size_blocks - 1;
} else {
be_lun->ctl_be_lun.maxlba = 0;
blocksize = 0;
be_lun->size_bytes = 0;
be_lun->size_blocks = 0;
}
be_lun->ctl_be_lun.blocksize = blocksize;
/* Tell the user the blocksize we ended up using */
params->blocksize_bytes = blocksize;
/* Tell the user the exact size we ended up using */
params->lun_size_bytes = be_lun->size_bytes;
be_lun->softc = softc;
be_lun->flags = CTL_BE_RAMDISK_LUN_UNCONFIGURED;
be_lun->ctl_be_lun.flags = CTL_LUN_FLAG_PRIMARY;
be_lun->ctl_be_lun.be_lun = be_lun;
if (params->flags & CTL_LUN_FLAG_ID_REQ) {
be_lun->ctl_be_lun.req_lun_id = params->req_lun_id;
be_lun->ctl_be_lun.flags |= CTL_LUN_FLAG_ID_REQ;
} else
be_lun->ctl_be_lun.req_lun_id = 0;
be_lun->ctl_be_lun.lun_shutdown = ctl_backend_ramdisk_lun_shutdown;
be_lun->ctl_be_lun.lun_config_status =
ctl_backend_ramdisk_lun_config_status;
be_lun->ctl_be_lun.be = &ctl_be_ramdisk_driver;
if ((params->flags & CTL_LUN_FLAG_SERIAL_NUM) == 0) {
snprintf(tmpstr, sizeof(tmpstr), "MYSERIAL%4d",
softc->num_luns);
strncpy((char *)be_lun->ctl_be_lun.serial_num, tmpstr,
ctl_min(sizeof(be_lun->ctl_be_lun.serial_num),
sizeof(tmpstr)));
/* Tell the user what we used for a serial number */
strncpy((char *)params->serial_num, tmpstr,
ctl_min(sizeof(params->serial_num), sizeof(tmpstr)));
} else {
strncpy((char *)be_lun->ctl_be_lun.serial_num,
params->serial_num,
ctl_min(sizeof(be_lun->ctl_be_lun.serial_num),
sizeof(params->serial_num)));
}
if ((params->flags & CTL_LUN_FLAG_DEVID) == 0) {
snprintf(tmpstr, sizeof(tmpstr), "MYDEVID%4d", softc->num_luns);
strncpy((char *)be_lun->ctl_be_lun.device_id, tmpstr,
ctl_min(sizeof(be_lun->ctl_be_lun.device_id),
sizeof(tmpstr)));
/* Tell the user what we used for a device ID */
strncpy((char *)params->device_id, tmpstr,
ctl_min(sizeof(params->device_id), sizeof(tmpstr)));
} else {
strncpy((char *)be_lun->ctl_be_lun.device_id,
params->device_id,
ctl_min(sizeof(be_lun->ctl_be_lun.device_id),
sizeof(params->device_id)));
}
mtx_lock(&softc->lock);
softc->num_luns++;
STAILQ_INSERT_TAIL(&softc->lun_list, be_lun, links);
mtx_unlock(&softc->lock);
retval = ctl_add_lun(&be_lun->ctl_be_lun);
if (retval != 0) {
mtx_lock(&softc->lock);
STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_ramdisk_lun,
links);
softc->num_luns--;
mtx_unlock(&softc->lock);
snprintf(req->error_str, sizeof(req->error_str),
"%s: ctl_add_lun() returned error %d, see dmesg for "
"details", __func__, retval);
retval = 0;
goto bailout_error;
}
if (do_wait == 0)
return (retval);
mtx_lock(&softc->lock);
/*
* Tell the config_status routine that we're waiting so it won't
* clean up the LUN in the event of an error.
*/
be_lun->flags |= CTL_BE_RAMDISK_LUN_WAITING;
while (be_lun->flags & CTL_BE_RAMDISK_LUN_UNCONFIGURED) {
retval = msleep(be_lun, &softc->lock, PCATCH, "ctlram", 0);
if (retval == EINTR)
break;
}
be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING;
if (be_lun->flags & CTL_BE_RAMDISK_LUN_CONFIG_ERR) {
snprintf(req->error_str, sizeof(req->error_str),
"%s: LUN configuration error, see dmesg for details",
__func__);
STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_ramdisk_lun,
links);
softc->num_luns--;
mtx_unlock(&softc->lock);
goto bailout_error;
} else {
params->req_lun_id = be_lun->ctl_be_lun.lun_id;
}
mtx_unlock(&softc->lock);
req->status = CTL_LUN_OK;
return (retval);
bailout_error:
req->status = CTL_LUN_ERROR;
free(be_lun, M_RAMDISK);
return (retval);
}
static void
ctl_backend_ramdisk_lun_shutdown(void *be_lun)
{
struct ctl_be_ramdisk_lun *lun;
struct ctl_be_ramdisk_softc *softc;
int do_free;
lun = (struct ctl_be_ramdisk_lun *)be_lun;
softc = lun->softc;
do_free = 0;
mtx_lock(&softc->lock);
lun->flags |= CTL_BE_RAMDISK_LUN_UNCONFIGURED;
if (lun->flags & CTL_BE_RAMDISK_LUN_WAITING) {
wakeup(lun);
} else {
STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_ramdisk_lun,
links);
softc->num_luns--;
do_free = 1;
}
mtx_unlock(&softc->lock);
if (do_free != 0)
free(be_lun, M_RAMDISK);
}
static void
ctl_backend_ramdisk_lun_config_status(void *be_lun,
ctl_lun_config_status status)
{
struct ctl_be_ramdisk_lun *lun;
struct ctl_be_ramdisk_softc *softc;
lun = (struct ctl_be_ramdisk_lun *)be_lun;
softc = lun->softc;
if (status == CTL_LUN_CONFIG_OK) {
mtx_lock(&softc->lock);
lun->flags &= ~CTL_BE_RAMDISK_LUN_UNCONFIGURED;
if (lun->flags & CTL_BE_RAMDISK_LUN_WAITING)
wakeup(lun);
mtx_unlock(&softc->lock);
/*
* We successfully added the LUN, attempt to enable it.
*/
if (ctl_enable_lun(&lun->ctl_be_lun) != 0) {
printf("%s: ctl_enable_lun() failed!\n", __func__);
if (ctl_invalidate_lun(&lun->ctl_be_lun) != 0) {
printf("%s: ctl_invalidate_lun() failed!\n",
__func__);
}
}
return;
}
mtx_lock(&softc->lock);
lun->flags &= ~CTL_BE_RAMDISK_LUN_UNCONFIGURED;
/*
* If we have a user waiting, let him handle the cleanup. If not,
* clean things up here.
*/
if (lun->flags & CTL_BE_RAMDISK_LUN_WAITING) {
lun->flags |= CTL_BE_RAMDISK_LUN_CONFIG_ERR;
wakeup(lun);
} else {
STAILQ_REMOVE(&softc->lun_list, lun, ctl_be_ramdisk_lun,
links);
softc->num_luns--;
free(lun, M_RAMDISK);
}
mtx_unlock(&softc->lock);
}
static int
ctl_backend_ramdisk_config_write(union ctl_io *io)
{
struct ctl_be_ramdisk_softc *softc;
int retval;
retval = 0;
softc = &rd_softc;
switch (io->scsiio.cdb[0]) {
case SYNCHRONIZE_CACHE:
case SYNCHRONIZE_CACHE_16:
/*
* The upper level CTL code will filter out any CDBs with
* the immediate bit set and return the proper error. It
* will also not allow a sync cache command to go to a LUN
* that is powered down.
*
* We don't really need to worry about what LBA range the
* user asked to be synced out. When they issue a sync
* cache command, we'll sync out the whole thing.
*
* This is obviously just a stubbed out implementation.
* The real implementation will be in the RAIDCore/CTL
* interface, and can only really happen when RAIDCore
* implements a per-array cache sync.
*/
ctl_set_success(&io->scsiio);
ctl_config_write_done(io);
break;
case START_STOP_UNIT: {
struct scsi_start_stop_unit *cdb;
struct ctl_be_lun *ctl_be_lun;
struct ctl_be_ramdisk_lun *be_lun;
cdb = (struct scsi_start_stop_unit *)io->scsiio.cdb;
ctl_be_lun = (struct ctl_be_lun *)io->io_hdr.ctl_private[
CTL_PRIV_BACKEND_LUN].ptr;
be_lun = (struct ctl_be_ramdisk_lun *)ctl_be_lun->be_lun;
if (cdb->how & SSS_START)
retval = ctl_start_lun(ctl_be_lun);
else {
retval = ctl_stop_lun(ctl_be_lun);
#ifdef NEEDTOPORT
if ((retval == 0)
&& (cdb->byte2 & SSS_ONOFFLINE))
retval = ctl_lun_offline(ctl_be_lun);
#endif
}
/*
* In general, the above routines should not fail. They
* just set state for the LUN. So we've got something
* pretty wrong here if we can't start or stop the LUN.
*/
if (retval != 0) {
ctl_set_internal_failure(&io->scsiio,
/*sks_valid*/ 1,
/*retry_count*/ 0xf051);
retval = CTL_RETVAL_COMPLETE;
} else {
ctl_set_success(&io->scsiio);
}
ctl_config_write_done(io);
break;
}
default:
ctl_set_invalid_opcode(&io->scsiio);
ctl_config_write_done(io);
retval = CTL_RETVAL_COMPLETE;
break;
}
return (retval);
}
static int
ctl_backend_ramdisk_config_read(union ctl_io *io)
{
/*
* XXX KDM need to implement!!
*/
return (0);
}

984
sys/cam/ctl/ctl_cmd_table.c Normal file
View File

@ -0,0 +1,984 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2009 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_cmd_table.c#4 $
* $FreeBSD$
*/
/*
* CAM Target Layer command table.
*
* Author: Ken Merry <ken@FreeBSD.org>, Kim Le
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/condvar.h>
#include <sys/queue.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_da.h>
#include <cam/ctl/ctl_io.h>
#include <cam/ctl/ctl.h>
#include <cam/ctl/ctl_frontend.h>
#include <cam/ctl/ctl_backend.h>
#include <cam/ctl/ctl_frontend_internal.h>
#include <cam/ctl/ctl_ioctl.h>
#include <cam/ctl/ctl_ha.h>
#include <cam/ctl/ctl_private.h>
/*
* Whenever support for a new command is added, it should be added to this
* table.
*/
struct ctl_cmd_entry ctl_cmd_table[] =
{
/* 00 TEST UNIT READY */
{ctl_tur, CTL_SERIDX_TUR, CTL_CMD_FLAG_OK_ON_BOTH |
CTL_FLAG_DATA_NONE |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_TUR},
/* 01 REWIND */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 02 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 03 REQUEST SENSE */
{ctl_request_sense, CTL_SERIDX_RQ_SNS, CTL_FLAG_DATA_IN |
CTL_CMD_FLAG_OK_ON_ALL_LUNS |
CTL_CMD_FLAG_ALLOW_ON_RESV |
CTL_CMD_FLAG_NO_SENSE |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_OFFLINE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_NONE},
/* 04 FORMAT UNIT */
{ctl_format, CTL_SERIDX_FORMAT, CTL_CMD_FLAG_OK_ON_SLUN |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_NONE},
/* 05 READ BLOCK LIMITS */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 06 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 07 REASSIGN BLOCKS */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 08 READ(6) */
{ctl_read_write, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_SLUN |
CTL_FLAG_DATA_IN |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_READ | CTL_LUN_PAT_RANGE},
/* 09 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 0A WRITE(6) */
{ctl_read_write, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_SLUN |
CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE},
/* 0B SEEK(6) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 0C */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 0D */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 0E */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 0F READ REVERSE(6) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 10 WRITE FILEMARKS(6) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 11 SPACE(6) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 12 INQUIRY */
{ctl_inquiry, CTL_SERIDX_INQ, CTL_CMD_FLAG_OK_ON_ALL_LUNS |
CTL_CMD_FLAG_ALLOW_ON_RESV |
CTL_CMD_FLAG_NO_SENSE |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_OFFLINE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_IN |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_NONE},
/* 13 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 14 RECOVER BUFFERED DATA */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 15 MODE SELECT(6) */
{ctl_mode_select, CTL_SERIDX_MD_SEL, CTL_CMD_FLAG_OK_ON_BOTH |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_NONE},
/* 16 RESERVE(6) */
{ctl_scsi_reserve, CTL_SERIDX_RESV, CTL_CMD_FLAG_ALLOW_ON_RESV |
CTL_CMD_FLAG_OK_ON_BOTH |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_NONE},
/* 17 RELEASE(6) */
{ctl_scsi_release, CTL_SERIDX_REL, CTL_CMD_FLAG_ALLOW_ON_RESV |
CTL_CMD_FLAG_OK_ON_BOTH |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_NONE,
CTL_LUN_PAT_NONE},
/* 18 COPY */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 19 ERASE(6) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 1A MODE SENSE(6) */
{ctl_mode_sense, CTL_SERIDX_MD_SNS, CTL_CMD_FLAG_OK_ON_BOTH |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_IN,
CTL_LUN_PAT_NONE},
/* 1B START STOP UNIT */
{ctl_start_stop, CTL_SERIDX_START, CTL_CMD_FLAG_OK_ON_SLUN |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_OFFLINE |
CTL_FLAG_DATA_NONE |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_NONE},
/* 1C RECEIVE DIAGNOSTIC RESULTS */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 1D SEND DIAGNOSTIC */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 1E PREVENT ALLOW MEDIUM REMOVAL */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 1F */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 20 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 21 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 22 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 23 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 24 SET WINDOW */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 25 READ CAPACITY(10) */
{ctl_read_capacity, CTL_SERIDX_RD_CAP, CTL_CMD_FLAG_OK_ON_SLUN|
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_IN |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_READCAP},
/* 26 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 27 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 28 READ(10) */
{ctl_read_write, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_SLUN |
CTL_FLAG_DATA_IN |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_READ | CTL_LUN_PAT_RANGE},
/* 29 READ GENERATION */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 2A WRITE(10) */
{ctl_read_write, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_SLUN| CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE},
/* 2B SEEK(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 2C ERASE(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 2D READ UPDATED BLOCK */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 2E WRITE AND VERIFY(10) */
{ctl_read_write, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_SLUN| CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE},
/* 2F VERIFY(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 30 SEARCH DATA HIGH(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 31 SEARCH DATA EQUAL(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 32 SEARCH DATA LOW(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 33 SET LIMITS(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 34 PRE-FETCH(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 35 SYNCHRONIZE CACHE(10) */
{ctl_sync_cache, CTL_SERIDX_START, CTL_CMD_FLAG_OK_ON_SLUN |
CTL_FLAG_DATA_NONE,
CTL_LUN_PAT_NONE},
/* 36 LOCK UNLOCK CACHE(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 37 READ DEFECT DATA(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 38 MEDIUM SCAN */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 39 COMPARE */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 3A COPY AND VERIFY */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 3B WRITE BUFFER */
{ctl_write_buffer, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_PROC |
CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_NONE},
/* 3C READ BUFFER */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 3D UPDATE BLOCK */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 3E READ LONG */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 3F WRITE LONG */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 40 CHANGE DEFINITION */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 41 WRITE SAME(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 42 READ SUB-CHANNEL */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 43 READ TOC/PMA/ATIP */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 44 REPORT DENSITY SUPPORT */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 45 PLAY AUDIO(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 46 GET CONFIGURATION */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 47 PLAY AUDIO MSF */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 48 PLAY AUDIO TRACK INDEX */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 49 PLAY TRACK RELATIVE(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 4A GET EVENT STATUS NOTIFICATION */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 4B PAUSE/RESUME */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 4C LOG SELECT */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 4D LOG SENSE */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 4E STOP PLAY/SCAN */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 4F */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 50 XDWRITE(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 51 XPWRITE(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 52 XDREAD(10) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 53 RESERVE TRACK */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 54 SEND OPC INFORMATION */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 55 MODE SELECT(10) */
{ctl_mode_select, CTL_SERIDX_MD_SEL, CTL_CMD_FLAG_OK_ON_BOTH |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_NONE},
/* 56 RESERVE(10) */
{ctl_scsi_reserve, CTL_SERIDX_RESV, CTL_CMD_FLAG_ALLOW_ON_RESV |
CTL_CMD_FLAG_OK_ON_BOTH |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_NONE},
/* 57 RELEASE(10) */
{ctl_scsi_release, CTL_SERIDX_REL, CTL_CMD_FLAG_ALLOW_ON_RESV |
CTL_CMD_FLAG_OK_ON_BOTH |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_NONE},
/* 58 REPAIR TRACK */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 59 READ MASTER CUE */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 5A MODE SENSE(10) */
{ctl_mode_sense, CTL_SERIDX_MD_SNS, CTL_CMD_FLAG_OK_ON_BOTH |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_IN,
CTL_LUN_PAT_NONE},
/* 5B CLOSE TRACK/SESSION */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 5C READ BUFFER CAPACITY */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 5D SEND CUE SHEET */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 5E PERSISTENT RESERVE IN */
{ctl_persistent_reserve_in, CTL_SERIDX_PRES_IN, CTL_CMD_FLAG_ALLOW_ON_RESV |
CTL_CMD_FLAG_OK_ON_BOTH |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_IN |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_NONE},
//{ctl_persistent_reserve_in, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE},
/* 5F PERSISTENT RESERVE OUT */
{ctl_persistent_reserve_out, CTL_SERIDX_PRES_OUT, CTL_CMD_FLAG_ALLOW_ON_RESV |
CTL_CMD_FLAG_OK_ON_BOTH |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE|
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_OUT |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_NONE},
//{ctl_persistent_reserve_out, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE},
/* 60 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 61 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 62 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 63 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 64 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 65 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 66 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 67 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 68 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 69 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 6A */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 6B */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 6C */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 6D */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 6E */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 6F */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 70 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 71 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 72 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 73 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 74 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 75 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 76 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 77 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 78 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 79 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 7A */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 7B */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 7C */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 7D */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 7E */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 7F */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 80 XDWRITE EXTENDED(16) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 81 REBUILD(16) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 82 REGENERATE(16) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 83 EXTENDED COPY */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 84 RECEIVE COPY RESULTS */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 85 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 86 ACCESS CONTROL IN */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 87 ACCESS CONTROL OUT */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 88 READ(16) */
{ctl_read_write, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_SLUN | CTL_FLAG_DATA_IN |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_READ | CTL_LUN_PAT_RANGE},
/* 89 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 8A WRITE(16) */
{ctl_read_write, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_SLUN| CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE},
/* 8B */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 8C READ ATTRIBUTE */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 8D WRITE ATTRIBUTE */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 8E WRITE AND VERIFY(16) */
{ctl_read_write, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_SLUN| CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE},
/* 8F VERIFY(16) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 90 PRE-FETCH(16) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 91 SYNCHRONIZE CACHE(16) */
{ctl_sync_cache, CTL_SERIDX_START, CTL_CMD_FLAG_OK_ON_SLUN |
CTL_FLAG_DATA_NONE,
CTL_LUN_PAT_NONE},
/* 92 LOCK UNLOCK CACHE(16) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 93 WRITE SAME(16) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 94 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 95 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 96 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 97 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 98 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 99 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 9A */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 9B */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 9C */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 9D */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 9E SERVICE ACTION IN(16) */
/* XXX KDM not all service actions will be read capacity!! */
{ctl_service_action_in, CTL_SERIDX_RD_CAP, CTL_CMD_FLAG_OK_ON_SLUN |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_IN |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_READCAP},
/* 9F SERVICE ACTION OUT(16) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* A0 REPORT LUNS */
{ctl_report_luns, CTL_SERIDX_INQ, CTL_CMD_FLAG_OK_ON_ALL_LUNS |
CTL_CMD_FLAG_ALLOW_ON_RESV |
CTL_CMD_FLAG_NO_SENSE |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_OFFLINE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_IN |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_NONE},
/* A1 BLANK */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* A2 SEND EVENT */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* A3 MAINTENANCE (IN) Service Action - (0A) REPORT TARGET PORT GROUP */
{ctl_maintenance_in, CTL_SERIDX_MAIN_IN, CTL_CMD_FLAG_OK_ON_BOTH |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
CTL_CMD_FLAG_OK_ON_SECONDARY |
CTL_FLAG_DATA_IN,
CTL_LUN_PAT_NONE},
/* A4 MAINTENANCE (OUT) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* A5 MOVE MEDIUM */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* A6 EXCHANGE MEDIUM */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* A7 MOVE MEDIUM ATTACHED */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* A8 READ(12) */
{ctl_read_write, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_SLUN | CTL_FLAG_DATA_IN |
CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
CTL_LUN_PAT_READ | CTL_LUN_PAT_RANGE},
/* A9 PLAY TRACK RELATIVE(12) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* AA WRITE(12) */
{ctl_read_write, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_SLUN| CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE},
/* AB SERVICE ACTION IN(12) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* AC ERASE(12) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* AD READ DVD STRUCTURE */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* AE WRITE AND VERIFY(12) */
{ctl_read_write, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_SLUN| CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE},
/* AF VERIFY(12) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* B0 SEARCH DATA HIGH(12) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* B1 SEARCH DATA EQUAL(12) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* B2 SEARCH DATA LOW(12) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* B3 SET LIMITS(12) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* B4 READ ELEMENT STATUS ATTACHED */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* B5 REQUEST VOLUME ELEMENT ADDRESS */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* B6 SEND VOLUME TAG */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* B7 READ DEFECT DATA(12) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* B8 READ ELEMENT STATUS */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* B9 READ CD MSF */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* BA REDUNDANCY GROUP (IN) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* BB REDUNDANCY GROUP (OUT) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* BC SPARE (IN) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* BD SPARE (OUT) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* BE VOLUME SET (IN) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* BF VOLUME SET (OUT) */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* C0 - ISC_SEND_MSG_SHORT */
//{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE},
{ctl_isc, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_PROC | CTL_FLAG_DATA_NONE,
CTL_LUN_PAT_NONE},
/* C1 - ISC_SEND_MSG */
//{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE},
{ctl_isc, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_PROC | CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_NONE},
/* C2 - ISC_WRITE */
//{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE},
{ctl_isc, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_PROC | CTL_FLAG_DATA_OUT,
CTL_LUN_PAT_NONE},
/* C3 - ISC_READ */
//{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE},
{ctl_isc, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_PROC | CTL_FLAG_DATA_IN,
CTL_LUN_PAT_NONE},
/* C4 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* C5 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* C6 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* C7 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* C8 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* C9 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* CA */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* CB */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* CC */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* CD */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* CE */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* CF */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* D0 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* D1 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* D2 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* D3 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* D4 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* D5 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* D6 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* D7 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* D8 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* D9 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* DA */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* DB */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* DC */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* DD */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* DE */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* DF */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* E0 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* E1 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* E2 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* E3 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* E4 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* E5 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* E6 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* E7 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* E8 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* E9 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* EA */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* EB */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* EC */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* ED */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* EE */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* EF */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* F0 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* F1 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* F2 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* F3 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* F4 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* F5 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* F6 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* F7 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* F8 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* F9 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* FA */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* FB */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* FC */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* FD */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* FE */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* FF */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE}
};

52
sys/cam/ctl/ctl_debug.h Normal file
View File

@ -0,0 +1,52 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_debug.h#2 $
* $FreeBSD$
*/
/*
* CAM Target Layer debugging interface.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#ifndef _CTL_DEBUG_H_
#define _CTL_DEBUG_H_
#ifdef CAM_CTL_DEBUG
#define CTL_DEBUG_PRINT(X) \
do { \
printf("ctl_debug: "); \
printf X; \
} while (0)
#else /* CAM_CTL_DEBUG */
#define CTL_DEBUG_PRINT(X)
#endif /* CAM_CTL_DEBUG */
#endif /* _CTL_DEBUG_H_ */

811
sys/cam/ctl/ctl_error.c Normal file
View File

@ -0,0 +1,811 @@
/*-
* Copyright (c) 2003-2009 Silicon Graphics International Corp.
* Copyright (c) 2011 Spectra Logic Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_error.c#2 $
*/
/*
* CAM Target Layer error reporting routines.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/stddef.h>
#include <sys/ctype.h>
#include <machine/stdarg.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_da.h>
#include <cam/ctl/ctl_io.h>
#include <cam/ctl/ctl.h>
#include <cam/ctl/ctl_frontend.h>
#include <cam/ctl/ctl_frontend_internal.h>
#include <cam/ctl/ctl_backend.h>
#include <cam/ctl/ctl_ioctl.h>
#include <cam/ctl/ctl_error.h>
#include <cam/ctl/ctl_ha.h>
#include <cam/ctl/ctl_private.h>
void
ctl_set_sense_data_va(struct scsi_sense_data *sense_data, void *lunptr,
scsi_sense_data_type sense_format, int current_error,
int sense_key, int asc, int ascq, va_list ap)
{
struct ctl_lun *lun;
lun = (struct ctl_lun *)lunptr;
/*
* Determine whether to return fixed or descriptor format sense
* data.
*/
if (sense_format == SSD_TYPE_NONE) {
/*
* If the format isn't specified, we only return descriptor
* sense if the LUN exists and descriptor sense is turned
* on for that LUN.
*/
if ((lun != NULL)
&& (lun->flags & CTL_LUN_SENSE_DESC))
sense_format = SSD_TYPE_DESC;
else
sense_format = SSD_TYPE_FIXED;
}
scsi_set_sense_data_va(sense_data, sense_format, current_error,
sense_key, asc, ascq, ap);
}
void
ctl_set_sense_data(struct scsi_sense_data *sense_data, void *lunptr,
scsi_sense_data_type sense_format, int current_error,
int sense_key, int asc, int ascq, ...)
{
va_list ap;
va_start(ap, ascq);
ctl_set_sense_data_va(sense_data, lunptr, sense_format, current_error,
sense_key, asc, ascq, ap);
va_end(ap);
}
void
ctl_set_sense(struct ctl_scsiio *ctsio, int current_error, int sense_key,
int asc, int ascq, ...)
{
va_list ap;
struct ctl_lun *lun;
/*
* The LUN can't go away until all of the commands have been
* completed. Therefore we can safely access the LUN structure and
* flags without the lock.
*/
lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
va_start(ap, ascq);
ctl_set_sense_data_va(&ctsio->sense_data,
lun,
SSD_TYPE_NONE,
current_error,
sense_key,
asc,
ascq,
ap);
va_end(ap);
ctsio->scsi_status = SCSI_STATUS_CHECK_COND;
ctsio->sense_len = SSD_FULL_SIZE;
ctsio->io_hdr.status = CTL_SCSI_ERROR | CTL_AUTOSENSE;
}
/*
* Transform fixed sense data into descriptor sense data.
*
* For simplicity's sake, we assume that both sense structures are
* SSD_FULL_SIZE. Otherwise, the logic gets more complicated.
*/
void
ctl_sense_to_desc(struct scsi_sense_data_fixed *sense_src,
struct scsi_sense_data_desc *sense_dest)
{
struct scsi_sense_stream stream_sense;
int current_error;
uint8_t stream_bits;
bzero(sense_dest, sizeof(*sense_dest));
if ((sense_src->error_code & SSD_ERRCODE) == SSD_DEFERRED_ERROR)
current_error = 0;
else
current_error = 1;
bzero(&stream_sense, sizeof(stream_sense));
/*
* Check to see whether any of the tape-specific bits are set. If
* so, we'll need a stream sense descriptor.
*/
if (sense_src->flags & (SSD_ILI|SSD_EOM|SSD_FILEMARK))
stream_bits = sense_src->flags & ~SSD_KEY;
else
stream_bits = 0;
/*
* Utilize our sense setting routine to do the transform. If a
* value is set in the fixed sense data, set it in the descriptor
* data. Otherwise, skip it.
*/
ctl_set_sense_data((struct scsi_sense_data *)sense_dest,
/*lun*/ NULL,
/*sense_format*/ SSD_TYPE_DESC,
current_error,
/*sense_key*/ sense_src->flags & SSD_KEY,
/*asc*/ sense_src->add_sense_code,
/*ascq*/ sense_src->add_sense_code_qual,
/* Information Bytes */
(scsi_4btoul(sense_src->info) != 0) ?
SSD_ELEM_INFO : SSD_ELEM_SKIP,
sizeof(sense_src->info),
sense_src->info,
/* Command specific bytes */
(scsi_4btoul(sense_src->cmd_spec_info) != 0) ?
SSD_ELEM_COMMAND : SSD_ELEM_SKIP,
sizeof(sense_src->cmd_spec_info),
sense_src->cmd_spec_info,
/* FRU */
(sense_src->fru != 0) ?
SSD_ELEM_FRU : SSD_ELEM_SKIP,
sizeof(sense_src->fru),
&sense_src->fru,
/* Sense Key Specific */
(sense_src->sense_key_spec[0] & SSD_SCS_VALID) ?
SSD_ELEM_SKS : SSD_ELEM_SKIP,
sizeof(sense_src->sense_key_spec),
sense_src->sense_key_spec,
/* Tape bits */
(stream_bits != 0) ?
SSD_ELEM_STREAM : SSD_ELEM_SKIP,
sizeof(stream_bits),
&stream_bits,
SSD_ELEM_NONE);
}
/*
* Transform descriptor format sense data into fixed sense data.
*
* Some data may be lost in translation, because there are descriptors
* thant can't be represented as fixed sense data.
*
* For simplicity's sake, we assume that both sense structures are
* SSD_FULL_SIZE. Otherwise, the logic gets more complicated.
*/
void
ctl_sense_to_fixed(struct scsi_sense_data_desc *sense_src,
struct scsi_sense_data_fixed *sense_dest)
{
int current_error;
uint8_t *info_ptr = NULL, *cmd_ptr = NULL, *fru_ptr = NULL;
uint8_t *sks_ptr = NULL, *stream_ptr = NULL;
int info_size = 0, cmd_size = 0, fru_size = 0;
int sks_size = 0, stream_size = 0;
int pos;
if ((sense_src->error_code & SSD_ERRCODE) == SSD_DESC_CURRENT_ERROR)
current_error = 1;
else
current_error = 0;
for (pos = 0; pos < (int)(sense_src->extra_len - 1);) {
struct scsi_sense_desc_header *header;
header = (struct scsi_sense_desc_header *)
&sense_src->sense_desc[pos];
/*
* See if this record goes past the end of the sense data.
* It shouldn't, but check just in case.
*/
if ((pos + header->length + sizeof(*header)) >
sense_src->extra_len)
break;
switch (sense_src->sense_desc[pos]) {
case SSD_DESC_INFO: {
struct scsi_sense_info *info;
info = (struct scsi_sense_info *)header;
info_ptr = info->info;
info_size = sizeof(info->info);
pos += info->length +
sizeof(struct scsi_sense_desc_header);
break;
}
case SSD_DESC_COMMAND: {
struct scsi_sense_command *cmd;
cmd = (struct scsi_sense_command *)header;
cmd_ptr = cmd->command_info;
cmd_size = sizeof(cmd->command_info);
pos += cmd->length +
sizeof(struct scsi_sense_desc_header);
break;
}
case SSD_DESC_FRU: {
struct scsi_sense_fru *fru;
fru = (struct scsi_sense_fru *)header;
fru_ptr = &fru->fru;
fru_size = sizeof(fru->fru);
pos += fru->length +
sizeof(struct scsi_sense_desc_header);
break;
}
case SSD_DESC_SKS: {
struct scsi_sense_sks *sks;
sks = (struct scsi_sense_sks *)header;
sks_ptr = sks->sense_key_spec;
sks_size = sizeof(sks->sense_key_spec);
pos = sks->length +
sizeof(struct scsi_sense_desc_header);
break;
}
case SSD_DESC_STREAM: {
struct scsi_sense_stream *stream_sense;
stream_sense = (struct scsi_sense_stream *)header;
stream_ptr = &stream_sense->byte3;
stream_size = sizeof(stream_sense->byte3);
pos = stream_sense->length +
sizeof(struct scsi_sense_desc_header);
break;
}
default:
/*
* We don't recognize this particular sense
* descriptor type, so just skip it.
*/
pos += sizeof(*header) + header->length;
break;
}
}
ctl_set_sense_data((struct scsi_sense_data *)sense_dest,
/*lun*/ NULL,
/*sense_format*/ SSD_TYPE_FIXED,
current_error,
/*sense_key*/ sense_src->sense_key & SSD_KEY,
/*asc*/ sense_src->add_sense_code,
/*ascq*/ sense_src->add_sense_code_qual,
/* Information Bytes */
(info_ptr != NULL) ? SSD_ELEM_INFO : SSD_ELEM_SKIP,
info_size,
info_ptr,
/* Command specific bytes */
(cmd_ptr != NULL) ? SSD_ELEM_COMMAND : SSD_ELEM_SKIP,
cmd_size,
cmd_ptr,
/* FRU */
(fru_ptr != NULL) ? SSD_ELEM_FRU : SSD_ELEM_SKIP,
fru_size,
fru_ptr,
/* Sense Key Specific */
(sks_ptr != NULL) ? SSD_ELEM_SKS : SSD_ELEM_SKIP,
sks_size,
sks_ptr,
/* Tape bits */
(stream_ptr != NULL) ? SSD_ELEM_STREAM : SSD_ELEM_SKIP,
stream_size,
stream_ptr,
SSD_ELEM_NONE);
}
ctl_sense_format
ctl_get_sense_format(struct scsi_sense_data *sense_data)
{
switch (sense_data->error_code & SSD_ERRCODE) {
case SSD_DESC_CURRENT_ERROR:
case SSD_DESC_DEFERRED_ERROR:
return (SSD_TYPE_DESC);
case SSD_CURRENT_ERROR:
case SSD_DEFERRED_ERROR:
default:
return (SSD_TYPE_FIXED);
break;
}
}
void
ctl_set_ua(struct ctl_scsiio *ctsio, int asc, int ascq)
{
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_UNIT_ATTENTION,
asc,
ascq,
SSD_ELEM_NONE);
}
ctl_ua_type
ctl_build_ua(ctl_ua_type ua_type, struct scsi_sense_data *sense,
ctl_sense_format sense_format)
{
ctl_ua_type ua_to_build;
int i, asc, ascq;
if (ua_type == CTL_UA_NONE)
return (ua_type);
ua_to_build = CTL_UA_NONE;
for (i = 0; i < (sizeof(ua_type) * 8); i++) {
if (ua_type & (1 << i)) {
ua_to_build = 1 << i;
break;
}
}
switch (ua_to_build) {
case CTL_UA_POWERON:
/* 29h/01h POWER ON OCCURRED */
asc = 0x29;
ascq = 0x01;
break;
case CTL_UA_BUS_RESET:
/* 29h/02h SCSI BUS RESET OCCURRED */
asc = 0x29;
ascq = 0x02;
break;
case CTL_UA_TARG_RESET:
/* 29h/03h BUS DEVICE RESET FUNCTION OCCURRED*/
asc = 0x29;
ascq = 0x03;
break;
case CTL_UA_LUN_RESET:
/* 29h/00h POWER ON, RESET, OR BUS DEVICE RESET OCCURRED */
/*
* Since we don't have a specific ASC/ASCQ pair for a LUN
* reset, just return the generic reset code.
*/
asc = 0x29;
ascq = 0x00;
break;
case CTL_UA_LUN_CHANGE:
/* 3Fh/0Eh REPORTED LUNS DATA HAS CHANGED */
asc = 0x3F;
ascq = 0x0E;
break;
case CTL_UA_MODE_CHANGE:
/* 2Ah/01h MODE PARAMETERS CHANGED */
asc = 0x2A;
ascq = 0x01;
break;
case CTL_UA_LOG_CHANGE:
/* 2Ah/02h LOG PARAMETERS CHANGED */
asc = 0x2A;
ascq = 0x02;
break;
case CTL_UA_LVD:
/* 29h/06h TRANSCEIVER MODE CHANGED TO LVD */
asc = 0x29;
ascq = 0x06;
break;
case CTL_UA_SE:
/* 29h/05h TRANSCEIVER MODE CHANGED TO SINGLE-ENDED */
asc = 0x29;
ascq = 0x05;
break;
case CTL_UA_RES_PREEMPT:
/* 2Ah/03h RESERVATIONS PREEMPTED */
asc = 0x2A;
ascq = 0x03;
break;
case CTL_UA_RES_RELEASE:
/* 2Ah/04h RESERVATIONS RELEASED */
asc = 0x2A;
ascq = 0x04;
break;
case CTL_UA_REG_PREEMPT:
/* 2Ah/05h REGISTRATIONS PREEMPTED */
asc = 0x2A;
ascq = 0x05;
break;
case CTL_UA_ASYM_ACC_CHANGE:
/* 2Ah/06n ASYMMETRIC ACCESS STATE CHANGED */
asc = 0x2A;
ascq = 0x06;
break;
default:
ua_to_build = CTL_UA_NONE;
return (ua_to_build);
break; /* NOTREACHED */
}
ctl_set_sense_data(sense,
/*lun*/ NULL,
sense_format,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_UNIT_ATTENTION,
asc,
ascq,
SSD_ELEM_NONE);
return (ua_to_build);
}
void
ctl_set_overlapped_cmd(struct ctl_scsiio *ctsio)
{
/* OVERLAPPED COMMANDS ATTEMPTED */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
/*asc*/ 0x4E,
/*ascq*/ 0x00,
SSD_ELEM_NONE);
}
void
ctl_set_overlapped_tag(struct ctl_scsiio *ctsio, uint8_t tag)
{
/* TAGGED OVERLAPPED COMMANDS (NN = QUEUE TAG) */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
/*asc*/ 0x4D,
/*ascq*/ tag,
SSD_ELEM_NONE);
}
/*
* Tell the user that there was a problem with the command or data he sent.
*/
void
ctl_set_invalid_field(struct ctl_scsiio *ctsio, int sks_valid, int command,
int field, int bit_valid, int bit)
{
uint8_t sks[3];
int asc;
if (command != 0) {
/* "Invalid field in CDB" */
asc = 0x24;
} else {
/* "Invalid field in parameter list" */
asc = 0x26;
}
if (sks_valid) {
sks[0] = SSD_SCS_VALID;
if (command)
sks[0] |= SSD_FIELDPTR_CMD;
scsi_ulto2b(field, &sks[1]);
if (bit_valid)
sks[0] |= SSD_BITPTR_VALID | bit;
}
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
asc,
/*ascq*/ 0x00,
/*type*/ (sks_valid != 0) ? SSD_ELEM_SKS : SSD_ELEM_SKIP,
/*size*/ sizeof(sks),
/*data*/ sks,
SSD_ELEM_NONE);
}
void
ctl_set_invalid_opcode(struct ctl_scsiio *ctsio)
{
struct scsi_sense_data *sense;
uint8_t sks[3];
sense = &ctsio->sense_data;
sks[0] = SSD_SCS_VALID | SSD_FIELDPTR_CMD;
scsi_ulto2b(0, &sks[1]);
/* "Invalid command operation code" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
/*asc*/ 0x20,
/*ascq*/ 0x00,
/*type*/ SSD_ELEM_SKS,
/*size*/ sizeof(sks),
/*data*/ sks,
SSD_ELEM_NONE);
}
void
ctl_set_param_len_error(struct ctl_scsiio *ctsio)
{
/* "Parameter list length error" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
/*asc*/ 0x1a,
/*ascq*/ 0x00,
SSD_ELEM_NONE);
}
void
ctl_set_already_locked(struct ctl_scsiio *ctsio)
{
/* Vendor unique "Somebody already is locked" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
/*asc*/ 0x81,
/*ascq*/ 0x00,
SSD_ELEM_NONE);
}
void
ctl_set_unsupported_lun(struct ctl_scsiio *ctsio)
{
/* "Logical unit not supported" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
/*asc*/ 0x25,
/*ascq*/ 0x00,
SSD_ELEM_NONE);
}
void
ctl_set_internal_failure(struct ctl_scsiio *ctsio, int sks_valid,
uint16_t retry_count)
{
uint8_t sks[3];
if (sks_valid) {
sks[0] = SSD_SCS_VALID;
sks[1] = (retry_count >> 8) & 0xff;
sks[2] = retry_count & 0xff;
}
/* "Internal target failure" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_HARDWARE_ERROR,
/*asc*/ 0x44,
/*ascq*/ 0x00,
/*type*/ (sks_valid != 0) ? SSD_ELEM_SKS : SSD_ELEM_SKIP,
/*size*/ sizeof(sks),
/*data*/ sks,
SSD_ELEM_NONE);
}
void
ctl_set_medium_error(struct ctl_scsiio *ctsio)
{
if ((ctsio->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN) {
/* "Unrecovered read error" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_MEDIUM_ERROR,
/*asc*/ 0x11,
/*ascq*/ 0x00,
SSD_ELEM_NONE);
} else {
/* "Write error - auto reallocation failed" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_MEDIUM_ERROR,
/*asc*/ 0x0C,
/*ascq*/ 0x02,
SSD_ELEM_NONE);
}
}
void
ctl_set_aborted(struct ctl_scsiio *ctsio)
{
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_ABORTED_COMMAND,
/*asc*/ 0x45,
/*ascq*/ 0x00,
SSD_ELEM_NONE);
}
void
ctl_set_lba_out_of_range(struct ctl_scsiio *ctsio)
{
/* "Logical block address out of range" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
/*asc*/ 0x21,
/*ascq*/ 0x00,
SSD_ELEM_NONE);
}
void
ctl_set_lun_stopped(struct ctl_scsiio *ctsio)
{
/* "Logical unit not ready, initializing cmd. required" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_NOT_READY,
/*asc*/ 0x04,
/*ascq*/ 0x02,
SSD_ELEM_NONE);
}
void
ctl_set_lun_not_ready(struct ctl_scsiio *ctsio)
{
/* "Logical unit not ready, manual intervention required" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_NOT_READY,
/*asc*/ 0x04,
/*ascq*/ 0x05,
SSD_ELEM_NONE);
}
void
ctl_set_illegal_pr_release(struct ctl_scsiio *ctsio)
{
/* "Invalid release of persistent reservation" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
/*asc*/ 0x26,
/*ascq*/ 0x04,
SSD_ELEM_NONE);
}
void
ctl_set_lun_standby(struct ctl_scsiio *ctsio)
{
/* "Logical unit not ready, target port in standby state" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_NOT_READY,
/*asc*/ 0x04,
/*ascq*/ 0x0b,
SSD_ELEM_NONE);
}
void
ctl_set_medium_format_corrupted(struct ctl_scsiio *ctsio)
{
/* "Medium format corrupted" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_MEDIUM_ERROR,
/*asc*/ 0x31,
/*ascq*/ 0x00,
SSD_ELEM_NONE);
}
void
ctl_set_medium_magazine_inaccessible(struct ctl_scsiio *ctsio)
{
/* "Medium magazine not accessible" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_NOT_READY,
/*asc*/ 0x3b,
/*ascq*/ 0x11,
SSD_ELEM_NONE);
}
void
ctl_set_data_phase_error(struct ctl_scsiio *ctsio)
{
/* "Data phase error" */
ctl_set_sense(ctsio,
/*current_error*/ 1,
/*sense_key*/ SSD_KEY_NOT_READY,
/*asc*/ 0x4b,
/*ascq*/ 0x00,
SSD_ELEM_NONE);
}
void
ctl_set_reservation_conflict(struct ctl_scsiio *ctsio)
{
struct scsi_sense_data *sense;
sense = &ctsio->sense_data;
memset(sense, 0, sizeof(*sense));
ctsio->scsi_status = SCSI_STATUS_RESERV_CONFLICT;
ctsio->sense_len = 0;
ctsio->io_hdr.status = CTL_SCSI_ERROR;
}
void
ctl_set_queue_full(struct ctl_scsiio *ctsio)
{
struct scsi_sense_data *sense;
sense = &ctsio->sense_data;
memset(sense, 0, sizeof(*sense));
ctsio->scsi_status = SCSI_STATUS_QUEUE_FULL;
ctsio->sense_len = 0;
ctsio->io_hdr.status = CTL_SCSI_ERROR;
}
void
ctl_set_busy(struct ctl_scsiio *ctsio)
{
struct scsi_sense_data *sense;
sense = &ctsio->sense_data;
memset(sense, 0, sizeof(*sense));
ctsio->scsi_status = SCSI_STATUS_BUSY;
ctsio->sense_len = 0;
ctsio->io_hdr.status = CTL_SCSI_ERROR;
}
void
ctl_set_success(struct ctl_scsiio *ctsio)
{
struct scsi_sense_data *sense;
sense = &ctsio->sense_data;
memset(sense, 0, sizeof(*sense));
ctsio->scsi_status = SCSI_STATUS_OK;
ctsio->sense_len = 0;
ctsio->io_hdr.status = CTL_SUCCESS;
}

92
sys/cam/ctl/ctl_error.h Normal file
View File

@ -0,0 +1,92 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_error.h#1 $
* $FreeBSD$
*/
/*
* Function definitions for various error reporting routines used both
* within CTL and various CTL clients.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#include <machine/stdarg.h>
#ifndef _CTL_ERROR_H_
#define _CTL_ERROR_H_
typedef enum {
CTL_SENSE_NOT_SPECIFIED,
CTL_SENSE_FIXED,
CTL_SENSE_DESCRIPTOR
} ctl_sense_format;
void ctl_set_sense_data_va(struct scsi_sense_data *sense_data, void *lun,
scsi_sense_data_type sense_format, int current_error,
int sense_key, int asc, int ascq, va_list ap);
void ctl_set_sense_data(struct scsi_sense_data *sense_data, void *lun,
scsi_sense_data_type sense_format, int current_error,
int sense_key, int asc, int ascq, ...);
void ctl_set_sense(struct ctl_scsiio *ctsio, int current_error, int sense_key,
int asc, int ascq, ...);
void ctl_sense_to_desc(struct scsi_sense_data_fixed *sense_src,
struct scsi_sense_data_desc *sense_dest);
void ctl_sense_to_fixed(struct scsi_sense_data_desc *sense_src,
struct scsi_sense_data_fixed *sense_dest);
ctl_sense_format ctl_get_sense_format(struct scsi_sense_data *sense_data);
void ctl_set_ua(struct ctl_scsiio *ctsio, int asc, int ascq);
ctl_ua_type ctl_build_ua(ctl_ua_type ua_type, struct scsi_sense_data *sense,
ctl_sense_format sense_format);
void ctl_set_overlapped_cmd(struct ctl_scsiio *ctsio);
void ctl_set_overlapped_tag(struct ctl_scsiio *ctsio, uint8_t tag);
void ctl_set_invalid_field(struct ctl_scsiio *ctsio, int sks_valid, int command,
int field, int bit_valid, int bit);
void ctl_set_invalid_opcode(struct ctl_scsiio *ctsio);
void ctl_set_param_len_error(struct ctl_scsiio *ctsio);
void ctl_set_already_locked(struct ctl_scsiio *ctsio);
void ctl_set_unsupported_lun(struct ctl_scsiio *ctsio);
void ctl_set_lun_standby(struct ctl_scsiio *ctsio);
void ctl_set_internal_failure(struct ctl_scsiio *ctsio, int sks_valid,
uint16_t retry_count);
void ctl_set_medium_error(struct ctl_scsiio *ctsio);
void ctl_set_aborted(struct ctl_scsiio *ctsio);
void ctl_set_lba_out_of_range(struct ctl_scsiio *ctsio);
void ctl_set_lun_stopped(struct ctl_scsiio *ctsio);
void ctl_set_lun_not_ready(struct ctl_scsiio *ctsio);
void ctl_set_illegal_pr_release(struct ctl_scsiio *ctsio);
void ctl_set_medium_format_corrupted(struct ctl_scsiio *ctsio);
void ctl_set_medium_magazine_inaccessible(struct ctl_scsiio *ctsio);
void ctl_set_data_phase_error(struct ctl_scsiio *ctsio);
void ctl_set_reservation_conflict(struct ctl_scsiio *ctsio);
void ctl_set_queue_full(struct ctl_scsiio *ctsio);
void ctl_set_busy(struct ctl_scsiio *ctsio);
void ctl_set_success(struct ctl_scsiio *ctsio);
#endif /* _CTL_ERROR_H_ */

187
sys/cam/ctl/ctl_frontend.c Normal file
View File

@ -0,0 +1,187 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_frontend.c#4 $
*/
/*
* CAM Target Layer front end interface code
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/endian.h>
#include <sys/queue.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_da.h>
#include <cam/ctl/ctl_io.h>
#include <cam/ctl/ctl.h>
#include <cam/ctl/ctl_frontend.h>
#include <cam/ctl/ctl_frontend_internal.h>
#include <cam/ctl/ctl_backend.h>
/* XXX KDM move defines from ctl_ioctl.h to somewhere else */
#include <cam/ctl/ctl_ioctl.h>
#include <cam/ctl/ctl_ha.h>
#include <cam/ctl/ctl_private.h>
#include <cam/ctl/ctl_debug.h>
extern struct ctl_softc *control_softc;
int
ctl_frontend_register(struct ctl_frontend *fe, int master_shelf)
{
struct ctl_io_pool *pool;
int port_num;
int retval;
retval = 0;
KASSERT(control_softc != NULL, ("CTL is not initialized"));
mtx_lock(&control_softc->ctl_lock);
port_num = ctl_ffz(&control_softc->ctl_port_mask, CTL_MAX_PORTS);
if ((port_num == -1)
|| (ctl_set_mask(&control_softc->ctl_port_mask, port_num) == -1)) {
fe->targ_port = -1;
mtx_unlock(&control_softc->ctl_lock);
return (1);
}
control_softc->num_frontends++;
mtx_unlock(&control_softc->ctl_lock);
/*
* We add 20 to whatever the caller requests, so he doesn't get
* burned by queueing things back to the pending sense queue. In
* theory, there should probably only be one outstanding item, at
* most, on the pending sense queue for a LUN. We'll clear the
* pending sense queue on the next command, whether or not it is
* a REQUEST SENSE.
*/
retval = ctl_pool_create(control_softc,
(fe->port_type != CTL_PORT_IOCTL) ?
CTL_POOL_FETD : CTL_POOL_IOCTL,
fe->num_requested_ctl_io + 20, &pool);
if (retval != 0) {
fe->targ_port = -1;
mtx_lock(&control_softc->ctl_lock);
ctl_clear_mask(&control_softc->ctl_port_mask, port_num);
mtx_unlock(&control_softc->ctl_lock);
return (retval);
}
mtx_lock(&control_softc->ctl_lock);
/* For now assume master shelf */
//fe->targ_port = port_num;
fe->targ_port = port_num + (master_shelf!=0 ? 0 : CTL_MAX_PORTS);
fe->max_initiators = CTL_MAX_INIT_PER_PORT;
STAILQ_INSERT_TAIL(&control_softc->fe_list, fe, links);
ctl_pool_acquire(pool);
control_softc->ctl_ports[port_num] = fe;
mtx_unlock(&control_softc->ctl_lock);
fe->ctl_pool_ref = pool;
return (retval);
}
int
ctl_frontend_deregister(struct ctl_frontend *fe)
{
struct ctl_io_pool *pool;
int port_num;
int retval;
retval = 0;
pool = (struct ctl_io_pool *)fe->ctl_pool_ref;
if (fe->targ_port == -1) {
retval = 1;
goto bailout;
}
mtx_lock(&control_softc->ctl_lock);
ctl_pool_invalidate(pool);
ctl_pool_release(pool);
STAILQ_REMOVE(&control_softc->fe_list, fe, ctl_frontend, links);
control_softc->num_frontends--;
port_num = (fe->targ_port < CTL_MAX_PORTS) ? fe->targ_port :
fe->targ_port - CTL_MAX_PORTS;
ctl_clear_mask(&control_softc->ctl_port_mask, port_num);
control_softc->ctl_ports[port_num] = NULL;
mtx_unlock(&control_softc->ctl_lock);
bailout:
return (retval);
}
void
ctl_frontend_set_wwns(struct ctl_frontend *fe, int wwnn_valid, uint64_t wwnn,
int wwpn_valid, uint64_t wwpn)
{
if (wwnn_valid)
fe->wwnn = wwnn;
if (wwpn_valid)
fe->wwpn = wwpn;
}
void
ctl_frontend_online(struct ctl_frontend *fe)
{
fe->port_online(fe->onoff_arg);
/* XXX KDM need a lock here? */
fe->status |= CTL_PORT_STATUS_ONLINE;
}
void
ctl_frontend_offline(struct ctl_frontend *fe)
{
fe->port_offline(fe->onoff_arg);
/* XXX KDM need a lock here? */
fe->status &= ~CTL_PORT_STATUS_ONLINE;
}
/*
* vim: ts=8
*/

295
sys/cam/ctl/ctl_frontend.h Normal file
View File

@ -0,0 +1,295 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_frontend.h#2 $
* $FreeBSD$
*/
/*
* CAM Target Layer front end registration hooks
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#ifndef _CTL_FRONTEND_H_
#define _CTL_FRONTEND_H_
typedef enum {
CTL_PORT_STATUS_NONE = 0x00,
CTL_PORT_STATUS_ONLINE = 0x01,
CTL_PORT_STATUS_TARG_ONLINE = 0x02,
CTL_PORT_STATUS_LUN_ONLINE = 0x04
} ctl_port_status;
typedef void (*port_func_t)(void *onoff_arg);
typedef int (*targ_func_t)(void *arg, struct ctl_id targ_id);
typedef int (*lun_func_t)(void *arg, struct ctl_id targ_id, int lun_id);
/*
* The ctl_frontend structure is the registration mechanism between a FETD
* (Front End Target Driver) and the CTL layer. Here is a description of
* the fields:
*
* port_type: This field tells CTL what kind of front end it is
* dealing with. This field serves two purposes.
* The first is to let CTL know whether the frontend
* in question is inside the main CTL module (i.e.
* the ioctl front end), and therefore its module
* reference count shouldn't be incremented. The
* CTL ioctl front end should continue to use the
* CTL_PORT_IOCTL argument as long as it is part of
* the main CTL module. The second is to let CTL
* know what kind of front end it is dealing with, so
* it can return the proper inquiry data for that
* particular port.
*
* num_requested_ctl_io: This is the number of ctl_io structures that the
* front end needs for its pool. This should
* generally be the maximum number of outstanding
* transactions that the FETD can handle. The CTL
* layer will add a few to this to account for
* ctl_io buffers queued for pending sense data.
* (Pending sense only gets queued if the FETD
* doesn't support autosense. e.g. non-packetized
* parallel SCSI doesn't support autosense.)
*
* port_name: A string describing the FETD. e.g. "LSI 1030T U320"
* or whatever you want to use to describe the driver.
*
*
* physical_port: This is the physical port number of this
* particular port within the driver/hardware. This
* number is hardware/driver specific.
* virtual_port: This is the virtual port number of this
* particular port. This is for things like NP-IV.
*
* port_online(): This function is called, with onoff_arg as its
* argument, by the CTL layer when it wants the FETD
* to start responding to selections on the specified
* target ID. (targ_target)
*
* port_offline(): This function is called, with onoff_arg as its
* argument, by the CTL layer when it wants the FETD
* to stop responding to selection on the specified
* target ID. (targ_target)
*
* onoff_arg: This is supplied as an argument to port_online()
* and port_offline(). This is specified by the
* FETD.
*
* targ_enable(): This function is called, with targ_lun_arg and a
* target ID as its arguments, by CTL when it wants
* the FETD to enable a particular target. targ_enable()
* will always be called for a particular target ID
* before any LUN is enabled for that target. If the
* FETD does not support enabling targets, but rather
* LUNs, it should ignore this call and return 0. If
* the FETD does support enabling targets, it should
* return 0 for success and non-zero if it cannot
* enable the given target.
*
* TODO: Add the ability to specify a WWID here.
*
* targ_disable(): This function is called, with targ_lun_arg and a
* target ID as its arguments, by CTL when it wants
* the FETD to disable a particular target.
* targ_disable() will always be called for a
* particular target ID after all LUNs are disabled
* on that particular target. If the FETD does not
* support enabling targets, it should ignore this
* call and return 0. If the FETD does support
* enabling targets, it should return 0 for success,
* and non-zero if it cannot disable the given target.
*
* lun_enable(): This function is called, with targ_lun_arg, a target
* ID and a LUN ID as its arguments, by CTL when it
* wants the FETD to enable a particular LUN. If the
* FETD doesn't really know about LUNs, it should
* just ignore this call and return 0. If the FETD
* cannot enable the requested LUN for some reason, the
* FETD should return non-zero status.
*
* lun_disable(): This function is called, with targ_lun_arg, a target
* ID and LUN ID as its arguments, by CTL when it
* wants the FETD to disable a particular LUN. If the
* FETD doesn't really know about LUNs, it should just
* ignore this call and return 0. If the FETD cannot
* disable the requested LUN for some reason, the
* FETD should return non-zero status.
*
* targ_lun_arg: This is supplied as an argument to the targ/lun
* enable/disable() functions. This is specified by
* the FETD.
*
* fe_datamove(): This function is called one or more times per I/O
* by the CTL layer to tell the FETD to initiate a
* DMA to or from the data buffer(s) specified by
* the passed-in ctl_io structure.
*
* fe_done(): This function is called by the CTL layer when a
* particular SCSI I/O or task management command has
* completed. For SCSI I/O requests (CTL_IO_SCSI),
* sense data is always supplied if the status is
* CTL_SCSI_ERROR and the SCSI status byte is
* SCSI_STATUS_CHECK_COND. If the FETD doesn't
* support autosense, the sense should be queued
* back to the CTL layer via ctl_queue_sense().
*
* fe_dump(): This function, if it exists, is called by CTL
* to request a dump of any debugging information or
* state to the console.
*
* max_targets: The maximum number of targets that we can create
* per-port.
*
* max_target_id: The highest target ID that we can use.
*
* targ_port: The CTL layer assigns a "port number" to every
* FETD. This port number should be passed back in
* in the header of every ctl_io that is queued to
* the CTL layer. This enables us to determine
* which bus the command came in on.
*
* ctl_pool_ref: Memory pool reference used by the FETD in calls to
* ctl_alloc_io().
*
* max_initiators: Maximum number of initiators that the FETD is
* allowed to have. Initiators should be numbered
* from 0 to max_initiators - 1. This value will
* typically be 16, and thus not a problem for
* parallel SCSI. This may present issues for Fibre
* Channel.
*
* wwnn World Wide Node Name to be used by the FETD.
* Note that this is set *after* registration. It
* will be set prior to the online function getting
* called.
*
* wwpn World Wide Port Name to be used by the FETD.
* Note that this is set *after* registration. It
* will be set prior to the online function getting
* called.
*
* status: Used by CTL to keep track of per-FETD state.
*
* links: Linked list pointers, used by CTL. The FETD
* shouldn't touch this field.
*/
struct ctl_frontend {
ctl_port_type port_type; /* passed to CTL */
int num_requested_ctl_io; /* passed to CTL */
char *port_name; /* passed to CTL */
int physical_port; /* passed to CTL */
int virtual_port; /* passed to CTL */
port_func_t port_online; /* passed to CTL */
port_func_t port_offline; /* passed to CTL */
void *onoff_arg; /* passed to CTL */
targ_func_t targ_enable; /* passed to CTL */
targ_func_t targ_disable; /* passed to CTL */
lun_func_t lun_enable; /* passed to CTL */
lun_func_t lun_disable; /* passed to CTL */
void *targ_lun_arg; /* passed to CTL */
void (*fe_datamove)(union ctl_io *io); /* passed to CTL */
void (*fe_done)(union ctl_io *io); /* passed to CTL */
void (*fe_dump)(void); /* passed to CTL */
int max_targets; /* passed to CTL */
int max_target_id; /* passed to CTL */
int32_t targ_port; /* passed back to FETD */
void *ctl_pool_ref; /* passed back to FETD */
uint32_t max_initiators; /* passed back to FETD */
uint64_t wwnn; /* set by CTL before online */
uint64_t wwpn; /* set by CTL before online */
ctl_port_status status; /* used by CTL */
STAILQ_ENTRY(ctl_frontend) links; /* used by CTL */
};
/*
* This may block until resources are allocated. Called at FETD module load
* time. Returns 0 for success, non-zero for failure.
*/
int ctl_frontend_register(struct ctl_frontend *fe, int master_SC);
/*
* Called at FETD module unload time.
* Returns 0 for success, non-zero for failure.
*/
int ctl_frontend_deregister(struct ctl_frontend *fe);
/*
* Called to set the WWNN and WWPN for a particular frontend.
*/
void ctl_frontend_set_wwns(struct ctl_frontend *fe, int wwnn_valid,
uint64_t wwnn, int wwpn_valid, uint64_t wwpn);
/*
* Called to bring a particular frontend online.
*/
void ctl_frontend_online(struct ctl_frontend *fe);
/*
* Called to take a particular frontend offline.
*/
void ctl_frontend_offline(struct ctl_frontend *fe);
/*
* This routine queues I/O and task management requests from the FETD to the
* CTL layer. Returns immediately. Returns 0 for success, non-zero for
* failure.
*/
int ctl_queue(union ctl_io *io);
/*
* This routine is used if the front end interface doesn't support
* autosense (e.g. non-packetized parallel SCSI). This will queue the
* scsiio structure back to a per-lun pending sense queue. This MUST be
* called BEFORE any request sense can get queued to the CTL layer -- I
* need it in the queue in order to service the request. The scsiio
* structure passed in here will be freed by the CTL layer when sense is
* retrieved by the initiator. Returns 0 for success, non-zero for failure.
*/
int ctl_queue_sense(union ctl_io *io);
/*
* This routine adds an initiator to CTL's port database. The WWPN should
* be the FC WWPN, if available. The targ_port field should be the same as
* the targ_port passed back from CTL in the ctl_frontend structure above.
* The iid field should be the same as the iid passed in the nexus of each
* ctl_io from this initiator.
*/
int ctl_add_initiator(uint64_t wwpn, int32_t targ_port, uint32_t iid);
/*
* This routine will remove an initiator from CTL's port database. The
* targ_port field should be the same as the targ_port passed back in the
* ctl_frontend structure above. The iid field should be the same as the
* iid passed in the nexus of each ctl_io from this initiator.
*/
int
ctl_remove_initiator(int32_t targ_port, uint32_t iid);
#endif /* _CTL_FRONTEND_H_ */

View File

@ -0,0 +1,866 @@
/*-
* Copyright (c) 2009 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_frontend_cam_sim.c#4 $
*/
/*
* CTL frontend to CAM SIM interface. This allows access to CTL LUNs via
* the da(4) and pass(4) drivers from inside the system.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/queue.h>
#include <sys/bus.h>
#include <sys/sysctl.h>
#include <machine/bus.h>
#include <sys/sbuf.h>
#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <cam/cam_sim.h>
#include <cam/cam_xpt_sim.h>
#include <cam/cam_xpt.h>
#include <cam/cam_periph.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_message.h>
#include <cam/ctl/ctl_io.h>
#include <cam/ctl/ctl.h>
#include <cam/ctl/ctl_frontend.h>
#include <cam/ctl/ctl_frontend_internal.h>
#include <cam/ctl/ctl_mem_pool.h>
#include <cam/ctl/ctl_debug.h>
#define io_ptr spriv_ptr1
struct cfcs_io {
union ccb *ccb;
};
struct cfcs_softc {
struct ctl_frontend fe;
char port_name[32];
struct cam_sim *sim;
struct cam_devq *devq;
struct cam_path *path;
struct mtx lock;
char lock_desc[32];
uint64_t wwnn;
uint64_t wwpn;
uint32_t cur_tag_num;
int online;
};
/*
* We can't handle CCBs with these flags. For the most part, we just don't
* handle physical addresses yet. That would require mapping things in
* order to do the copy.
*/
#define CFCS_BAD_CCB_FLAGS (CAM_DATA_PHYS | CAM_SG_LIST_PHYS | \
CAM_MSG_BUF_PHYS | CAM_SNS_BUF_PHYS | CAM_CDB_PHYS | CAM_SENSE_PTR |\
CAM_SENSE_PHYS)
int cfcs_init(void);
void cfcs_shutdown(void);
static void cfcs_poll(struct cam_sim *sim);
static void cfcs_online(void *arg);
static void cfcs_offline(void *arg);
static int cfcs_targ_enable(void *arg, struct ctl_id targ_id);
static int cfcs_targ_disable(void *arg, struct ctl_id targ_id);
static int cfcs_lun_enable(void *arg, struct ctl_id target_id, int lun_id);
static int cfcs_lun_disable(void *arg, struct ctl_id target_id, int lun_id);
static void cfcs_datamove(union ctl_io *io);
static void cfcs_done(union ctl_io *io);
void cfcs_action(struct cam_sim *sim, union ccb *ccb);
static void cfcs_async(void *callback_arg, uint32_t code,
struct cam_path *path, void *arg);
struct cfcs_softc cfcs_softc;
/*
* This is primarly intended to allow for error injection to test the CAM
* sense data and sense residual handling code. This sets the maximum
* amount of SCSI sense data that we will report to CAM.
*/
static int cfcs_max_sense = sizeof(struct scsi_sense_data);
SYSINIT(cfcs_init, SI_SUB_CONFIGURE, SI_ORDER_FOURTH, cfcs_init, NULL);
SYSCTL_NODE(_kern_cam, OID_AUTO, ctl2cam, CTLFLAG_RD, 0,
"CAM Target Layer SIM frontend");
SYSCTL_INT(_kern_cam_ctl2cam, OID_AUTO, max_sense, CTLFLAG_RW,
&cfcs_max_sense, 0, "Maximum sense data size");
int
cfcs_init(void)
{
struct cfcs_softc *softc;
struct ccb_setasync csa;
struct ctl_frontend *fe;
#ifdef NEEDTOPORT
char wwnn[8];
#endif
int retval;
softc = &cfcs_softc;
retval = 0;
bzero(softc, sizeof(*softc));
sprintf(softc->lock_desc, "ctl2cam");
mtx_init(&softc->lock, softc->lock_desc, NULL, MTX_DEF);
fe = &softc->fe;
fe->port_type = CTL_PORT_INTERNAL;
/* XXX KDM what should the real number be here? */
fe->num_requested_ctl_io = 4096;
snprintf(softc->port_name, sizeof(softc->port_name), "ctl2cam");
fe->port_name = softc->port_name;
fe->port_online = cfcs_online;
fe->port_offline = cfcs_offline;
fe->onoff_arg = softc;
fe->targ_enable = cfcs_targ_enable;
fe->targ_disable = cfcs_targ_disable;
fe->lun_enable = cfcs_lun_enable;
fe->lun_disable = cfcs_lun_disable;
fe->targ_lun_arg = softc;
fe->fe_datamove = cfcs_datamove;
fe->fe_done = cfcs_done;
/* XXX KDM what should we report here? */
/* XXX These should probably be fetched from CTL. */
fe->max_targets = 1;
fe->max_target_id = 15;
retval = ctl_frontend_register(fe, /*master_SC*/ 1);
if (retval != 0) {
printf("%s: ctl_frontend_register() failed with error %d!\n",
__func__, retval);
retval = 1;
goto bailout;
}
/*
* Get the WWNN out of the database, and create a WWPN as well.
*/
#ifdef NEEDTOPORT
ddb_GetWWNN((char *)wwnn);
softc->wwnn = be64dec(wwnn);
softc->wwpn = softc->wwnn + (softc->fe.targ_port & 0xff);
#endif
/*
* If the CTL frontend didn't tell us what our WWNN/WWPN is, go
* ahead and set something random.
*/
if (fe->wwnn == 0) {
uint64_t random_bits;
arc4rand(&random_bits, sizeof(random_bits), 0);
softc->wwnn = (random_bits & 0x0000000fffffff00ULL) |
/* Company ID */ 0x5000000000000000ULL |
/* NL-Port */ 0x0300;
softc->wwpn = softc->wwnn + fe->targ_port + 1;
fe->wwnn = softc->wwnn;
fe->wwpn = softc->wwpn;
} else {
softc->wwnn = fe->wwnn;
softc->wwpn = fe->wwpn;
}
softc->devq = cam_simq_alloc(fe->num_requested_ctl_io);
if (softc->devq == NULL) {
printf("%s: error allocating devq\n", __func__);
retval = ENOMEM;
goto bailout;
}
softc->sim = cam_sim_alloc(cfcs_action, cfcs_poll, softc->port_name,
softc, /*unit*/ 0, &softc->lock, 1,
fe->num_requested_ctl_io, softc->devq);
if (softc->sim == NULL) {
printf("%s: error allocating SIM\n", __func__);
retval = ENOMEM;
goto bailout;
}
mtx_lock(&softc->lock);
if (xpt_bus_register(softc->sim, NULL, 0) != CAM_SUCCESS) {
printf("%s: error registering SIM\n", __func__);
retval = ENOMEM;
goto bailout;
}
if (xpt_create_path(&softc->path, /*periph*/NULL,
cam_sim_path(softc->sim),
CAM_TARGET_WILDCARD,
CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
printf("%s: error creating path\n", __func__);
xpt_bus_deregister(cam_sim_path(softc->sim));
retval = 1;
goto bailout;
}
mtx_unlock(&softc->lock);
xpt_setup_ccb(&csa.ccb_h, softc->path, /*priority*/ 5);
csa.ccb_h.func_code = XPT_SASYNC_CB;
csa.event_enable = AC_LOST_DEVICE;
csa.callback = cfcs_async;
csa.callback_arg = softc->sim;
xpt_action((union ccb *)&csa);
return (retval);
bailout:
if (softc->sim)
cam_sim_free(softc->sim, /*free_devq*/ TRUE);
else if (softc->devq)
cam_simq_free(softc->devq);
mtx_unlock(&softc->lock);
return (retval);
}
static void
cfcs_poll(struct cam_sim *sim)
{
}
void
cfcs_shutdown(void)
{
}
static void
cfcs_online(void *arg)
{
struct cfcs_softc *softc;
union ccb *ccb;
softc = (struct cfcs_softc *)arg;
mtx_lock(&softc->lock);
softc->online = 1;
mtx_unlock(&softc->lock);
ccb = xpt_alloc_ccb_nowait();
if (ccb == NULL) {
printf("%s: unable to allocate CCB for rescan\n", __func__);
return;
}
if (xpt_create_path(&ccb->ccb_h.path, xpt_periph,
cam_sim_path(softc->sim), CAM_TARGET_WILDCARD,
CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
printf("%s: can't allocate path for rescan\n", __func__);
xpt_free_ccb(ccb);
return;
}
xpt_rescan(ccb);
}
static void
cfcs_offline(void *arg)
{
struct cfcs_softc *softc;
union ccb *ccb;
softc = (struct cfcs_softc *)arg;
mtx_lock(&softc->lock);
softc->online = 0;
mtx_unlock(&softc->lock);
ccb = xpt_alloc_ccb_nowait();
if (ccb == NULL) {
printf("%s: unable to allocate CCB for rescan\n", __func__);
return;
}
if (xpt_create_path(&ccb->ccb_h.path, xpt_periph,
cam_sim_path(softc->sim), CAM_TARGET_WILDCARD,
CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
printf("%s: can't allocate path for rescan\n", __func__);
xpt_free_ccb(ccb);
return;
}
xpt_rescan(ccb);
}
static int
cfcs_targ_enable(void *arg, struct ctl_id targ_id)
{
return (0);
}
static int
cfcs_targ_disable(void *arg, struct ctl_id targ_id)
{
return (0);
}
static int
cfcs_lun_enable(void *arg, struct ctl_id target_id, int lun_id)
{
return (0);
}
static int
cfcs_lun_disable(void *arg, struct ctl_id target_id, int lun_id)
{
return (0);
}
/*
* This function is very similar to ctl_ioctl_do_datamove(). Is there a
* way to combine the functionality?
*
* XXX KDM may need to move this into a thread. We're doing a bcopy in the
* caller's context, which will usually be the backend. That may not be a
* good thing.
*/
static void
cfcs_datamove(union ctl_io *io)
{
union ccb *ccb;
bus_dma_segment_t cam_sg_entry, *cam_sglist;
struct ctl_sg_entry ctl_sg_entry, *ctl_sglist;
int cam_sg_count, ctl_sg_count, cam_sg_start;
int cam_sg_offset;
int len_to_copy, len_copied;
int ctl_watermark, cam_watermark;
int i, j;
cam_sg_offset = 0;
cam_sg_start = 0;
ccb = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
/*
* Note that we have a check in cfcs_action() to make sure that any
* CCBs with "bad" flags are returned with CAM_REQ_INVALID. This
* is just to make sure no one removes that check without updating
* this code to provide the additional functionality necessary to
* support those modes of operation.
*/
KASSERT(((ccb->ccb_h.flags & CFCS_BAD_CCB_FLAGS) == 0), ("invalid "
"CAM flags %#x", (ccb->ccb_h.flags & CFCS_BAD_CCB_FLAGS)));
/*
* Simplify things on both sides by putting single buffers into a
* single entry S/G list.
*/
if (ccb->ccb_h.flags & CAM_SCATTER_VALID) {
if (ccb->ccb_h.flags & CAM_SG_LIST_PHYS) {
/* We should filter this out on entry */
panic("%s: physical S/G list, should not get here",
__func__);
} else {
int len_seen;
cam_sglist = (bus_dma_segment_t *)ccb->csio.data_ptr;
cam_sg_count = ccb->csio.sglist_cnt;
for (i = 0, len_seen = 0; i < cam_sg_count; i++) {
if ((len_seen + cam_sglist[i].ds_len) >=
io->scsiio.kern_rel_offset) {
cam_sg_start = i;
cam_sg_offset =
io->scsiio.kern_rel_offset -
len_seen;
break;
}
len_seen += cam_sglist[i].ds_len;
}
}
} else {
cam_sglist = &cam_sg_entry;
cam_sglist[0].ds_len = ccb->csio.dxfer_len;
cam_sglist[0].ds_addr = (bus_addr_t)ccb->csio.data_ptr;
cam_sg_count = 1;
cam_sg_start = 0;
cam_sg_offset = io->scsiio.kern_rel_offset;
}
if (io->scsiio.kern_sg_entries > 0) {
ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr;
ctl_sg_count = io->scsiio.kern_sg_entries;
} else {
ctl_sglist = &ctl_sg_entry;
ctl_sglist->addr = io->scsiio.kern_data_ptr;
ctl_sglist->len = io->scsiio.kern_data_len;
ctl_sg_count = 1;
}
ctl_watermark = 0;
cam_watermark = cam_sg_offset;
len_copied = 0;
for (i = cam_sg_start, j = 0;
i < cam_sg_count && j < ctl_sg_count;) {
uint8_t *cam_ptr, *ctl_ptr;
len_to_copy = ctl_min(cam_sglist[i].ds_len - cam_watermark,
ctl_sglist[j].len - ctl_watermark);
cam_ptr = (uint8_t *)cam_sglist[i].ds_addr;
cam_ptr = cam_ptr + cam_watermark;
if (io->io_hdr.flags & CTL_FLAG_BUS_ADDR) {
/*
* XXX KDM fix this!
*/
panic("need to implement bus address support");
#if 0
kern_ptr = bus_to_virt(kern_sglist[j].addr);
#endif
} else
ctl_ptr = (uint8_t *)ctl_sglist[j].addr;
ctl_ptr = ctl_ptr + ctl_watermark;
ctl_watermark += len_to_copy;
cam_watermark += len_to_copy;
if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) ==
CTL_FLAG_DATA_IN) {
CTL_DEBUG_PRINT(("%s: copying %d bytes to CAM\n",
__func__, len_to_copy));
CTL_DEBUG_PRINT(("%s: from %p to %p\n", ctl_ptr,
__func__, cam_ptr));
bcopy(ctl_ptr, cam_ptr, len_to_copy);
} else {
CTL_DEBUG_PRINT(("%s: copying %d bytes from CAM\n",
__func__, len_to_copy));
CTL_DEBUG_PRINT(("%s: from %p to %p\n", cam_ptr,
__func__, ctl_ptr));
bcopy(cam_ptr, ctl_ptr, len_to_copy);
}
len_copied += len_to_copy;
if (cam_sglist[i].ds_len == cam_watermark) {
i++;
cam_watermark = 0;
}
if (ctl_sglist[j].len == ctl_watermark) {
j++;
ctl_watermark = 0;
}
}
io->scsiio.ext_data_filled += len_copied;
io->scsiio.be_move_done(io);
}
static void
cfcs_done(union ctl_io *io)
{
union ccb *ccb;
struct cfcs_softc *softc;
struct cam_sim *sim;
ccb = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
sim = xpt_path_sim(ccb->ccb_h.path);
softc = (struct cfcs_softc *)cam_sim_softc(sim);
/*
* At this point we should have status. If we don't, that's a bug.
*/
KASSERT(((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE),
("invalid CTL status %#x", io->io_hdr.status));
/*
* Translate CTL status to CAM status.
*/
switch (io->io_hdr.status & CTL_STATUS_MASK) {
case CTL_SUCCESS:
ccb->ccb_h.status = CAM_REQ_CMP;
break;
case CTL_SCSI_ERROR:
ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
ccb->csio.scsi_status = io->scsiio.scsi_status;
bcopy(&io->scsiio.sense_data, &ccb->csio.sense_data,
min(io->scsiio.sense_len, ccb->csio.sense_len));
if (ccb->csio.sense_len > io->scsiio.sense_len)
ccb->csio.sense_resid = ccb->csio.sense_len -
io->scsiio.sense_len;
else
ccb->csio.sense_resid = 0;
if ((ccb->csio.sense_len - ccb->csio.sense_resid) >
cfcs_max_sense) {
ccb->csio.sense_resid = ccb->csio.sense_len -
cfcs_max_sense;
}
break;
case CTL_CMD_ABORTED:
ccb->ccb_h.status = CAM_REQ_ABORTED;
break;
case CTL_ERROR:
default:
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
break;
}
mtx_lock(sim->mtx);
xpt_done(ccb);
mtx_unlock(sim->mtx);
ctl_free_io(io);
}
void
cfcs_action(struct cam_sim *sim, union ccb *ccb)
{
struct cfcs_softc *softc;
int err;
softc = (struct cfcs_softc *)cam_sim_softc(sim);
mtx_assert(&softc->lock, MA_OWNED);
switch (ccb->ccb_h.func_code) {
case XPT_SCSI_IO: {
union ctl_io *io;
struct ccb_scsiio *csio;
csio = &ccb->csio;
/*
* Catch CCB flags, like physical address flags, that
* indicate situations we currently can't handle.
*/
if (ccb->ccb_h.flags & CFCS_BAD_CCB_FLAGS) {
ccb->ccb_h.status = CAM_REQ_INVALID;
printf("%s: bad CCB flags %#x (all flags %#x)\n",
__func__, ccb->ccb_h.flags & CFCS_BAD_CCB_FLAGS,
ccb->ccb_h.flags);
xpt_done(ccb);
return;
}
/*
* If we aren't online, there are no devices to see.
*/
if (softc->online == 0) {
ccb->ccb_h.status = CAM_DEV_NOT_THERE;
xpt_done(ccb);
return;
}
io = ctl_alloc_io(softc->fe.ctl_pool_ref);
if (io == NULL) {
printf("%s: can't allocate ctl_io\n", __func__);
ccb->ccb_h.status = CAM_BUSY | CAM_DEV_QFRZN;
xpt_freeze_devq(ccb->ccb_h.path, 1);
xpt_done(ccb);
return;
}
ctl_zero_io(io);
/* Save pointers on both sides */
io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = ccb;
ccb->ccb_h.io_ptr = io;
/*
* Only SCSI I/O comes down this path, resets, etc. come
* down via the XPT_RESET_BUS/LUN CCBs below.
*/
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.nexus.initid.id = 1;
io->io_hdr.nexus.targ_port = softc->fe.targ_port;
/*
* XXX KDM how do we handle target IDs?
*/
io->io_hdr.nexus.targ_target.id = ccb->ccb_h.target_id;
io->io_hdr.nexus.targ_lun = ccb->ccb_h.target_lun;
/*
* This tag scheme isn't the best, since we could in theory
* have a very long-lived I/O and tag collision, especially
* in a high I/O environment. But it should work well
* enough for now. Since we're using unsigned ints,
* they'll just wrap around.
*/
io->scsiio.tag_num = softc->cur_tag_num++;
csio->tag_id = io->scsiio.tag_num;
switch (csio->tag_action) {
case CAM_TAG_ACTION_NONE:
io->scsiio.tag_type = CTL_TAG_UNTAGGED;
break;
case MSG_SIMPLE_TASK:
io->scsiio.tag_type = CTL_TAG_SIMPLE;
break;
case MSG_HEAD_OF_QUEUE_TASK:
io->scsiio.tag_type = CTL_TAG_HEAD_OF_QUEUE;
break;
case MSG_ORDERED_TASK:
io->scsiio.tag_type = CTL_TAG_ORDERED;
break;
case MSG_ACA_TASK:
io->scsiio.tag_type = CTL_TAG_ACA;
break;
default:
io->scsiio.tag_type = CTL_TAG_UNTAGGED;
printf("%s: unhandled tag type %#x!!\n", __func__,
csio->tag_action);
break;
}
if (csio->cdb_len > sizeof(io->scsiio.cdb)) {
printf("%s: WARNING: CDB len %d > ctl_io space %zd\n",
__func__, csio->cdb_len, sizeof(io->scsiio.cdb));
}
io->scsiio.cdb_len = min(csio->cdb_len, sizeof(io->scsiio.cdb));
bcopy(csio->cdb_io.cdb_bytes, io->scsiio.cdb,
io->scsiio.cdb_len);
err = ctl_queue(io);
if (err != CTL_RETVAL_COMPLETE) {
printf("%s: func %d: error %d returned by "
"ctl_queue()!\n", __func__,
ccb->ccb_h.func_code, err);
ctl_free_io(io);
} else {
ccb->ccb_h.status |= CAM_SIM_QUEUED;
}
break;
}
case XPT_ABORT: {
union ctl_io *io;
union ccb *abort_ccb;
abort_ccb = ccb->cab.abort_ccb;
if (abort_ccb->ccb_h.func_code != XPT_SCSI_IO) {
ccb->ccb_h.status = CAM_REQ_INVALID;
xpt_done(ccb);
}
/*
* If we aren't online, there are no devices to talk to.
*/
if (softc->online == 0) {
ccb->ccb_h.status = CAM_DEV_NOT_THERE;
xpt_done(ccb);
return;
}
io = ctl_alloc_io(softc->fe.ctl_pool_ref);
if (io == NULL) {
ccb->ccb_h.status = CAM_BUSY | CAM_DEV_QFRZN;
xpt_freeze_devq(ccb->ccb_h.path, 1);
xpt_done(ccb);
return;
}
ctl_zero_io(io);
/* Save pointers on both sides */
io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = ccb;
ccb->ccb_h.io_ptr = io;
io->io_hdr.io_type = CTL_IO_TASK;
io->io_hdr.nexus.initid.id = 1;
io->io_hdr.nexus.targ_port = softc->fe.targ_port;
io->io_hdr.nexus.targ_target.id = ccb->ccb_h.target_id;
io->io_hdr.nexus.targ_lun = ccb->ccb_h.target_lun;
io->taskio.task_action = CTL_TASK_ABORT_TASK;
io->taskio.tag_num = abort_ccb->csio.tag_id;
switch (abort_ccb->csio.tag_action) {
case CAM_TAG_ACTION_NONE:
io->taskio.tag_type = CTL_TAG_UNTAGGED;
break;
case MSG_SIMPLE_TASK:
io->taskio.tag_type = CTL_TAG_SIMPLE;
break;
case MSG_HEAD_OF_QUEUE_TASK:
io->taskio.tag_type = CTL_TAG_HEAD_OF_QUEUE;
break;
case MSG_ORDERED_TASK:
io->taskio.tag_type = CTL_TAG_ORDERED;
break;
case MSG_ACA_TASK:
io->taskio.tag_type = CTL_TAG_ACA;
break;
default:
io->taskio.tag_type = CTL_TAG_UNTAGGED;
printf("%s: unhandled tag type %#x!!\n", __func__,
abort_ccb->csio.tag_action);
break;
}
err = ctl_queue(io);
if (err != CTL_RETVAL_COMPLETE) {
printf("%s func %d: error %d returned by "
"ctl_queue()!\n", __func__,
ccb->ccb_h.func_code, err);
ctl_free_io(io);
}
break;
}
case XPT_GET_TRAN_SETTINGS: {
struct ccb_trans_settings *cts;
struct ccb_trans_settings_scsi *scsi;
struct ccb_trans_settings_fc *fc;
cts = &ccb->cts;
scsi = &cts->proto_specific.scsi;
fc = &cts->xport_specific.fc;
cts->protocol = PROTO_SCSI;
cts->protocol_version = SCSI_REV_SPC2;
cts->transport = XPORT_FC;
cts->transport_version = 0;
scsi->valid = CTS_SCSI_VALID_TQ;
scsi->flags = CTS_SCSI_FLAGS_TAG_ENB;
fc->valid = CTS_FC_VALID_SPEED;
fc->bitrate = 800000;
fc->wwnn = softc->wwnn;
fc->wwpn = softc->wwpn;
fc->port = softc->fe.targ_port;
fc->valid |= CTS_FC_VALID_WWNN | CTS_FC_VALID_WWPN |
CTS_FC_VALID_PORT;
ccb->ccb_h.status = CAM_REQ_CMP;
break;
}
case XPT_SET_TRAN_SETTINGS:
/* XXX KDM should we actually do something here? */
ccb->ccb_h.status = CAM_REQ_CMP;
break;
case XPT_RESET_BUS:
case XPT_RESET_DEV: {
union ctl_io *io;
/*
* If we aren't online, there are no devices to talk to.
*/
if (softc->online == 0) {
ccb->ccb_h.status = CAM_DEV_NOT_THERE;
xpt_done(ccb);
return;
}
io = ctl_alloc_io(softc->fe.ctl_pool_ref);
if (io == NULL) {
ccb->ccb_h.status = CAM_BUSY | CAM_DEV_QFRZN;
xpt_freeze_devq(ccb->ccb_h.path, 1);
xpt_done(ccb);
return;
}
ctl_zero_io(io);
/* Save pointers on both sides */
io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = ccb;
ccb->ccb_h.io_ptr = io;
io->io_hdr.io_type = CTL_IO_TASK;
io->io_hdr.nexus.initid.id = 0;
io->io_hdr.nexus.targ_port = softc->fe.targ_port;
io->io_hdr.nexus.targ_target.id = ccb->ccb_h.target_id;
io->io_hdr.nexus.targ_lun = ccb->ccb_h.target_lun;
if (ccb->ccb_h.func_code == XPT_RESET_BUS)
io->taskio.task_action = CTL_TASK_BUS_RESET;
else
io->taskio.task_action = CTL_TASK_LUN_RESET;
err = ctl_queue(io);
if (err != CTL_RETVAL_COMPLETE) {
printf("%s func %d: error %d returned by "
"ctl_queue()!\n", __func__,
ccb->ccb_h.func_code, err);
ctl_free_io(io);
}
break;
}
case XPT_CALC_GEOMETRY:
cam_calc_geometry(&ccb->ccg, 1);
xpt_done(ccb);
break;
case XPT_PATH_INQ: {
struct ccb_pathinq *cpi;
cpi = &ccb->cpi;
cpi->version_num = 0;
cpi->hba_inquiry = PI_TAG_ABLE;
cpi->target_sprt = 0;
cpi->hba_misc = 0;
cpi->hba_eng_cnt = 0;
cpi->max_target = 1;
cpi->max_lun = 1024;
/* Do we really have a limit? */
cpi->maxio = 1024 * 1024;
cpi->async_flags = 0;
cpi->hpath_id = 0;
cpi->initiator_id = 0;
strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
strncpy(cpi->hba_vid, "FreeBSD", HBA_IDLEN);
strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
cpi->unit_number = 0;
cpi->bus_id = 0;
cpi->base_transfer_speed = 800000;
cpi->protocol = PROTO_SCSI;
cpi->protocol_version = SCSI_REV_SPC2;
/*
* Pretend to be Fibre Channel.
*/
cpi->transport = XPORT_FC;
cpi->transport_version = 0;
cpi->xport_specific.fc.wwnn = softc->wwnn;
cpi->xport_specific.fc.wwpn = softc->wwpn;
cpi->xport_specific.fc.port = softc->fe.targ_port;
cpi->xport_specific.fc.bitrate = 8 * 1000 * 1000;
cpi->ccb_h.status = CAM_REQ_CMP;
break;
}
default:
ccb->ccb_h.status = CAM_PROVIDE_FAIL;
printf("%s: unsupported CCB type %#x\n", __func__,
ccb->ccb_h.func_code);
xpt_done(ccb);
break;
}
}
static void
cfcs_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
{
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,154 @@
/*-
* Copyright (c) 2004 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_frontend_internal.h#1 $
* $FreeBSD$
*/
/*
* CTL kernel internal frontend target driver. This allows kernel-level
* clients to send commands into CTL.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#ifndef _CTL_FRONTEND_INTERNAL_H_
#define _CTL_FRONTEND_INTERNAL_H_
/*
* These are general metatask error codes. If the error code is CFI_MT_ERROR,
* check any metatask-specific status codes for more detail on the problem.
*/
typedef enum {
CFI_MT_NONE,
CFI_MT_PORT_OFFLINE,
CFI_MT_ERROR,
CFI_MT_SUCCESS
} cfi_mt_status;
typedef enum {
CFI_TASK_NONE,
CFI_TASK_SHUTDOWN,
CFI_TASK_STARTUP,
CFI_TASK_BBRREAD
} cfi_tasktype;
struct cfi_task_startstop {
int total_luns;
int luns_complete;
int luns_failed;
};
/*
* Error code description:
* CFI_BBR_SUCCESS - the read was successful
* CFI_BBR_LUN_UNCONFIG - CFI probe for this lun hasn't completed
* CFI_BBR_NO_LUN - this lun doesn't exist, as far as CFI knows
* CFI_BBR_NO_MEM - memory allocation error
* CFI_BBR_BAD_LEN - data length isn't a multiple of the blocksize
* CFI_BBR_RESERV_CONFLICT - another initiator has this lun reserved, so
* we can't issue I/O at all.
* CFI_BBR_LUN_STOPPED - the lun is powered off.
* CFI_BBR_LUN_OFFLINE_CTL - the lun is offline from a CTL standpoint
* CFI_BBR_LUN_OFFLINE_RC - the lun is offline from a RAIDCore standpoint.
* This is bad, because it basically means we've
* had a double failure on the LUN.
* CFI_BBR_SCSI_ERROR - generic SCSI error, see status byte and sense
* data for more resolution if you want it.
* CFI_BBR_ERROR - the catch-all error code.
*/
typedef enum {
CFI_BBR_SUCCESS,
CFI_BBR_LUN_UNCONFIG,
CFI_BBR_NO_LUN,
CFI_BBR_NO_MEM,
CFI_BBR_BAD_LEN,
CFI_BBR_RESERV_CONFLICT,
CFI_BBR_LUN_STOPPED,
CFI_BBR_LUN_OFFLINE_CTL,
CFI_BBR_LUN_OFFLINE_RC,
CFI_BBR_SCSI_ERROR,
CFI_BBR_ERROR,
} cfi_bbrread_status;
struct cfi_task_bbrread {
int lun_num; /* lun number */
uint64_t lba; /* logical block address */
int len; /* length in bytes */
cfi_bbrread_status status; /* BBR status */
uint8_t scsi_status; /* SCSI status */
struct scsi_sense_data sense_data; /* SCSI sense data */
};
union cfi_taskinfo {
struct cfi_task_startstop startstop;
struct cfi_task_bbrread bbrread;
};
struct cfi_metatask;
typedef void (*cfi_cb_t)(void *arg, struct cfi_metatask *metatask);
struct cfi_metatask {
cfi_tasktype tasktype; /* passed to CFI */
cfi_mt_status status; /* returned from CFI */
union cfi_taskinfo taskinfo; /* returned from CFI */
struct ctl_mem_element *element; /* used by CFI, don't touch*/
cfi_cb_t callback; /* passed to CFI */
void *callback_arg; /* passed to CFI */
STAILQ_ENTRY(cfi_metatask) links; /* used by CFI, don't touch*/
};
#ifdef _KERNEL
MALLOC_DECLARE(M_CTL_CFI);
/*
* This is the API for sending meta commands (commands that are sent to more
* than one LUN) to the internal frontend:
* - Allocate a metatask using cfi_alloc_metatask(). can_wait == 0 means
* that you're calling from an interrupt context. can_wait == 1 means
* that you're calling from a thread context and don't mind waiting to
* allocate memory.
* - Setup the task type, callback and callback argument.
* - Call cfi_action().
* - When the callback comes, note the status and any per-command status
* (see the taskinfo union) and then free the metatask with
* cfi_free_metatask().
*/
struct cfi_metatask *cfi_alloc_metatask(int can_wait);
void cfi_free_metatask(struct cfi_metatask *metatask);
void cfi_action(struct cfi_metatask *metatask);
#endif /* _KERNEL */
#endif /* _CTL_FRONTEND_INTERNAL_H_ */
/*
* vim: ts=8
*/

270
sys/cam/ctl/ctl_ha.h Normal file
View File

@ -0,0 +1,270 @@
/*-
* Copyright (c) 2003-2009 Silicon Graphics International Corp.
* Copyright (c) 2011 Spectra Logic Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_ha.h#1 $
* $FreeBSD$
*/
#ifndef _CTL_HA_H_
#define _CTL_HA_H_
/*
* CTL High Availability Modes:
*
* CTL_HA_MODE_SER_ONLY: Commands are serialized to the other side. Write
* mirroring and read re-direction are assumed to
* happen in the back end.
* CTL_HA_MODE_XFER: Commands are serialized and data is transferred
* for write mirroring and read re-direction.
*/
typedef enum {
CTL_HA_MODE_SER_ONLY,
CTL_HA_MODE_XFER
} ctl_ha_mode;
/*
* This is a stubbed out High Availability interface. It assumes two nodes
* staying in sync.
*
* The reason this interface is here, and stubbed out, is that CTL was
* originally written with support for Copan's (now SGI) high availability
* framework. That framework was not released by SGI, and would not have
* been generally applicable to FreeBSD anyway.
*
* The idea here is to show the kind of API that would need to be in place
* in a HA framework to work with CTL's HA hooks. This API is very close
* to the Copan/SGI API, so that the code using it could stay in place
* as-is.
*
* So, in summary, this is a shell without real substance, and much more
* work would be needed to actually make HA work. The implementation
* inside CTL will also need to change to fit the eventual implementation.
* The additional pieces we would need are:
*
* - HA "Supervisor" framework that can startup the components of the
* system, and initiate failover (i.e. active/active to single mode)
* and failback (single to active/active mode) state transitions.
* This framework would be able to recognize when an event happens
* that requires it to initiate state transitions in the components it
* manages.
*
* - HA communication framework. This framework should have the following
* features:
* - Separate channels for separate system components. The CTL
* instance on one node should communicate with the CTL instance
* on another node.
* - Short message passing. These messages would be fixed length, so
* they could be preallocated and easily passed between the nodes.
* i.e. conceptually like an ethernet packet.
* - DMA/large buffer capability. This would require some negotiation
* with the other node to define the destination. It could
* allow for "push" (i.e. initiated by the requesting node) DMA or
* "pull" (i.e. initiated by the target controller) DMA or both.
* - Communication channel status change notification.
* - HA capability in other portions of the storage stack. Having two CTL
* instances communicate is just one part of an overall HA solution.
* State needs to be synchronized at multiple levels of the system in
* order for failover to actually work. For instance, if CTL is using a
* file on a ZFS filesystem as its backing store, the ZFS array state
* should be synchronized with the other node, so that the other node
* can immediately take over if the node that is primary for a particular
* array fails.
*/
/*
* Communication channel IDs for various system components. This is to
* make sure one CTL instance talks with another, one ZFS instance talks
* with another, etc.
*/
typedef enum {
CTL_HA_CHAN_NONE,
CTL_HA_CHAN_CTL,
CTL_HA_CHAN_ZFS,
CTL_HA_CHAN_MAX
} ctl_ha_channel;
/*
* HA communication event notification. These are events generated by the
* HA communication subsystem.
*
* CTL_HA_EVT_MSG_RECV: Message received by the other node.
* CTL_HA_EVT_MSG_SENT: Message sent to the other node.
* CTL_HA_EVT_DISCONNECT: Communication channel disconnected.
* CTL_HA_EVT_DMA_SENT: DMA successfully sent to other node (push).
* CTL_HA_EVT_DMA_RECEIVED: DMA successfully received by other node (pull).
*/
typedef enum {
CTL_HA_EVT_NONE,
CTL_HA_EVT_MSG_RECV,
CTL_HA_EVT_MSG_SENT,
CTL_HA_EVT_DISCONNECT,
CTL_HA_EVT_DMA_SENT,
CTL_HA_EVT_DMA_RECEIVED,
CTL_HA_EVT_MAX
} ctl_ha_event;
typedef enum {
CTL_HA_STATUS_WAIT,
CTL_HA_STATUS_SUCCESS,
CTL_HA_STATUS_ERROR,
CTL_HA_STATUS_INVALID,
CTL_HA_STATUS_DISCONNECT,
CTL_HA_STATUS_BUSY,
CTL_HA_STATUS_MAX
} ctl_ha_status;
typedef enum {
CTL_HA_DATA_CTL,
CTL_HA_DATA_ZFS,
CTL_HA_DATA_MAX
} ctl_ha_dtid;
typedef enum {
CTL_HA_DT_CMD_READ,
CTL_HA_DT_CMD_WRITE,
} ctl_ha_dt_cmd;
struct ctl_ha_dt_req;
typedef void (*ctl_ha_dt_cb)(struct ctl_ha_dt_req *);
struct ctl_ha_dt_req {
ctl_ha_dt_cmd command;
void *context;
ctl_ha_dt_cb callback;
ctl_ha_dtid id;
int ret;
uint32_t size;
uint8_t *local;
uint8_t *remote;
};
typedef void (*ctl_evt_handler)(ctl_ha_channel channel, ctl_ha_event event,
int param);
void ctl_ha_register_evthandler(ctl_ha_channel channel,
ctl_evt_handler handler);
static inline ctl_ha_status
ctl_ha_msg_create(ctl_ha_channel channel, ctl_evt_handler handler)
{
return (CTL_HA_STATUS_SUCCESS);
}
/*
* Receive a message of the specified size.
*/
static inline ctl_ha_status
ctl_ha_msg_recv(ctl_ha_channel channel, void *buffer, unsigned int size,
int wait)
{
return (CTL_HA_STATUS_SUCCESS);
}
/*
* Send a message of the specified size.
*/
static inline ctl_ha_status
ctl_ha_msg_send(ctl_ha_channel channel, void *buffer, unsigned int size,
int wait)
{
return (CTL_HA_STATUS_SUCCESS);
}
/*
* Allocate a data transfer request structure.
*/
static inline struct ctl_ha_dt_req *
ctl_dt_req_alloc(void)
{
return (NULL);
}
/*
* Free a data transfer request structure.
*/
static inline void
ctl_dt_req_free(struct ctl_ha_dt_req *req)
{
return;
}
/*
* Issue a DMA request for a single buffer.
*/
static inline ctl_ha_status
ctl_dt_single(struct ctl_ha_dt_req *req)
{
return (CTL_HA_STATUS_WAIT);
}
/*
* SINGLE: One node
* HA: Two nodes (Active/Active implied)
* SLAVE/MASTER: The component can set these flags to indicate which side
* is in control. It has no effect on the HA framework.
*/
typedef enum {
CTL_HA_STATE_UNKNOWN = 0x00,
CTL_HA_STATE_SINGLE = 0x01,
CTL_HA_STATE_HA = 0x02,
CTL_HA_STATE_MASK = 0x0F,
CTL_HA_STATE_SLAVE = 0x10,
CTL_HA_STATE_MASTER = 0x20
} ctl_ha_state;
typedef enum {
CTL_HA_COMP_STATUS_OK,
CTL_HA_COMP_STATUS_FAILED,
CTL_HA_COMP_STATUS_ERROR
} ctl_ha_comp_status;
struct ctl_ha_component;
typedef ctl_ha_comp_status (*ctl_hacmp_init_t)(struct ctl_ha_component *);
typedef ctl_ha_comp_status (*ctl_hacmp_start_t)(struct ctl_ha_component *,
ctl_ha_state);
struct ctl_ha_component {
char *name;
ctl_ha_state state;
ctl_ha_comp_status status;
ctl_hacmp_init_t init;
ctl_hacmp_start_t start;
ctl_hacmp_init_t quiesce;
};
#define CTL_HA_STATE_IS_SINGLE(state) ((state & CTL_HA_STATE_MASK) == \
CTL_HA_STATE_SINGLE)
#define CTL_HA_STATE_IS_HA(state) ((state & CTL_HA_STATE_MASK) == \
CTL_HA_STATE_HA)
#endif /* _CTL_HA_H_ */

474
sys/cam/ctl/ctl_io.h Normal file
View File

@ -0,0 +1,474 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_io.h#5 $
* $FreeBSD$
*/
/*
* CAM Target Layer data movement structures/interface.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#ifndef _CTL_IO_H_
#define _CTL_IO_H_
#ifdef _CTL_C
#define EXTERN(__var,__val) __var = __val
#else
#define EXTERN(__var,__val) extern __var
#endif
#define CTL_MAX_CDBLEN 32
/*
* Uncomment this next line to enable printing out times for I/Os
* that take longer than CTL_TIME_IO_SECS seconds to get to the datamove
* and/or done stage.
*/
#define CTL_TIME_IO
#ifdef CTL_TIME_IO
#define CTL_TIME_IO_DEFAULT_SECS 90
EXTERN(int ctl_time_io_secs, CTL_TIME_IO_DEFAULT_SECS);
#endif
/*
* Uncomment these next two lines to enable the CTL I/O delay feature. You
* can delay I/O at two different points -- datamove and done. This is
* useful for diagnosing abort conditions (for hosts that send an abort on a
* timeout), and for determining how long a host's timeout is.
*/
#define CTL_IO_DELAY
#define CTL_TIMER_BYTES sizeof(struct callout)
typedef enum {
CTL_STATUS_NONE, /* No status */
CTL_SUCCESS, /* Transaction completed successfully */
CTL_CMD_TIMEOUT, /* Command timed out, shouldn't happen here */
CTL_SEL_TIMEOUT, /* Selection timeout, shouldn't happen here */
CTL_ERROR, /* General CTL error XXX expand on this? */
CTL_SCSI_ERROR, /* SCSI error, look at status byte/sense data */
CTL_CMD_ABORTED, /* Command aborted, don't return status */
CTL_STATUS_MASK = 0xfff,/* Mask off any status flags */
CTL_AUTOSENSE = 0x1000 /* Autosense performed */
} ctl_io_status;
/*
* WARNING: Keep the data in/out/none flags where they are. They're used
* in conjuction with ctl_cmd_flags. See comment above ctl_cmd_flags
* definition in ctl_private.h.
*/
typedef enum {
CTL_FLAG_NONE = 0x00000000, /* no flags */
CTL_FLAG_DATA_IN = 0x00000001, /* DATA IN */
CTL_FLAG_DATA_OUT = 0x00000002, /* DATA OUT */
CTL_FLAG_DATA_NONE = 0x00000003, /* no data */
CTL_FLAG_DATA_MASK = 0x00000003,
CTL_FLAG_KDPTR_SGLIST = 0x00000008, /* kern_data_ptr is S/G list*/
CTL_FLAG_EDPTR_SGLIST = 0x00000010, /* ext_data_ptr is S/G list */
CTL_FLAG_DO_AUTOSENSE = 0x00000020, /* grab sense info */
CTL_FLAG_USER_REQ = 0x00000040, /* request came from userland */
CTL_FLAG_CONTROL_DEV = 0x00000080, /* processor device */
CTL_FLAG_ALLOCATED = 0x00000100, /* data space allocated */
CTL_FLAG_BLOCKED = 0x00000200, /* on the blocked queue */
CTL_FLAG_ABORT = 0x00000800, /* this I/O should be aborted */
CTL_FLAG_DMA_INPROG = 0x00001000, /* DMA in progress */
CTL_FLAG_NO_DATASYNC = 0x00002000, /* don't cache flush data */
CTL_FLAG_DELAY_DONE = 0x00004000, /* delay injection done */
CTL_FLAG_INT_COPY = 0x00008000, /* internal copy, no done call*/
CTL_FLAG_SENT_2OTHER_SC = 0x00010000,
CTL_FLAG_FROM_OTHER_SC = 0x00020000,
CTL_FLAG_IS_WAS_ON_RTR = 0x00040000, /* Don't rerun cmd on failover*/
CTL_FLAG_BUS_ADDR = 0x00080000, /* ctl_sglist contains BUS
addresses, not virtual ones*/
CTL_FLAG_IO_CONT = 0x00100000, /* Continue I/O instead of
completing */
CTL_FLAG_AUTO_MIRROR = 0x00200000, /* Automatically use memory
from the RC cache mirrored
address area. */
#if 0
CTL_FLAG_ALREADY_DONE = 0x00200000 /* I/O already completed */
#endif
CTL_FLAG_NO_DATAMOVE = 0x00400000,
CTL_FLAG_DMA_QUEUED = 0x00800000, /* DMA queued but not started*/
CTL_FLAG_STATUS_QUEUED = 0x01000000, /* Status queued but not sent*/
CTL_FLAG_REDIR_DONE = 0x02000000, /* Redirection has already
been done. */
CTL_FLAG_FAILOVER = 0x04000000, /* Killed by a failover */
CTL_FLAG_IO_ACTIVE = 0x08000000, /* I/O active on this SC */
CTL_FLAG_RDMA_MASK = CTL_FLAG_NO_DATASYNC | CTL_FLAG_BUS_ADDR |
CTL_FLAG_AUTO_MIRROR | CTL_FLAG_REDIR_DONE
/* Flags we care about for
remote DMA */
} ctl_io_flags;
struct ctl_lba_len {
uint64_t lba;
uint32_t len;
};
union ctl_priv {
uint8_t bytes[sizeof(uint64_t) * 2];
uint64_t integer;
void *ptr;
};
/*
* Number of CTL private areas.
*/
#define CTL_NUM_PRIV 6
/*
* Which private area are we using for a particular piece of data?
*/
#define CTL_PRIV_LUN 0 /* CTL LUN pointer goes here */
#define CTL_PRIV_LBA_LEN 1 /* Decoded LBA/len for read/write*/
#define CTL_PRIV_MODEPAGE 1 /* Modepage info for config write */
#define CTL_PRIV_BACKEND 2 /* Reserved for block, RAIDCore */
#define CTL_PRIV_BACKEND_LUN 3 /* Backend LUN pointer */
#define CTL_PRIV_FRONTEND 4 /* LSI driver, ioctl front end */
#define CTL_PRIV_USER 5 /* Userland use */
#define CTL_INVALID_PORTNAME 0xFF
#define CTL_UNMAPPED_IID 0xFF
/*
* XXX KDM this size is for the port_priv variable in struct ctl_io_hdr
* below. This should be defined in terms of the size of struct
* ctlfe_lun_cmd_info at the moment:
* struct ctlfe_lun_cmd_info {
* int cur_transfer_index;
* ctlfe_cmd_flags flags;
* bus_dma_segment_t cam_sglist[32];
* };
*
* This isn't really the way I'd prefer to do it, but it does make some
* sense, AS LONG AS we can guarantee that there will always only be one
* outstanding DMA request per ctl_io. If that assumption isn't valid,
* then we've got problems.
*
* At some point it may be nice switch CTL over to using CCBs for
* everything. At that point we can probably use the ATIO/CTIO model, so
* that multiple simultaneous DMAs per command will just work.
*
* Also note that the current size, 600, is appropriate for 64-bit
* architectures, but is overkill for 32-bit architectures. Need a way to
* figure out the size at compile time, or just get rid of this altogether.
*/
#define CTL_PORT_PRIV_SIZE 600
struct ctl_sg_entry {
void *addr;
size_t len;
};
struct ctl_id {
uint32_t id;
uint64_t wwid[2];
};
typedef enum {
CTL_IO_NONE,
CTL_IO_SCSI,
CTL_IO_TASK,
} ctl_io_type;
struct ctl_nexus {
struct ctl_id initid; /* Initiator ID */
uint32_t targ_port; /* Target port, filled in by PORT */
struct ctl_id targ_target; /* Destination target */
uint32_t targ_lun; /* Destination lun */
};
typedef enum {
CTL_MSG_SERIALIZE,
CTL_MSG_R2R,
CTL_MSG_FINISH_IO,
CTL_MSG_BAD_JUJU,
CTL_MSG_MANAGE_TASKS,
CTL_MSG_PERS_ACTION,
CTL_MSG_SYNC_FE,
CTL_MSG_APS_LOCK,
CTL_MSG_DATAMOVE,
CTL_MSG_DATAMOVE_DONE
} ctl_msg_type;
struct ctl_scsiio;
#define CTL_NUM_SG_ENTRIES 9
struct ctl_io_hdr {
uint32_t version; /* interface version XXX */
ctl_io_type io_type; /* task I/O, SCSI I/O, etc. */
ctl_msg_type msg_type;
struct ctl_nexus nexus; /* Initiator, port, target, lun */
uint32_t iid_indx; /* the index into the iid mapping */
uint32_t flags; /* transaction flags */
uint32_t status; /* transaction status */
uint32_t port_status; /* trans status, set by PORT, 0 = good*/
uint32_t timeout; /* timeout in ms */
uint32_t retries; /* retry count */
#ifdef CTL_IO_DELAY
uint8_t timer_bytes[CTL_TIMER_BYTES]; /* timer kludge */
#endif /* CTL_IO_DELAY */
#ifdef CTL_TIME_IO
time_t start_time; /* I/O start time */
struct bintime start_bt; /* Timer start ticks */
struct bintime dma_start_bt; /* DMA start ticks */
struct bintime dma_bt; /* DMA total ticks */
uint32_t num_dmas; /* Number of DMAs */
#endif /* CTL_TIME_IO */
union ctl_io *original_sc;
union ctl_io *serializing_sc;
void *pool; /* I/O pool */
union ctl_priv ctl_private[CTL_NUM_PRIV];/* CTL private area */
uint8_t port_priv[CTL_PORT_PRIV_SIZE];/* PORT private area*/
struct ctl_sg_entry remote_sglist[CTL_NUM_SG_ENTRIES];
struct ctl_sg_entry remote_dma_sglist[CTL_NUM_SG_ENTRIES];
struct ctl_sg_entry local_sglist[CTL_NUM_SG_ENTRIES];
struct ctl_sg_entry local_dma_sglist[CTL_NUM_SG_ENTRIES];
STAILQ_ENTRY(ctl_io_hdr) links; /* linked list pointer */
TAILQ_ENTRY(ctl_io_hdr) ooa_links;
TAILQ_ENTRY(ctl_io_hdr) blocked_links;
};
typedef enum {
CTL_TAG_UNTAGGED,
CTL_TAG_SIMPLE,
CTL_TAG_ORDERED,
CTL_TAG_HEAD_OF_QUEUE,
CTL_TAG_ACA
} ctl_tag_type;
union ctl_io;
/*
* SCSI passthrough I/O structure for the CAM Target Layer. Note
* that some of these fields are here for completeness, but they aren't
* used in the CTL implementation. e.g., timeout and retries won't be
* used.
*
* Note: Make sure the io_hdr is *always* the first element in this
* structure.
*/
struct ctl_scsiio {
struct ctl_io_hdr io_hdr; /* common to all I/O types */
uint32_t ext_sg_entries; /* 0 = no S/G list, > 0 = num entries */
uint8_t *ext_data_ptr; /* data buffer or S/G list */
uint32_t ext_data_len; /* Data transfer length */
uint32_t ext_data_filled; /* Amount of data filled so far */
uint32_t kern_sg_entries; /* 0 = no S/G list, > 0 = num entries */
uint32_t rem_sg_entries; /* 0 = no S/G list, > 0 = num entries */
uint8_t *kern_data_ptr; /* data buffer or S/G list */
uint32_t kern_data_len; /* Length of this S/G list/buffer */
uint32_t kern_total_len; /* Total length of this transaction */
uint32_t kern_data_resid; /* Length left to transfer after this*/
uint32_t kern_rel_offset; /* Byte Offset of this transfer */
struct scsi_sense_data sense_data; /* sense data */
uint8_t sense_len; /* Returned sense length */
uint8_t scsi_status; /* SCSI status byte */
uint8_t sense_residual; /* sense residual length */
uint32_t residual; /* data residual length */
uint32_t tag_num; /* tag number */
ctl_tag_type tag_type; /* simple, ordered, head of queue,etc.*/
uint8_t cdb_len; /* CDB length */
uint8_t cdb[CTL_MAX_CDBLEN]; /* CDB */
int (*be_move_done)(union ctl_io *io); /* called by fe */
int (*io_cont)(union ctl_io *io); /* to continue processing */
};
typedef enum {
CTL_TASK_ABORT_TASK,
CTL_TASK_ABORT_TASK_SET,
CTL_TASK_CLEAR_ACA,
CTL_TASK_CLEAR_TASK_SET,
CTL_TASK_LUN_RESET,
CTL_TASK_TARGET_RESET,
CTL_TASK_BUS_RESET,
CTL_TASK_PORT_LOGIN,
CTL_TASK_PORT_LOGOUT
} ctl_task_type;
/*
* Task management I/O structure. Aborts, bus resets, etc., are sent using
* this structure.
*
* Note: Make sure the io_hdr is *always* the first element in this
* structure.
*/
struct ctl_taskio {
struct ctl_io_hdr io_hdr; /* common to all I/O types */
ctl_task_type task_action; /* Target Reset, Abort, etc. */
uint32_t tag_num; /* tag number */
ctl_tag_type tag_type; /* simple, ordered, etc. */
};
typedef enum {
CTL_PR_REG_KEY,
CTL_PR_UNREG_KEY,
CTL_PR_PREEMPT,
CTL_PR_CLEAR,
CTL_PR_RESERVE,
CTL_PR_RELEASE
} ctl_pr_action;
/*
* The PR info is specifically for sending Persistent Reserve actions
* to the other SC which it must also act on.
*
* Note: Make sure the io_hdr is *always* the first element in this
* structure.
*/
struct ctl_pr_info {
ctl_pr_action action;
uint8_t sa_res_key[8];
uint8_t res_type;
uint16_t residx;
};
struct ctl_ha_msg_hdr {
ctl_msg_type msg_type;
union ctl_io *original_sc;
union ctl_io *serializing_sc;
struct ctl_nexus nexus; /* Initiator, port, target, lun */
uint32_t status; /* transaction status */
TAILQ_ENTRY(ctl_ha_msg_hdr) links;
};
#define CTL_HA_MAX_SG_ENTRIES 16
/*
* Used for CTL_MSG_APS_LOCK.
*/
struct ctl_ha_msg_aps {
struct ctl_ha_msg_hdr hdr;
uint8_t lock_flag;
};
/*
* Used for CTL_MSG_PERS_ACTION.
*/
struct ctl_ha_msg_pr {
struct ctl_ha_msg_hdr hdr;
struct ctl_pr_info pr_info;
};
/*
* The S/G handling here is a little different than the standard ctl_scsiio
* structure, because we can't pass data by reference in between controllers.
* The S/G list in the ctl_scsiio struct is normally passed in the
* kern_data_ptr field. So kern_sg_entries here will always be non-zero,
* even if there is only one entry.
*
* Used for CTL_MSG_DATAMOVE.
*/
struct ctl_ha_msg_dt {
struct ctl_ha_msg_hdr hdr;
ctl_io_flags flags; /* Only I/O flags are used here */
uint32_t sg_sequence; /* S/G portion number */
uint8_t sg_last; /* last S/G batch = 1 */
uint32_t sent_sg_entries; /* previous S/G count */
uint32_t cur_sg_entries; /* current S/G entries */
uint32_t kern_sg_entries; /* total S/G entries */
uint32_t kern_data_len; /* Length of this S/G list */
uint32_t kern_total_len; /* Total length of this
transaction */
uint32_t kern_data_resid; /* Length left to transfer
after this*/
uint32_t kern_rel_offset; /* Byte Offset of this
transfer */
struct ctl_sg_entry sg_list[CTL_HA_MAX_SG_ENTRIES];
};
/*
* Used for CTL_MSG_SERIALIZE, CTL_MSG_FINISH_IO, CTL_MSG_BAD_JUJU.
*/
struct ctl_ha_msg_scsi {
struct ctl_ha_msg_hdr hdr;
uint8_t cdb[CTL_MAX_CDBLEN]; /* CDB */
uint32_t tag_num; /* tag number */
ctl_tag_type tag_type; /* simple, ordered, etc. */
uint8_t scsi_status; /* SCSI status byte */
struct scsi_sense_data sense_data; /* sense data */
uint8_t sense_len; /* Returned sense length */
uint8_t sense_residual; /* sense residual length */
uint32_t residual; /* data residual length */
uint32_t fetd_status; /* trans status, set by FETD,
0 = good*/
struct ctl_lba_len lbalen; /* used for stats */
};
/*
* Used for CTL_MSG_MANAGE_TASKS.
*/
struct ctl_ha_msg_task {
struct ctl_ha_msg_hdr hdr;
ctl_task_type task_action; /* Target Reset, Abort, etc. */
uint32_t tag_num; /* tag number */
ctl_tag_type tag_type; /* simple, ordered, etc. */
};
union ctl_ha_msg {
struct ctl_ha_msg_hdr hdr;
struct ctl_ha_msg_task task;
struct ctl_ha_msg_scsi scsi;
struct ctl_ha_msg_dt dt;
struct ctl_ha_msg_pr pr;
struct ctl_ha_msg_aps aps;
};
struct ctl_prio {
struct ctl_io_hdr io_hdr;
struct ctl_ha_msg_pr pr_msg;
};
union ctl_io {
struct ctl_io_hdr io_hdr; /* common to all I/O types */
struct ctl_scsiio scsiio; /* Normal SCSI commands */
struct ctl_taskio taskio; /* SCSI task management/reset */
struct ctl_prio presio; /* update per. res info on other SC */
};
#ifdef _KERNEL
union ctl_io *ctl_alloc_io(void *pool_ref);
void ctl_free_io(union ctl_io *io);
void ctl_zero_io(union ctl_io *io);
void ctl_copy_io(union ctl_io *src, union ctl_io *dest);
#endif /* _KERNEL */
#endif /* _CTL_IO_H_ */
/*
* vim: ts=8
*/

604
sys/cam/ctl/ctl_ioctl.h Normal file
View File

@ -0,0 +1,604 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* Copyright (c) 2011 Spectra Logic Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_ioctl.h#4 $
* $FreeBSD$
*/
/*
* CAM Target Layer ioctl interface.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#ifndef _CTL_IOCTL_H_
#define _CTL_IOCTL_H_
#define CTL_DEFAULT_DEV "/dev/cam/ctl"
/*
* Maximum number of targets we support.
*/
#define CTL_MAX_TARGETS 1
/*
* Maximum target ID we support.
*/
#define CTL_MAX_TARGID 15
/*
* Maximum number of LUNs we support at the moment. MUST be a power of 2.
*/
#define CTL_MAX_LUNS 256
/*
* Maximum number of initiators per port.
*/
#define CTL_MAX_INIT_PER_PORT 2048 // Was 16
/*
* Maximum number of ports registered at one time.
*/
#define CTL_MAX_PORTS 32
/*
* Maximum number of initiators we support.
*/
#define CTL_MAX_INITIATORS (CTL_MAX_INIT_PER_PORT * CTL_MAX_PORTS)
/* Hopefully this won't conflict with new misc devices that pop up */
#define CTL_MINOR 225
typedef enum {
CTL_OOA_INVALID_LUN,
CTL_OOA_SUCCESS
} ctl_ooa_status;
struct ctl_ooa_info {
uint32_t target_id; /* Passed in to CTL */
uint32_t lun_id; /* Passed in to CTL */
uint32_t num_entries; /* Returned from CTL */
ctl_ooa_status status; /* Returned from CTL */
};
struct ctl_hard_startstop_info {
cfi_mt_status status;
int total_luns;
int luns_complete;
int luns_failed;
};
struct ctl_bbrread_info {
int lun_num; /* Passed in to CTL */
uint64_t lba; /* Passed in to CTL */
int len; /* Passed in to CTL */
cfi_mt_status status; /* Returned from CTL */
cfi_bbrread_status bbr_status; /* Returned from CTL */
uint8_t scsi_status; /* Returned from CTL */
struct scsi_sense_data sense_data; /* Returned from CTL */
};
typedef enum {
CTL_DELAY_TYPE_NONE,
CTL_DELAY_TYPE_CONT,
CTL_DELAY_TYPE_ONESHOT
} ctl_delay_type;
typedef enum {
CTL_DELAY_LOC_NONE,
CTL_DELAY_LOC_DATAMOVE,
CTL_DELAY_LOC_DONE,
} ctl_delay_location;
typedef enum {
CTL_DELAY_STATUS_NONE,
CTL_DELAY_STATUS_OK,
CTL_DELAY_STATUS_INVALID_LUN,
CTL_DELAY_STATUS_INVALID_TYPE,
CTL_DELAY_STATUS_INVALID_LOC,
CTL_DELAY_STATUS_NOT_IMPLEMENTED
} ctl_delay_status;
struct ctl_io_delay_info {
uint32_t target_id;
uint32_t lun_id;
ctl_delay_type delay_type;
ctl_delay_location delay_loc;
uint32_t delay_secs;
ctl_delay_status status;
};
typedef enum {
CTL_GS_SYNC_NONE,
CTL_GS_SYNC_OK,
CTL_GS_SYNC_NO_LUN
} ctl_gs_sync_status;
/*
* The target and LUN id specify which device to modify. The sync interval
* means that we will let through every N SYNCHRONIZE CACHE commands.
*/
struct ctl_sync_info {
uint32_t target_id; /* passed to kernel */
uint32_t lun_id; /* passed to kernel */
int sync_interval; /* depends on whether get/set */
ctl_gs_sync_status status; /* passed from kernel */
};
typedef enum {
CTL_STATS_NO_IO,
CTL_STATS_READ,
CTL_STATS_WRITE
} ctl_stat_types;
#define CTL_STATS_NUM_TYPES 3
typedef enum {
CTL_LUN_STATS_NO_BLOCKSIZE = 0x01
} ctl_lun_stats_flags;
struct ctl_lun_io_port_stats {
uint32_t targ_port;
uint64_t bytes[CTL_STATS_NUM_TYPES];
uint64_t operations[CTL_STATS_NUM_TYPES];
struct bintime time[CTL_STATS_NUM_TYPES];
uint64_t num_dmas[CTL_STATS_NUM_TYPES];
struct bintime dma_time[CTL_STATS_NUM_TYPES];
};
struct ctl_lun_io_stats {
uint8_t device_type;
uint64_t lun_number;
uint32_t blocksize;
ctl_lun_stats_flags flags;
struct ctl_lun_io_port_stats ports[CTL_MAX_PORTS];
};
typedef enum {
CTL_SS_OK,
CTL_SS_NEED_MORE_SPACE,
CTL_SS_ERROR
} ctl_stats_status;
typedef enum {
CTL_STATS_FLAG_NONE = 0x00,
CTL_STATS_FLAG_TIME_VALID = 0x01
} ctl_stats_flags;
struct ctl_stats {
int alloc_len; /* passed to kernel */
struct ctl_lun_io_stats *lun_stats; /* passed to/from kernel */
int fill_len; /* passed to userland */
int num_luns; /* passed to userland */
ctl_stats_status status; /* passed to userland */
ctl_stats_flags flags; /* passed to userland */
struct timespec timestamp; /* passed to userland */
};
/*
* The types of errors that can be injected:
*
* NONE: No error specified.
* ABORTED: SSD_KEY_ABORTED_COMMAND, 0x45, 0x00
* MEDIUM_ERR: Medium error, different asc/ascq depending on read/write.
* UA: Unit attention.
* CUSTOM: User specifies the sense data.
* TYPE: Mask to use with error types.
*
* Flags that affect injection behavior:
* CONTINUOUS: This error will stay around until explicitly cleared.
* DESCRIPTOR: Use descriptor sense instead of fixed sense.
*/
typedef enum {
CTL_LUN_INJ_NONE = 0x000,
CTL_LUN_INJ_ABORTED = 0x001,
CTL_LUN_INJ_MEDIUM_ERR = 0x002,
CTL_LUN_INJ_UA = 0x003,
CTL_LUN_INJ_CUSTOM = 0x004,
CTL_LUN_INJ_TYPE = 0x0ff,
CTL_LUN_INJ_CONTINUOUS = 0x100,
CTL_LUN_INJ_DESCRIPTOR = 0x200
} ctl_lun_error;
/*
* Flags to specify what type of command the given error pattern will
* execute on. The first group of types can be ORed together.
*
* READ: Any read command.
* WRITE: Any write command.
* READWRITE: Any read or write command.
* READCAP: Any read capacity command.
* TUR: Test Unit Ready.
* ANY: Any command.
* MASK: Mask for basic command patterns.
*
* Special types:
*
* CMD: The CDB to act on is specified in struct ctl_error_desc_cmd.
* RANGE: For read/write commands, act when the LBA is in the
* specified range.
*/
typedef enum {
CTL_LUN_PAT_NONE = 0x000,
CTL_LUN_PAT_READ = 0x001,
CTL_LUN_PAT_WRITE = 0x002,
CTL_LUN_PAT_READWRITE = CTL_LUN_PAT_READ | CTL_LUN_PAT_WRITE,
CTL_LUN_PAT_READCAP = 0x004,
CTL_LUN_PAT_TUR = 0x008,
CTL_LUN_PAT_ANY = 0x0ff,
CTL_LUN_PAT_MASK = 0x0ff,
CTL_LUN_PAT_CMD = 0x100,
CTL_LUN_PAT_RANGE = 0x200
} ctl_lun_error_pattern;
/*
* This structure allows the user to specify a particular CDB pattern to
* look for.
*
* cdb_pattern: Fill in the relevant bytes to look for in the CDB.
* cdb_valid_bytes: Bitmask specifying valid bytes in the cdb_pattern.
* flags: Specify any command flags (see ctl_io_flags) that
* should be set.
*/
struct ctl_error_desc_cmd {
uint8_t cdb_pattern[CTL_MAX_CDBLEN];
uint32_t cdb_valid_bytes;
uint32_t flags;
};
/*
* Error injection descriptor.
*
* target_id: Target ID to act on.
* lun_id LUN to act on.
* lun_error: The type of error to inject. See above for descriptions.
* error_pattern: What kind of command to act on. See above.
* cmd_desc: For CTL_LUN_PAT_CMD only.
* lba_range: For CTL_LUN_PAT_RANGE only.
* custom_sense: Specify sense. For CTL_LUN_INJ_CUSTOM only.
* serial: Serial number returned by the kernel. Use for deletion.
* links: Kernel use only.
*/
struct ctl_error_desc {
uint32_t target_id; /* To kernel */
uint32_t lun_id; /* To kernel */
ctl_lun_error lun_error; /* To kernel */
ctl_lun_error_pattern error_pattern; /* To kernel */
struct ctl_error_desc_cmd cmd_desc; /* To kernel */
struct ctl_lba_len lba_range; /* To kernel */
struct scsi_sense_data custom_sense; /* To kernel */
uint64_t serial; /* From kernel */
STAILQ_ENTRY(ctl_error_desc) links; /* Kernel use only */
};
typedef enum {
CTL_OOA_FLAG_NONE = 0x00,
CTL_OOA_FLAG_ALL_LUNS = 0x01
} ctl_ooa_flags;
typedef enum {
CTL_OOA_OK,
CTL_OOA_NEED_MORE_SPACE,
CTL_OOA_ERROR
} ctl_get_ooa_status;
typedef enum {
CTL_OOACMD_FLAG_NONE = 0x00,
CTL_OOACMD_FLAG_DMA = 0x01,
CTL_OOACMD_FLAG_BLOCKED = 0x02,
CTL_OOACMD_FLAG_ABORT = 0x04,
CTL_OOACMD_FLAG_RTR = 0x08,
CTL_OOACMD_FLAG_DMA_QUEUED = 0x10
} ctl_ooa_cmd_flags;
struct ctl_ooa_entry {
ctl_ooa_cmd_flags cmd_flags;
uint8_t cdb[CTL_MAX_CDBLEN];
uint8_t cdb_len;
uint32_t tag_num;
uint32_t lun_num;
struct bintime start_bt;
};
struct ctl_ooa {
ctl_ooa_flags flags; /* passed to kernel */
uint64_t lun_num; /* passed to kernel */
uint32_t alloc_len; /* passed to kernel */
uint32_t alloc_num; /* passed to kernel */
struct ctl_ooa_entry *entries; /* filled in kernel */
uint32_t fill_len; /* passed to userland */
uint32_t fill_num; /* passed to userland */
uint32_t dropped_num; /* passed to userland */
struct bintime cur_bt; /* passed to userland */
ctl_get_ooa_status status; /* passed to userland */
};
typedef enum {
CTL_PORT_LIST_NONE,
CTL_PORT_LIST_OK,
CTL_PORT_LIST_NEED_MORE_SPACE,
CTL_PORT_LIST_ERROR
} ctl_port_list_status;
struct ctl_port_list {
uint32_t alloc_len; /* passed to kernel */
uint32_t alloc_num; /* passed to kernel */
struct ctl_port_entry *entries; /* filled in kernel */
uint32_t fill_len; /* passed to userland */
uint32_t fill_num; /* passed to userland */
uint32_t dropped_num; /* passed to userland */
ctl_port_list_status status; /* passed to userland */
};
typedef enum {
CTL_LUN_NOSTATUS,
CTL_LUN_OK,
CTL_LUN_ERROR
} ctl_lun_status;
#define CTL_ERROR_STR_LEN 160
#define CTL_BEARG_RD 0x01
#define CTL_BEARG_WR 0x02
#define CTL_BEARG_RW (CTL_BEARG_RD|CTL_BEARG_WR)
#define CTL_BEARG_ASCII 0x04
/*
* Backend Argument:
*
* namelen: Length of the name field, including the terminating NUL.
*
* name: Name of the paramter. This must be NUL-terminated.
*
* flags: Flags for the parameter, see above for values.
*
* vallen: Length of the value in bytes.
*
* value: Value to be set/fetched.
*
* kname: For kernel use only.
*
* kvalue: For kernel use only.
*/
struct ctl_be_arg {
int namelen;
char *name;
int flags;
int vallen;
void *value;
char *kname;
void *kvalue;
};
typedef enum {
CTL_LUNREQ_CREATE,
CTL_LUNREQ_RM
} ctl_lunreq_type;
/*
* LUN creation parameters:
*
* flags: Various LUN flags, see ctl_backend.h for a
* description of the flag values and meanings.
*
* device_type: The SCSI device type. e.g. 0 for Direct Access,
* 3 for Processor, etc. Only certain backends may
* support setting this field. The CTL_LUN_FLAG_DEV_TYPE
* flag should be set in the flags field if the device
* type is set.
*
* lun_size_bytes: The size of the LUN in bytes. For some backends
* this is relevant (e.g. ramdisk), for others, it may
* be ignored in favor of using the properties of the
* backing store. If specified, this should be a
* multiple of the blocksize.
*
* The actual size of the LUN is returned in this
* field.
*
* blocksize_bytes: The LUN blocksize in bytes. For some backends this
* is relevant, for others it may be ignored in
* favor of using the properties of the backing store.
*
* The actual blocksize of the LUN is returned in this
* field.
*
* req_lun_id: The requested LUN ID. The CTL_LUN_FLAG_ID_REQ flag
* should be set if this is set. The request will be
* granted if the LUN number is available, otherwise
* the LUN addition request will fail.
*
* The allocated LUN number is returned in this field.
*
* serial_num: This is the value returned in SCSI INQUIRY VPD page
* 0x80. If it is specified, the CTL_LUN_FLAG_SERIAL_NUM
* flag should be set.
*
* The serial number value used is returned in this
* field.
*
* device_id: This is the value returned in the T10 vendor ID
* based DESIGNATOR field in the SCSI INQUIRY VPD page
* 0x83 data. If it is specified, the CTL_LUN_FLAG_DEVID
* flag should be set.
*
* The device id value used is returned in this field.
*/
struct ctl_lun_create_params {
ctl_backend_lun_flags flags;
uint8_t device_type;
uint64_t lun_size_bytes;
uint32_t blocksize_bytes;
uint32_t req_lun_id;
uint8_t serial_num[CTL_SN_LEN];
uint8_t device_id[CTL_DEVID_LEN];
};
/*
* LUN removal parameters:
*
* lun_id: The number of the LUN to delete. This must be set.
* The LUN must be backed by the given backend.
*/
struct ctl_lun_rm_params {
uint32_t lun_id;
};
/*
* Union of request type data. Fill in the appropriate union member for
* the request type.
*/
union ctl_lunreq_data {
struct ctl_lun_create_params create;
struct ctl_lun_rm_params rm;
};
/*
* LUN request interface:
*
* backend: This is required, and is NUL-terminated a string
* that is the name of the backend, like "ramdisk" or
* "block".
*
* reqtype: The type of request, CTL_LUNREQ_CREATE to create a
* LUN, CTL_LUNREQ_RM to delete a LUN.
*
* reqdata: Request type-specific information. See the
* description of individual the union members above
* for more information.
*
* num_be_args: This is the number of backend-specific arguments
* in the be_args array.
*
* be_args: This is an array of backend-specific arguments.
* See above for a description of the fields in this
* structure.
*
* status: Status of the LUN request.
*
* error_str: If the status is CTL_LUN_ERROR, this will
* contain a string describing the error.
*
* kern_be_args: For kernel use only.
*/
struct ctl_lun_req {
char backend[CTL_BE_NAME_LEN];
ctl_lunreq_type reqtype;
union ctl_lunreq_data reqdata;
int num_be_args;
struct ctl_be_arg *be_args;
ctl_lun_status status;
char error_str[CTL_ERROR_STR_LEN];
struct ctl_be_arg *kern_be_args;
};
/*
* LUN list status:
*
* NONE: No status.
*
* OK: Request completed successfully.
*
* NEED_MORE_SPACE: The allocated length of the entries field is too
* small for the available data.
*
* ERROR: An error occured, look at the error string for a
* description of the error.
*/
typedef enum {
CTL_LUN_LIST_NONE,
CTL_LUN_LIST_OK,
CTL_LUN_LIST_NEED_MORE_SPACE,
CTL_LUN_LIST_ERROR
} ctl_lun_list_status;
/*
* LUN list interface
*
* backend_name: This is a NUL-terminated string. If the string
* length is 0, then all LUNs on all backends will
* be enumerated. Otherwise this is the name of the
* backend to be enumerated, like "ramdisk" or "block".
*
* alloc_len: The length of the data buffer allocated for entries.
* In order to properly size the buffer, make one call
* with alloc_len set to 0, and then use the returned
* dropped_len as the buffer length to allocate and
* pass in on a subsequent call.
*
* lun_xml: XML-formatted information on the requested LUNs.
*
* fill_len: The amount of data filled in the storage for entries.
*
* status: The status of the request. See above for the
* description of the values of this field.
*
* error_str: If the status indicates an error, this string will
* be filled in to describe the error.
*/
struct ctl_lun_list {
char backend[CTL_BE_NAME_LEN]; /* passed to kernel*/
uint32_t alloc_len; /* passed to kernel */
char *lun_xml; /* filled in kernel */
uint32_t fill_len; /* passed to userland */
ctl_lun_list_status status; /* passed to userland */
char error_str[CTL_ERROR_STR_LEN];
/* passed to userland */
};
#define CTL_IO _IOWR(CTL_MINOR, 0x00, union ctl_io)
#define CTL_ENABLE_PORT _IOW(CTL_MINOR, 0x04, struct ctl_port_entry)
#define CTL_DISABLE_PORT _IOW(CTL_MINOR, 0x05, struct ctl_port_entry)
#define CTL_DUMP_OOA _IO(CTL_MINOR, 0x06)
#define CTL_CHECK_OOA _IOWR(CTL_MINOR, 0x07, struct ctl_ooa_info)
#define CTL_HARD_STOP _IOR(CTL_MINOR, 0x08, \
struct ctl_hard_startstop_info)
#define CTL_HARD_START _IOR(CTL_MINOR, 0x09, \
struct ctl_hard_startstop_info)
#define CTL_DELAY_IO _IOWR(CTL_MINOR, 0x10, struct ctl_io_delay_info)
#define CTL_REALSYNC_GET _IOR(CTL_MINOR, 0x11, int)
#define CTL_REALSYNC_SET _IOW(CTL_MINOR, 0x12, int)
#define CTL_SETSYNC _IOWR(CTL_MINOR, 0x13, struct ctl_sync_info)
#define CTL_GETSYNC _IOWR(CTL_MINOR, 0x14, struct ctl_sync_info)
#define CTL_GETSTATS _IOWR(CTL_MINOR, 0x15, struct ctl_stats)
#define CTL_ERROR_INJECT _IOWR(CTL_MINOR, 0x16, struct ctl_error_desc)
#define CTL_BBRREAD _IOWR(CTL_MINOR, 0x17, struct ctl_bbrread_info)
#define CTL_GET_OOA _IOWR(CTL_MINOR, 0x18, struct ctl_ooa)
#define CTL_DUMP_STRUCTS _IO(CTL_MINOR, 0x19)
#define CTL_GET_PORT_LIST _IOWR(CTL_MINOR, 0x20, struct ctl_port_list)
#define CTL_LUN_REQ _IOWR(CTL_MINOR, 0x21, struct ctl_lun_req)
#define CTL_LUN_LIST _IOWR(CTL_MINOR, 0x22, struct ctl_lun_list)
#define CTL_ERROR_INJECT_DELETE _IOW(CTL_MINOR, 0x23, struct ctl_error_desc)
#define CTL_SET_PORT_WWNS _IOW(CTL_MINOR, 0x24, struct ctl_port_entry)
#endif /* _CTL_IOCTL_H_ */
/*
* vim: ts=8
*/

192
sys/cam/ctl/ctl_mem_pool.c Normal file
View File

@ -0,0 +1,192 @@
/*-
* Copyright (c) 2003, 2004 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_mem_pool.c#1 $
*/
/*
* CAM Target Layer memory pool code.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/queue.h>
#include <cam/ctl/ctl_mem_pool.h>
MALLOC_DEFINE(M_CTL_POOL, "ctlpool", "CTL memory pool");
int
ctl_init_mem_pool(struct ctl_mem_pool *pool, int chunk_size,
ctl_mem_pool_flags flags, int grow_inc,
int initial_pool_size)
{
pool->flags = flags;
pool->chunk_size = chunk_size;
pool->grow_inc = grow_inc;
mtx_init(&pool->lock, "Pool mutex", NULL, MTX_DEF);
STAILQ_INIT(&pool->free_mem_list);
cv_init(&pool->wait_mem, "CTL mem pool");
if (ctl_grow_mem_pool(pool, initial_pool_size, /*can_wait*/ 1) !=
initial_pool_size)
return (1);
else
return (0);
}
struct ctl_mem_element *
ctl_alloc_mem_element(struct ctl_mem_pool *pool, int can_wait)
{
struct ctl_mem_element *mem;
for (;;) {
mtx_lock(&pool->lock);
mem = STAILQ_FIRST(&pool->free_mem_list);
if (mem != NULL) {
STAILQ_REMOVE(&pool->free_mem_list, mem,
ctl_mem_element, links);
mem->flags = CTL_MEM_ELEMENT_PREALLOC;
}
mtx_unlock(&pool->lock);
if (mem != NULL)
return (mem);
/*
* Grow the pool permanantly by the requested increment
* instead of temporarily. This has the effect that
* whatever the high water mark of transactions is for
* this pool, we'll keep that much memory around.
*/
if (pool->flags & CTL_MEM_POOL_PERM_GROW) {
if (ctl_grow_mem_pool(pool, pool->grow_inc,
can_wait) != 0)
continue;
}
mem = (struct ctl_mem_element *)malloc(sizeof(*mem),
M_CTL_POOL, can_wait ? M_WAITOK : M_NOWAIT);
if (mem != NULL) {
mem->flags = CTL_MEM_ELEMENT_NONE;
mem->pool = pool;
mem->bytes = malloc(pool->chunk_size, M_CTL_POOL,
can_wait ? M_WAITOK : M_NOWAIT);
if (mem->bytes == NULL) {
free(mem, M_CTL_POOL);
mem = NULL;
} else {
return (mem);
}
}
if (can_wait == 0)
return (NULL);
cv_wait_unlock(&pool->wait_mem, &pool->lock);
}
}
void
ctl_free_mem_element(struct ctl_mem_element *mem)
{
struct ctl_mem_pool *pool;
pool = mem->pool;
if (mem->flags & CTL_MEM_ELEMENT_PREALLOC) {
mtx_lock(&pool->lock);
STAILQ_INSERT_TAIL(&pool->free_mem_list, mem, links);
mtx_unlock(&pool->lock);
cv_broadcast(&pool->wait_mem);
} else
free(mem, M_CTL_POOL);
}
int
ctl_grow_mem_pool(struct ctl_mem_pool *pool, int count, int can_wait)
{
int i;
for (i = 0; i < count; i++) {
struct ctl_mem_element *mem;
mem = (struct ctl_mem_element *)malloc(sizeof(*mem),
M_CTL_POOL, can_wait ? M_WAITOK : M_NOWAIT);
if (mem == NULL)
break;
mem->bytes = malloc(pool->chunk_size, M_CTL_POOL, can_wait ?
M_WAITOK : M_NOWAIT);
if (mem->bytes == NULL) {
free(mem, M_CTL_POOL);
break;
}
mem->flags = CTL_MEM_ELEMENT_PREALLOC;
mem->pool = pool;
mtx_lock(&pool->lock);
STAILQ_INSERT_TAIL(&pool->free_mem_list, mem, links);
mtx_unlock(&pool->lock);
}
return (i);
}
int
ctl_shrink_mem_pool(struct ctl_mem_pool *pool)
{
struct ctl_mem_element *mem, *mem_next;
mtx_lock(&pool->lock);
for (mem = STAILQ_FIRST(&pool->free_mem_list); mem != NULL;
mem = mem_next) {
mem_next = STAILQ_NEXT(mem, links);
STAILQ_REMOVE(&pool->free_mem_list, mem, ctl_mem_element,
links);
free(mem->bytes, M_CTL_POOL);
free(mem, M_CTL_POOL);
}
mtx_unlock(&pool->lock);
return (0);
}

View File

@ -0,0 +1,83 @@
/*-
* Copyright (c) 2003, 2004 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_mem_pool.h#1 $
* $FreeBSD$
*/
/*
* CAM Target Layer memory pool code.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#ifndef _CTL_MEMPOOL_H_
#define _CTL_MEMPOOL_H_
typedef enum {
CTL_MEM_POOL_NONE,
CTL_MEM_POOL_PERM_GROW
} ctl_mem_pool_flags;
struct ctl_mem_pool {
ctl_mem_pool_flags flags;
int chunk_size;
int grow_inc;
struct mtx lock;
struct cv wait_mem;
STAILQ_HEAD(, ctl_mem_element) free_mem_list;
};
typedef enum {
CTL_MEM_ELEMENT_NONE,
CTL_MEM_ELEMENT_PREALLOC
} ctl_mem_element_flags;
struct ctl_mem_element {
ctl_mem_element_flags flags;
struct ctl_mem_pool *pool;
uint8_t *bytes;
STAILQ_ENTRY(ctl_mem_element) links;
};
#ifdef _KERNEL
MALLOC_DECLARE(M_CTL_POOL);
int ctl_init_mem_pool(struct ctl_mem_pool *pool, int chunk_size,
ctl_mem_pool_flags flags, int grow_inc,
int initial_pool_size);
struct ctl_mem_element *ctl_alloc_mem_element(struct ctl_mem_pool *pool,
int can_wait);
void ctl_free_mem_element(struct ctl_mem_element *mem);
int ctl_grow_mem_pool(struct ctl_mem_pool *pool, int count,
int can_wait);
int ctl_shrink_mem_pool(struct ctl_mem_pool *pool);
#endif /* _KERNEL */
#endif /* _CTL_MEMPOOL_H_ */

493
sys/cam/ctl/ctl_private.h Normal file
View File

@ -0,0 +1,493 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2008 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_private.h#7 $
* $FreeBSD$
*/
/*
* CAM Target Layer driver private data structures/definitions.
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#ifndef _CTL_PRIVATE_H_
#define _CTL_PRIVATE_H_
/*
* SCSI vendor and product names.
*/
#define CTL_VENDOR "FREEBSD "
#define CTL_DIRECT_PRODUCT "CTLDISK "
#define CTL_PROCESSOR_PRODUCT "CTLPROCESSOR "
#define CTL_UNKNOWN_PRODUCT "CTLDEVICE "
struct ctl_fe_ioctl_startstop_info {
struct cv sem;
struct ctl_hard_startstop_info hs_info;
};
struct ctl_fe_ioctl_bbrread_info {
struct cv sem;
struct ctl_bbrread_info *bbr_info;
int wakeup_done;
struct mtx *lock;
};
typedef enum {
CTL_IOCTL_INPROG,
CTL_IOCTL_DATAMOVE,
CTL_IOCTL_DONE
} ctl_fe_ioctl_state;
struct ctl_fe_ioctl_params {
struct cv sem;
struct mtx ioctl_mtx;
ctl_fe_ioctl_state state;
};
#define CTL_POOL_ENTRIES_INTERNAL 200
#define CTL_POOL_ENTRIES_EMERGENCY 300
#define CTL_POOL_ENTRIES_OTHER_SC 200
typedef enum {
CTL_POOL_INTERNAL,
CTL_POOL_FETD,
CTL_POOL_EMERGENCY,
CTL_POOL_IOCTL,
CTL_POOL_4OTHERSC
} ctl_pool_type;
typedef enum {
CTL_POOL_FLAG_NONE = 0x00,
CTL_POOL_FLAG_INVALID = 0x01
} ctl_pool_flags;
struct ctl_io_pool {
ctl_pool_type type;
ctl_pool_flags flags;
uint32_t id;
struct ctl_softc *ctl_softc;
uint32_t refcount;
uint64_t total_allocated;
uint64_t total_freed;
int32_t total_ctl_io;
int32_t free_ctl_io;
STAILQ_HEAD(, ctl_io_hdr) free_queue;
STAILQ_ENTRY(ctl_io_pool) links;
};
typedef enum {
CTL_IOCTL_FLAG_NONE = 0x00,
CTL_IOCTL_FLAG_ENABLED = 0x01
} ctl_ioctl_flags;
struct ctl_ioctl_info {
ctl_ioctl_flags flags;
uint32_t cur_tag_num;
struct ctl_frontend fe;
char port_name[24];
};
typedef enum {
CTL_SER_BLOCK,
CTL_SER_EXTENT,
CTL_SER_PASS,
CTL_SER_SKIP
} ctl_serialize_action;
typedef enum {
CTL_ACTION_BLOCK,
CTL_ACTION_OVERLAP,
CTL_ACTION_OVERLAP_TAG,
CTL_ACTION_PASS,
CTL_ACTION_SKIP,
CTL_ACTION_ERROR
} ctl_action;
/*
* WARNING: Keep the bottom nibble here free, we OR in the data direction
* flags for each command.
*
* Note: "OK_ON_ALL_LUNS" == we don't have to have a lun configured
* "OK_ON_BOTH" == we have to have a lun configured
*/
typedef enum {
CTL_CMD_FLAG_NONE = 0x0000,
CTL_CMD_FLAG_NO_SENSE = 0x0010,
CTL_CMD_FLAG_OK_ON_ALL_LUNS = 0x0020,
CTL_CMD_FLAG_ALLOW_ON_RESV = 0x0040,
CTL_CMD_FLAG_OK_ON_PROC = 0x0100,
CTL_CMD_FLAG_OK_ON_SLUN = 0x0200,
CTL_CMD_FLAG_OK_ON_BOTH = 0x0300,
CTL_CMD_FLAG_OK_ON_STOPPED = 0x0400,
CTL_CMD_FLAG_OK_ON_INOPERABLE = 0x0800,
CTL_CMD_FLAG_OK_ON_OFFLINE = 0x1000,
CTL_CMD_FLAG_OK_ON_SECONDARY = 0x2000,
CTL_CMD_FLAG_ALLOW_ON_PR_RESV = 0x4000
} ctl_cmd_flags;
typedef enum {
CTL_SERIDX_TUR = 0,
CTL_SERIDX_READ,
CTL_SERIDX_WRITE,
CTL_SERIDX_MD_SNS,
CTL_SERIDX_MD_SEL,
CTL_SERIDX_RQ_SNS,
CTL_SERIDX_INQ,
CTL_SERIDX_RD_CAP,
CTL_SERIDX_RESV,
CTL_SERIDX_REL,
CTL_SERIDX_LOG_SNS,
CTL_SERIDX_FORMAT,
CTL_SERIDX_START,
CTL_SERIDX_PRES_IN,
CTL_SERIDX_PRES_OUT,
CTL_SERIDX_MAIN_IN,
/* TBD: others to be filled in as needed */
CTL_SERIDX_COUNT, /* LAST, not a normal code, provides # codes */
CTL_SERIDX_INVLD = CTL_SERIDX_COUNT
} ctl_seridx;
typedef int ctl_opfunc(struct ctl_scsiio *ctsio);
struct ctl_cmd_entry {
ctl_opfunc *execute;
ctl_seridx seridx;
ctl_cmd_flags flags;
ctl_lun_error_pattern pattern;
};
typedef enum {
CTL_LUN_NONE = 0x000,
CTL_LUN_CONTROL = 0x001,
CTL_LUN_RESERVED = 0x002,
CTL_LUN_INVALID = 0x004,
CTL_LUN_DISABLED = 0x008,
CTL_LUN_MALLOCED = 0x010,
CTL_LUN_STOPPED = 0x020,
CTL_LUN_INOPERABLE = 0x040,
CTL_LUN_OFFLINE = 0x080,
CTL_LUN_PR_RESERVED = 0x100,
CTL_LUN_PRIMARY_SC = 0x200,
CTL_LUN_SENSE_DESC = 0x400
} ctl_lun_flags;
typedef enum {
CTLBLOCK_FLAG_NONE = 0x00,
CTLBLOCK_FLAG_INVALID = 0x01
} ctlblock_flags;
union ctl_softcs {
struct ctl_softc *ctl_softc;
struct ctlblock_softc *ctlblock_softc;
};
/*
* Mode page defaults.
*/
#if 0
/*
* These values make Solaris trim off some of the capacity.
*/
#define CTL_DEFAULT_SECTORS_PER_TRACK 63
#define CTL_DEFAULT_HEADS 255
/*
* These values seem to work okay.
*/
#define CTL_DEFAULT_SECTORS_PER_TRACK 63
#define CTL_DEFAULT_HEADS 16
/*
* These values work reasonably well.
*/
#define CTL_DEFAULT_SECTORS_PER_TRACK 512
#define CTL_DEFAULT_HEADS 64
#endif
/*
* Solaris is somewhat picky about how many heads and sectors per track you
* have defined in mode pages 3 and 4. These values seem to cause Solaris
* to get the capacity more or less right when you run the format tool.
* They still have problems when dealing with devices larger than 1TB,
* but there isn't anything we can do about that.
*
* For smaller LUN sizes, this ends up causing the number of cylinders to
* work out to 0. Solaris actually recognizes that and comes up with its
* own bogus geometry to fit the actual capacity of the drive. They really
* should just give up on geometry and stick to the read capacity
* information alone for modern disk drives.
*
* One thing worth mentioning about Solaris' mkfs command is that it
* doesn't like sectors per track values larger than 256. 512 seems to
* work okay for format, but causes problems when you try to make a
* filesystem.
*
* Another caveat about these values: the product of these two values
* really should be a power of 2. This is because of the simplistic
* shift-based calculation that we have to use on the i386 platform to
* calculate the number of cylinders here. (If you use a divide, you end
* up calling __udivdi3(), which is a hardware FP call on the PC. On the
* XScale, it is done in software, so you can do that from inside the
* kernel.)
*
* So for the current values (256 S/T, 128 H), we get 32768, which works
* very nicely for calculating cylinders.
*
* If you want to change these values so that their product is no longer a
* power of 2, re-visit the calculation in ctl_init_page_index(). You may
* need to make it a bit more complicated to get the number of cylinders
* right.
*/
#define CTL_DEFAULT_SECTORS_PER_TRACK 256
#define CTL_DEFAULT_HEADS 128
#define CTL_DEFAULT_ROTATION_RATE 10000
struct ctl_page_index;
typedef int ctl_modesen_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index,
int pc);
typedef int ctl_modesel_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index,
uint8_t *page_ptr);
typedef enum {
CTL_PAGE_FLAG_NONE = 0x00,
CTL_PAGE_FLAG_DISK_ONLY = 0x01
} ctl_page_flags;
struct ctl_page_index {
uint8_t page_code;
uint8_t subpage;
uint16_t page_len;
uint8_t *page_data;
ctl_page_flags page_flags;
ctl_modesen_handler *sense_handler;
ctl_modesel_handler *select_handler;
};
#define CTL_PAGE_CURRENT 0x00
#define CTL_PAGE_CHANGEABLE 0x01
#define CTL_PAGE_DEFAULT 0x02
#define CTL_PAGE_SAVED 0x03
static struct ctl_page_index page_index_template[] = {
{SMS_FORMAT_DEVICE_PAGE, 0, sizeof(struct scsi_format_page), NULL,
CTL_PAGE_FLAG_DISK_ONLY, NULL, NULL},
{SMS_RIGID_DISK_PAGE, 0, sizeof(struct scsi_rigid_disk_page), NULL,
CTL_PAGE_FLAG_DISK_ONLY, NULL, NULL},
{SMS_CACHING_PAGE, 0, sizeof(struct scsi_caching_page), NULL,
CTL_PAGE_FLAG_DISK_ONLY, NULL, NULL},
{SMS_CONTROL_MODE_PAGE, 0, sizeof(struct scsi_control_page), NULL,
CTL_PAGE_FLAG_NONE, NULL, ctl_control_page_handler},
{SMS_VENDOR_SPECIFIC_PAGE | SMPH_SPF, PWR_SUBPAGE_CODE,
sizeof(struct copan_power_subpage), NULL, CTL_PAGE_FLAG_NONE,
ctl_power_sp_sense_handler, ctl_power_sp_handler},
{SMS_VENDOR_SPECIFIC_PAGE | SMPH_SPF, APS_SUBPAGE_CODE,
sizeof(struct copan_aps_subpage), NULL, CTL_PAGE_FLAG_NONE,
NULL, ctl_aps_sp_handler},
{SMS_VENDOR_SPECIFIC_PAGE | SMPH_SPF, DBGCNF_SUBPAGE_CODE,
sizeof(struct copan_debugconf_subpage), NULL, CTL_PAGE_FLAG_NONE,
ctl_debugconf_sp_sense_handler, ctl_debugconf_sp_select_handler},
};
#define CTL_NUM_MODE_PAGES sizeof(page_index_template)/ \
sizeof(page_index_template[0])
struct ctl_mode_pages {
struct scsi_format_page format_page[4];
struct scsi_rigid_disk_page rigid_disk_page[4];
struct scsi_caching_page caching_page[4];
struct scsi_control_page control_page[4];
struct copan_power_subpage power_subpage[4];
struct copan_aps_subpage aps_subpage[4];
struct copan_debugconf_subpage debugconf_subpage[4];
struct ctl_page_index index[CTL_NUM_MODE_PAGES];
};
struct ctl_pending_sense {
ctl_ua_type ua_pending;
struct scsi_sense_data sense;
};
struct ctl_lun_delay_info {
ctl_delay_type datamove_type;
uint32_t datamove_delay;
ctl_delay_type done_type;
uint32_t done_delay;
};
typedef enum {
CTL_ERR_INJ_NONE = 0x00,
CTL_ERR_INJ_ABORTED = 0x01
} ctl_err_inject_flags;
typedef enum {
CTL_PR_FLAG_NONE = 0x00,
CTL_PR_FLAG_REGISTERED = 0x01,
CTL_PR_FLAG_ACTIVE_RES = 0x02
} ctl_per_res_flags;
struct ctl_per_res_info {
struct scsi_per_res_key res_key;
uint8_t registered;
};
#define CTL_PR_ALL_REGISTRANTS 0xFFFF
#define CTL_PR_NO_RESERVATION 0xFFF0
/*
* For report target port groups.
*/
#define NUM_TARGET_PORT_GROUPS 2
#define NUM_PORTS_PER_GRP 2
struct ctl_lun {
struct mtx lun_lock;
struct ctl_id target;
uint64_t lun;
ctl_lun_flags flags;
STAILQ_HEAD(,ctl_error_desc) error_list;
uint64_t error_serial;
struct ctl_softc *ctl_softc;
struct ctl_be_lun *be_lun;
struct ctl_backend_driver *backend;
int io_count;
struct ctl_lun_delay_info delay_info;
int sync_interval;
int sync_count;
TAILQ_HEAD(ctl_ooaq, ctl_io_hdr) ooa_queue;
TAILQ_HEAD(ctl_blockq,ctl_io_hdr) blocked_queue;
STAILQ_ENTRY(ctl_lun) links;
STAILQ_ENTRY(ctl_lun) run_links;
struct ctl_nexus rsv_nexus;
uint32_t have_ca[CTL_MAX_INITIATORS >> 5];
struct ctl_pending_sense pending_sense[CTL_MAX_INITIATORS];
struct ctl_mode_pages mode_pages;
struct ctl_lun_io_stats stats;
struct ctl_per_res_info per_res[2*CTL_MAX_INITIATORS];
unsigned int PRGeneration;
int pr_key_count;
uint16_t pr_res_idx;
uint8_t res_type;
uint8_t write_buffer[524288];
};
typedef enum {
CTL_FLAG_TASK_PENDING = 0x01,
CTL_FLAG_REAL_SYNC = 0x02,
CTL_FLAG_MASTER_SHELF = 0x04
} ctl_gen_flags;
struct ctl_wwpn_iid {
int in_use;
uint64_t wwpn;
uint32_t iid;
int32_t port;
};
struct ctl_softc {
struct mtx ctl_lock;
struct cdev *dev;
int open_count;
struct ctl_id target;
int num_disks;
int num_luns;
ctl_gen_flags flags;
ctl_ha_mode ha_mode;
struct ctl_ioctl_info ioctl_info;
struct ctl_lun lun;
struct ctl_io_pool *internal_pool;
struct ctl_io_pool *emergency_pool;
struct ctl_io_pool *othersc_pool;
struct proc *work_thread;
int targ_online;
uint32_t ctl_lun_mask[CTL_MAX_LUNS >> 5];
struct ctl_lun *ctl_luns[CTL_MAX_LUNS];
struct ctl_wwpn_iid wwpn_iid[CTL_MAX_PORTS][CTL_MAX_INIT_PER_PORT];
uint32_t ctl_port_mask;
uint64_t aps_locked_lun;
STAILQ_HEAD(, ctl_lun) lun_list;
STAILQ_HEAD(, ctl_be_lun) pending_lun_queue;
STAILQ_HEAD(, ctl_io_hdr) task_queue;
STAILQ_HEAD(, ctl_io_hdr) incoming_queue;
STAILQ_HEAD(, ctl_io_hdr) rtr_queue;
STAILQ_HEAD(, ctl_io_hdr) done_queue;
STAILQ_HEAD(, ctl_io_hdr) isc_queue;
uint32_t num_frontends;
STAILQ_HEAD(, ctl_frontend) fe_list;
struct ctl_frontend *ctl_ports[CTL_MAX_PORTS];
uint32_t num_backends;
STAILQ_HEAD(, ctl_backend_driver) be_list;
uint32_t num_pools;
uint32_t cur_pool_id;
STAILQ_HEAD(, ctl_io_pool) io_pools;
time_t last_print_jiffies;
uint32_t skipped_prints;
};
#ifdef _KERNEL
extern struct ctl_cmd_entry ctl_cmd_table[];
uint32_t ctl_get_initindex(struct ctl_nexus *nexus);
int ctl_pool_create(struct ctl_softc *ctl_softc, ctl_pool_type pool_type,
uint32_t total_ctl_io, struct ctl_io_pool **npool);
int ctl_pool_acquire(struct ctl_io_pool *pool);
int ctl_pool_invalidate(struct ctl_io_pool *pool);
int ctl_pool_release(struct ctl_io_pool *pool);
void ctl_pool_free(struct ctl_softc *ctl_softc, struct ctl_io_pool *pool);
int ctl_scsi_release(struct ctl_scsiio *ctsio);
int ctl_scsi_reserve(struct ctl_scsiio *ctsio);
int ctl_start_stop(struct ctl_scsiio *ctsio);
int ctl_sync_cache(struct ctl_scsiio *ctsio);
int ctl_format(struct ctl_scsiio *ctsio);
int ctl_write_buffer(struct ctl_scsiio *ctsio);
int ctl_mode_select(struct ctl_scsiio *ctsio);
int ctl_mode_sense(struct ctl_scsiio *ctsio);
int ctl_read_capacity(struct ctl_scsiio *ctsio);
int ctl_service_action_in(struct ctl_scsiio *ctsio);
int ctl_read_write(struct ctl_scsiio *ctsio);
int ctl_report_luns(struct ctl_scsiio *ctsio);
int ctl_request_sense(struct ctl_scsiio *ctsio);
int ctl_tur(struct ctl_scsiio *ctsio);
int ctl_inquiry(struct ctl_scsiio *ctsio);
int ctl_persistent_reserve_in(struct ctl_scsiio *ctsio);
int ctl_persistent_reserve_out(struct ctl_scsiio *ctsio);
int ctl_maintenance_in(struct ctl_scsiio *ctsio);
void ctl_done_lock(union ctl_io *io, int have_lock);
int ctl_isc(struct ctl_scsiio *ctsio);
#endif /* _KERNEL */
#endif /* _CTL_PRIVATE_H_ */
/*
* vim: ts=8
*/

227
sys/cam/ctl/ctl_scsi_all.c Normal file
View File

@ -0,0 +1,227 @@
/*-
* Implementation of Utility functions for all SCSI device types.
*
* Copyright (c) 1997, 1998, 1999 Justin T. Gibbs.
* Copyright (c) 1997, 1998, 2003 Kenneth D. Merry.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification, immediately at the beginning of the file.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_scsi_all.c#2 $
*/
#include <sys/param.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#ifdef _KERNEL
#include <sys/systm.h>
#include <sys/libkern.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#else
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#endif
#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <cam/cam_queue.h>
#include <cam/cam_xpt.h>
#include <cam/scsi/scsi_all.h>
#include <cam/ctl/ctl_io.h>
#include <cam/ctl/ctl_scsi_all.h>
#include <sys/sbuf.h>
#ifndef _KERNEL
#include <camlib.h>
#endif
const char *
ctl_scsi_status_string(struct ctl_scsiio *ctsio)
{
switch(ctsio->scsi_status) {
case SCSI_STATUS_OK:
return("OK");
case SCSI_STATUS_CHECK_COND:
return("Check Condition");
case SCSI_STATUS_BUSY:
return("Busy");
case SCSI_STATUS_INTERMED:
return("Intermediate");
case SCSI_STATUS_INTERMED_COND_MET:
return("Intermediate-Condition Met");
case SCSI_STATUS_RESERV_CONFLICT:
return("Reservation Conflict");
case SCSI_STATUS_CMD_TERMINATED:
return("Command Terminated");
case SCSI_STATUS_QUEUE_FULL:
return("Queue Full");
case SCSI_STATUS_ACA_ACTIVE:
return("ACA Active");
case SCSI_STATUS_TASK_ABORTED:
return("Task Aborted");
default: {
static char unkstr[64];
snprintf(unkstr, sizeof(unkstr), "Unknown %#x",
ctsio->scsi_status);
return(unkstr);
}
}
}
/*
* scsi_command_string() returns 0 for success and -1 for failure.
*/
int
ctl_scsi_command_string(struct ctl_scsiio *ctsio,
struct scsi_inquiry_data *inq_data, struct sbuf *sb)
{
char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1];
sbuf_printf(sb, "%s. CDB: %s",
scsi_op_desc(ctsio->cdb[0], inq_data),
scsi_cdb_string(ctsio->cdb, cdb_str, sizeof(cdb_str)));
return(0);
}
void
ctl_scsi_path_string(union ctl_io *io, char *path_str, int len)
{
if (io->io_hdr.nexus.targ_target.wwid[0] == 0) {
snprintf(path_str, len, "(%ju:%d:%ju:%d): ",
(uintmax_t)io->io_hdr.nexus.initid.id,
io->io_hdr.nexus.targ_port,
(uintmax_t)io->io_hdr.nexus.targ_target.id,
io->io_hdr.nexus.targ_lun);
} else {
/*
* XXX KDM find a better way to display FC WWIDs.
*/
#ifdef _KERNEL
snprintf(path_str, len, "(%ju:%d:%#jx,%#jx:%d): ",
(uintmax_t)io->io_hdr.nexus.initid.id,
io->io_hdr.nexus.targ_port,
(intmax_t)io->io_hdr.nexus.targ_target.wwid[0],
(intmax_t)io->io_hdr.nexus.targ_target.wwid[1],
io->io_hdr.nexus.targ_lun);
#else /* _KERNEL */
snprintf(path_str, len, "(%ju:%d:%#jx,%#jx:%d): ",
(uintmax_t)io->io_hdr.nexus.initid.id,
io->io_hdr.nexus.targ_port,
(intmax_t)io->io_hdr.nexus.targ_target.wwid[0],
(intmax_t)io->io_hdr.nexus.targ_target.wwid[1],
io->io_hdr.nexus.targ_lun);
#endif /* _KERNEL */
}
}
/*
* ctl_scsi_sense_sbuf() returns 0 for success and -1 for failure.
*/
int
ctl_scsi_sense_sbuf(struct ctl_scsiio *ctsio,
struct scsi_inquiry_data *inq_data, struct sbuf *sb,
scsi_sense_string_flags flags)
{
char path_str[64];
if ((ctsio == NULL) || (sb == NULL))
return(-1);
ctl_scsi_path_string((union ctl_io *)ctsio, path_str, sizeof(path_str));
if (flags & SSS_FLAG_PRINT_COMMAND) {
sbuf_cat(sb, path_str);
ctl_scsi_command_string(ctsio, inq_data, sb);
sbuf_printf(sb, "\n");
}
scsi_sense_only_sbuf(&ctsio->sense_data, ctsio->sense_len, sb,
path_str, inq_data, ctsio->cdb, ctsio->cdb_len);
return(0);
}
char *
ctl_scsi_sense_string(struct ctl_scsiio *ctsio,
struct scsi_inquiry_data *inq_data, char *str,
int str_len)
{
struct sbuf sb;
sbuf_new(&sb, str, str_len, 0);
ctl_scsi_sense_sbuf(ctsio, inq_data, &sb, SSS_FLAG_PRINT_COMMAND);
sbuf_finish(&sb);
return(sbuf_data(&sb));
}
#ifdef _KERNEL
void
ctl_scsi_sense_print(struct ctl_scsiio *ctsio,
struct scsi_inquiry_data *inq_data)
{
struct sbuf sb;
char str[512];
sbuf_new(&sb, str, sizeof(str), 0);
ctl_scsi_sense_sbuf(ctsio, inq_data, &sb, SSS_FLAG_PRINT_COMMAND);
sbuf_finish(&sb);
printf("%s", sbuf_data(&sb));
}
#else /* _KERNEL */
void
ctl_scsi_sense_print(struct ctl_scsiio *ctsio,
struct scsi_inquiry_data *inq_data, FILE *ofile)
{
struct sbuf sb;
char str[512];
if ((ctsio == NULL) || (ofile == NULL))
return;
sbuf_new(&sb, str, sizeof(str), 0);
ctl_scsi_sense_sbuf(ctsio, inq_data, &sb, SSS_FLAG_PRINT_COMMAND);
sbuf_finish(&sb);
fprintf(ofile, "%s", sbuf_data(&sb));
}
#endif /* _KERNEL */

View File

@ -0,0 +1,52 @@
/*-
* Copyright (c) 1997, 1998, 1999 Justin T. Gibbs.
* Copyright (c) 1997, 1998, 2003 Kenneth D. Merry.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification, immediately at the beginning of the file.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_scsi_all.h#2 $
*/
__FBSDID("$FreeBSD$");
__BEGIN_DECLS
const char * ctl_scsi_status_string(struct ctl_scsiio *ctsio);
#ifdef _KERNEL
void ctl_scsi_sense_print(struct ctl_scsiio *ctsio,
struct scsi_inquiry_data *inq_data);
#else /* _KERNEL */
void ctl_scsi_sense_print(struct ctl_scsiio *ctsio,
struct scsi_inquiry_data *inq_data,
FILE *ofile);
#endif /* _KERNEL */
int ctl_scsi_command_string(struct ctl_scsiio *ctsio,
struct scsi_inquiry_data *inq_data,struct sbuf *sb);
int ctl_scsi_sense_sbuf(struct ctl_scsiio *ctsio,
struct scsi_inquiry_data *inq_data, struct sbuf *sb,
scsi_sense_string_flags flags);
void ctl_scsi_path_string(union ctl_io *io, char *path_str, int strlen);
char *ctl_scsi_sense_string(struct ctl_scsiio *ctsio,
struct scsi_inquiry_data *inq_data, char *str,
int str_len);
__END_DECLS

View File

@ -0,0 +1,81 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_ser_table.c#1 $
* $FreeBSD$
*/
/*
* CAM Target Layer command serialization table.
*
* Author: Kim Le
*/
/****************************************************************************/
/* TABLE ctlSerTbl */
/* */
/* The matrix which drives the serialization algorithm. The major index */
/* (the first) into this table is the command being checked and the minor */
/* index is the command against which the first command is being checked. */
/* i.e., the major index (row) command is ahead of the minor index command */
/* (column) in the queue. This allows the code to optimize by capturing */
/* the result of the first indexing operation into a pointer. */
/* */
/* Whenever a new value is added to the IDX_T type, this matrix must be */
/* expanded by one row AND one column -- Because of this, some effort */
/* should be made to re-use the indexes whenever possible. */
/* */
/****************************************************************************/
#define sK CTL_SER_SKIP /* Skip */
#define pS CTL_SER_PASS /* pS */
#define bK CTL_SER_BLOCK /* Blocked */
#define xT CTL_SER_EXTENT /* Extent check */
static ctl_serialize_action
ctl_serialize_table[CTL_SERIDX_COUNT][CTL_SERIDX_COUNT] = {
/**>IDX_ :: 2nd:TUR RD WRT MDSN MDSL RQSN INQ RDCP RES REL LSNS FMT STR PRIN PROT MAININ*/
/*TUR */{ pS, pS, pS, bK, bK, bK, pS, pS, bK, bK, pS, bK, bK, bK, bK, bK},
/*READ */{ pS, pS, xT, bK, bK, bK, pS, pS, bK, bK, pS, bK, bK, bK, bK, bK},
/*WRITE */{ pS, xT, xT, bK, bK, bK, pS, pS, bK, bK, pS, bK, bK, bK, bK, bK},
/*MD_SNS */{ bK, bK, bK, pS, bK, bK, pS, pS, bK, bK, pS, bK, bK, bK, bK, bK},
/*MD_SEL */{ bK, bK, bK, bK, bK, bK, pS, pS, bK, bK, pS, bK, bK, bK, bK, bK},
/*RQ_SNS */{ pS, pS, pS, pS, pS, bK, pS, pS, bK, bK, pS, bK, bK, bK, bK, bK},
/*INQ */{ pS, pS, pS, pS, pS, bK, pS, pS, bK, bK, pS, bK, bK, bK, bK, bK},
/*RD_CAP */{ pS, pS, pS, pS, pS, bK, pS, pS, bK, bK, pS, bK, bK, bK, bK, bK},
/*RESV */{ bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK},
/*REL */{ bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK},
/*LOG_SNS */{ pS, pS, pS, pS, bK, bK, pS, pS, bK, bK, pS, bK, bK, bK, bK, bK},
/*FORMAT */{ pS, bK, bK, bK, bK, pS, pS, bK, bK, bK, bK, bK, bK, bK, bK, bK},
/*START */{ bK, bK, bK, bK, bK, bK, pS, bK, bK, bK, bK, bK, bK, bK, bK, bK},
/*PRES_IN */{ bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK},
/*PRES_OUT*/{ bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK, bK},
/*MAIN_IN */{ bK, bK, bK, bK, bK, bK, pS, bK, bK, bK, bK, bK, bK, bK, bK, pS}
};

843
sys/cam/ctl/ctl_util.c Normal file
View File

@ -0,0 +1,843 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_util.c#2 $
*/
/*
* CAM Target Layer SCSI library
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <sys/malloc.h>
#else /* __KERNEL__ */
#include <sys/types.h>
#include <sys/time.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif /* __KERNEL__ */
#include <sys/sbuf.h>
#include <sys/queue.h>
#include <sys/callout.h>
#include <cam/scsi/scsi_all.h>
#include <cam/ctl/ctl_io.h>
#include <cam/ctl/ctl_scsi_all.h>
#include <cam/ctl/ctl_util.h>
struct ctl_status_desc {
ctl_io_status status;
const char *description;
};
struct ctl_task_desc {
ctl_task_type task_action;
const char *description;
};
static struct ctl_status_desc ctl_status_table[] = {
{CTL_STATUS_NONE, "No Status"},
{CTL_SUCCESS, "Command Completed Successfully"},
{CTL_CMD_TIMEOUT, "Command Timed Out"},
{CTL_SEL_TIMEOUT, "Selection Timeout"},
{CTL_ERROR, "Command Failed"},
{CTL_SCSI_ERROR, "SCSI Error"},
{CTL_CMD_ABORTED, "Command Aborted"},
};
static struct ctl_task_desc ctl_task_table[] = {
{CTL_TASK_ABORT_TASK, "Abort Task"},
{CTL_TASK_ABORT_TASK_SET, "Abort Task Set"},
{CTL_TASK_CLEAR_ACA, "Clear ACA"},
{CTL_TASK_CLEAR_TASK_SET, "Clear Task Set"},
{CTL_TASK_LUN_RESET, "LUN Reset"},
{CTL_TASK_TARGET_RESET, "Target Reset"},
{CTL_TASK_BUS_RESET, "Bus Reset"},
{CTL_TASK_PORT_LOGIN, "Port Login"},
{CTL_TASK_PORT_LOGOUT, "Port Logout"}
};
void
ctl_scsi_tur(union ctl_io *io, ctl_tag_type tag_type, uint8_t control)
{
struct ctl_scsiio *ctsio;
struct scsi_test_unit_ready *cdb;
ctl_scsi_zero_io(io);
io->io_hdr.io_type = CTL_IO_SCSI;
ctsio = &io->scsiio;
cdb = (struct scsi_test_unit_ready *)ctsio->cdb;
cdb->opcode = TEST_UNIT_READY;
cdb->control = control;
io->io_hdr.flags = CTL_FLAG_DATA_NONE;
ctsio->tag_type = tag_type;
ctsio->cdb_len = sizeof(*cdb);
ctsio->ext_data_len = 0;
ctsio->ext_data_ptr = NULL;
ctsio->ext_sg_entries = 0;
ctsio->ext_data_filled = 0;
ctsio->sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_inquiry(union ctl_io *io, uint8_t *data_ptr, int32_t data_len,
uint8_t byte2, uint8_t page_code, ctl_tag_type tag_type,
uint8_t control)
{
struct ctl_scsiio *ctsio;
struct scsi_inquiry *cdb;
ctl_scsi_zero_io(io);
io->io_hdr.io_type = CTL_IO_SCSI;
ctsio = &io->scsiio;
cdb = (struct scsi_inquiry *)ctsio->cdb;
cdb->opcode = INQUIRY;
cdb->byte2 = byte2;
cdb->page_code = page_code;
cdb->control = control;
scsi_ulto2b(data_len, cdb->length);
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.flags = CTL_FLAG_DATA_IN;
ctsio->tag_type = tag_type;
ctsio->cdb_len = sizeof(*cdb);
ctsio->ext_data_len = data_len;
ctsio->ext_data_ptr = data_ptr;
ctsio->ext_sg_entries = 0;
ctsio->ext_data_filled = 0;
ctsio->sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_request_sense(union ctl_io *io, uint8_t *data_ptr,
int32_t data_len, uint8_t byte2, ctl_tag_type tag_type,
uint8_t control)
{
struct ctl_scsiio *ctsio;
struct scsi_request_sense *cdb;
ctl_scsi_zero_io(io);
io->io_hdr.io_type = CTL_IO_SCSI;
ctsio = &io->scsiio;
cdb = (struct scsi_request_sense *)ctsio->cdb;
cdb->opcode = REQUEST_SENSE;
cdb->byte2 = byte2;
cdb->control = control;
cdb->length = data_len;
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.flags = CTL_FLAG_DATA_IN;
ctsio->tag_type = tag_type;
ctsio->cdb_len = sizeof(*cdb);
ctsio->ext_data_ptr = data_ptr;
ctsio->ext_data_len = data_len;
ctsio->ext_sg_entries = 0;
ctsio->ext_data_filled = 0;
ctsio->sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_report_luns(union ctl_io *io, uint8_t *data_ptr, uint32_t data_len,
uint8_t select_report, ctl_tag_type tag_type,
uint8_t control)
{
struct ctl_scsiio *ctsio;
struct scsi_report_luns *cdb;
ctl_scsi_zero_io(io);
io->io_hdr.io_type = CTL_IO_SCSI;
ctsio = &io->scsiio;
cdb = (struct scsi_report_luns *)ctsio->cdb;
cdb->opcode = REPORT_LUNS;
cdb->select_report = select_report;
scsi_ulto4b(data_len, cdb->length);
cdb->control = control;
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.flags = CTL_FLAG_DATA_IN;
ctsio->tag_type = tag_type;
ctsio->cdb_len = sizeof(*cdb);
ctsio->ext_data_ptr = data_ptr;
ctsio->ext_data_len = data_len;
ctsio->ext_sg_entries = 0;
ctsio->ext_data_filled = 0;
ctsio->sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_read_write_buffer(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, int read_buffer, uint8_t mode,
uint8_t buffer_id, uint32_t buffer_offset,
ctl_tag_type tag_type, uint8_t control)
{
struct ctl_scsiio *ctsio;
struct scsi_write_buffer *cdb;
ctl_scsi_zero_io(io);
io->io_hdr.io_type = CTL_IO_SCSI;
ctsio = &io->scsiio;
cdb = (struct scsi_write_buffer *)ctsio->cdb;
if (read_buffer != 0)
cdb->opcode = READ_BUFFER;
else
cdb->opcode = WRITE_BUFFER;
cdb->byte2 = mode & RWB_MODE;
cdb->buffer_id = buffer_id;
scsi_ulto3b(buffer_offset, cdb->offset);
scsi_ulto3b(data_len, cdb->length);
cdb->control = control;
io->io_hdr.io_type = CTL_IO_SCSI;
if (read_buffer != 0)
io->io_hdr.flags = CTL_FLAG_DATA_IN;
else
io->io_hdr.flags = CTL_FLAG_DATA_OUT;
ctsio->tag_type = tag_type;
ctsio->cdb_len = sizeof(*cdb);
ctsio->ext_data_ptr = data_ptr;
ctsio->ext_data_len = data_len;
ctsio->ext_sg_entries = 0;
ctsio->ext_data_filled = 0;
ctsio->sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_read_write(union ctl_io *io, uint8_t *data_ptr, uint32_t data_len,
int read_op, uint8_t byte2, int minimum_cdb_size,
uint64_t lba, uint32_t num_blocks, ctl_tag_type tag_type,
uint8_t control)
{
struct ctl_scsiio *ctsio;
ctl_scsi_zero_io(io);
io->io_hdr.io_type = CTL_IO_SCSI;
ctsio = &io->scsiio;
/*
* Pick out the smallest CDB that will hold the user's request.
* minimum_cdb_size allows cranking the CDB size up, even for
* requests that would not normally need a large CDB. This can be
* useful for testing (e.g. to make sure READ_16 support works without
* having an array larger than 2TB) and for compatibility -- e.g.
* if your device doesn't support READ_6. (ATAPI drives don't.)
*/
if ((minimum_cdb_size < 10)
&& ((lba & 0x1fffff) == lba)
&& ((num_blocks & 0xff) == num_blocks)
&& (byte2 == 0)) {
struct scsi_rw_6 *cdb;
/*
* Note that according to SBC-2, the target should return 256
* blocks if the transfer length in a READ(6) or WRITE(6) CDB
* is set to 0. Since it's possible that some targets
* won't do the right thing, we only send a READ(6) or
* WRITE(6) for transfer sizes up to and including 255 blocks.
*/
cdb = (struct scsi_rw_6 *)ctsio->cdb;
cdb->opcode = (read_op) ? READ_6 : WRITE_6;
scsi_ulto3b(lba, cdb->addr);
cdb->length = num_blocks & 0xff;
cdb->control = control;
ctsio->cdb_len = sizeof(*cdb);
} else if ((minimum_cdb_size < 12)
&& ((num_blocks & 0xffff) == num_blocks)
&& ((lba & 0xffffffff) == lba)) {
struct scsi_rw_10 *cdb;
cdb = (struct scsi_rw_10 *)ctsio->cdb;
cdb->opcode = (read_op) ? READ_10 : WRITE_10;
cdb->byte2 = byte2;
scsi_ulto4b(lba, cdb->addr);
cdb->reserved = 0;
scsi_ulto2b(num_blocks, cdb->length);
cdb->control = control;
ctsio->cdb_len = sizeof(*cdb);
} else if ((minimum_cdb_size < 16)
&& ((num_blocks & 0xffffffff) == num_blocks)
&& ((lba & 0xffffffff) == lba)) {
struct scsi_rw_12 *cdb;
cdb = (struct scsi_rw_12 *)ctsio->cdb;
cdb->opcode = (read_op) ? READ_12 : WRITE_12;
cdb->byte2 = byte2;
scsi_ulto4b(lba, cdb->addr);
scsi_ulto4b(num_blocks, cdb->length);
cdb->reserved = 0;
cdb->control = control;
ctsio->cdb_len = sizeof(*cdb);
} else {
struct scsi_rw_16 *cdb;
cdb = (struct scsi_rw_16 *)ctsio->cdb;
cdb->opcode = (read_op) ? READ_16 : WRITE_16;
cdb->byte2 = byte2;
scsi_u64to8b(lba, cdb->addr);
scsi_ulto4b(num_blocks, cdb->length);
cdb->reserved = 0;
cdb->control = control;
ctsio->cdb_len = sizeof(*cdb);
}
io->io_hdr.io_type = CTL_IO_SCSI;
if (read_op != 0)
io->io_hdr.flags = CTL_FLAG_DATA_IN;
else
io->io_hdr.flags = CTL_FLAG_DATA_OUT;
ctsio->tag_type = tag_type;
ctsio->ext_data_ptr = data_ptr;
ctsio->ext_data_len = data_len;
ctsio->ext_sg_entries = 0;
ctsio->ext_data_filled = 0;
ctsio->sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_read_capacity(union ctl_io *io, uint8_t *data_ptr, uint32_t data_len,
uint32_t addr, int reladr, int pmi,
ctl_tag_type tag_type, uint8_t control)
{
struct scsi_read_capacity *cdb;
ctl_scsi_zero_io(io);
io->io_hdr.io_type = CTL_IO_SCSI;
cdb = (struct scsi_read_capacity *)io->scsiio.cdb;
cdb->opcode = READ_CAPACITY;
if (reladr)
cdb->byte2 = SRC_RELADR;
if (pmi)
cdb->pmi = SRC_PMI;
scsi_ulto4b(addr, cdb->addr);
cdb->control = control;
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.flags = CTL_FLAG_DATA_IN;
io->scsiio.tag_type = tag_type;
io->scsiio.ext_data_ptr = data_ptr;
io->scsiio.ext_data_len = data_len;
io->scsiio.ext_sg_entries = 0;
io->scsiio.ext_data_filled = 0;
io->scsiio.sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_read_capacity_16(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, uint64_t addr, int reladr,
int pmi, ctl_tag_type tag_type, uint8_t control)
{
struct scsi_read_capacity_16 *cdb;
ctl_scsi_zero_io(io);
io->io_hdr.io_type = CTL_IO_SCSI;
cdb = (struct scsi_read_capacity_16 *)io->scsiio.cdb;
cdb->opcode = SERVICE_ACTION_IN;
cdb->service_action = SRC16_SERVICE_ACTION;
if (reladr)
cdb->reladr |= SRC16_RELADR;
if (pmi)
cdb->reladr |= SRC16_PMI;
scsi_u64to8b(addr, cdb->addr);
scsi_ulto4b(data_len, cdb->alloc_len);
cdb->control = control;
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.flags = CTL_FLAG_DATA_IN;
io->scsiio.tag_type = tag_type;
io->scsiio.ext_data_ptr = data_ptr;
io->scsiio.ext_data_len = data_len;
io->scsiio.ext_sg_entries = 0;
io->scsiio.ext_data_filled = 0;
io->scsiio.sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_mode_sense(union ctl_io *io, uint8_t *data_ptr, uint32_t data_len,
int dbd, int llbaa, uint8_t page_code, uint8_t pc,
uint8_t subpage, int minimum_cdb_size,
ctl_tag_type tag_type, uint8_t control)
{
ctl_scsi_zero_io(io);
if ((minimum_cdb_size < 10)
&& (llbaa == 0)
&& (data_len < 256)) {
struct scsi_mode_sense_6 *cdb;
cdb = (struct scsi_mode_sense_6 *)io->scsiio.cdb;
cdb->opcode = MODE_SENSE_6;
if (dbd)
cdb->byte2 |= SMS_DBD;
cdb->page = page_code | pc;
cdb->subpage = subpage;
cdb->length = data_len;
cdb->control = control;
} else {
struct scsi_mode_sense_10 *cdb;
cdb = (struct scsi_mode_sense_10 *)io->scsiio.cdb;
cdb->opcode = MODE_SENSE_10;
if (dbd)
cdb->byte2 |= SMS_DBD;
if (llbaa)
cdb->byte2 |= SMS10_LLBAA;
cdb->page = page_code | pc;
cdb->subpage = subpage;
scsi_ulto2b(data_len, cdb->length);
cdb->control = control;
}
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.flags = CTL_FLAG_DATA_IN;
io->scsiio.tag_type = tag_type;
io->scsiio.ext_data_ptr = data_ptr;
io->scsiio.ext_data_len = data_len;
io->scsiio.ext_sg_entries = 0;
io->scsiio.ext_data_filled = 0;
io->scsiio.sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_start_stop(union ctl_io *io, int start, int load_eject, int immediate,
int power_conditions, int onoffline __unused,
ctl_tag_type tag_type, uint8_t control)
{
struct scsi_start_stop_unit *cdb;
cdb = (struct scsi_start_stop_unit *)io->scsiio.cdb;
ctl_scsi_zero_io(io);
cdb->opcode = START_STOP_UNIT;
if (immediate)
cdb->byte2 |= SSS_IMMED;
#ifdef NEEDTOPORT
if (onoffline)
cdb->byte2 |= SSS_ONOFFLINE;
#endif
cdb->how = power_conditions;
if (load_eject)
cdb->how |= SSS_LOEJ;
if (start)
cdb->how |= SSS_START;
cdb->control = control;
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.flags = CTL_FLAG_DATA_NONE;
io->scsiio.tag_type = tag_type;
io->scsiio.ext_data_ptr = NULL;
io->scsiio.ext_data_len = 0;
io->scsiio.ext_sg_entries = 0;
io->scsiio.ext_data_filled = 0;
io->scsiio.sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_sync_cache(union ctl_io *io, int immed, int reladr,
int minimum_cdb_size, uint64_t starting_lba,
uint32_t block_count, ctl_tag_type tag_type,
uint8_t control)
{
ctl_scsi_zero_io(io);
if ((minimum_cdb_size < 16)
&& ((block_count & 0xffff) == block_count)
&& ((starting_lba & 0xffffffff) == starting_lba)) {
struct scsi_sync_cache *cdb;
cdb = (struct scsi_sync_cache *)io->scsiio.cdb;
cdb->opcode = SYNCHRONIZE_CACHE;
if (reladr)
cdb->byte2 |= SSC_RELADR;
if (immed)
cdb->byte2 |= SSC_IMMED;
scsi_ulto4b(starting_lba, cdb->begin_lba);
scsi_ulto2b(block_count, cdb->lb_count);
cdb->control = control;
} else {
struct scsi_sync_cache_16 *cdb;
cdb = (struct scsi_sync_cache_16 *)io->scsiio.cdb;
cdb->opcode = SYNCHRONIZE_CACHE_16;
if (reladr)
cdb->byte2 |= SSC_RELADR;
if (immed)
cdb->byte2 |= SSC_IMMED;
scsi_u64to8b(starting_lba, cdb->begin_lba);
scsi_ulto4b(block_count, cdb->lb_count);
cdb->control = control;
}
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.flags = CTL_FLAG_DATA_NONE;
io->scsiio.tag_type = tag_type;
io->scsiio.ext_data_ptr = NULL;
io->scsiio.ext_data_len = 0;
io->scsiio.ext_sg_entries = 0;
io->scsiio.ext_data_filled = 0;
io->scsiio.sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_persistent_res_in(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, int action,
ctl_tag_type tag_type, uint8_t control)
{
struct scsi_per_res_in *cdb;
ctl_scsi_zero_io(io);
cdb = (struct scsi_per_res_in *)io->scsiio.cdb;
cdb->opcode = PERSISTENT_RES_IN;
cdb->action = action;
scsi_ulto2b(data_len, cdb->length);
cdb->control = control;
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.flags = CTL_FLAG_DATA_IN;
io->scsiio.tag_type = tag_type;
io->scsiio.ext_data_ptr = data_ptr;
io->scsiio.ext_data_len = data_len;
io->scsiio.ext_sg_entries = 0;
io->scsiio.ext_data_filled = 0;
io->scsiio.sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_persistent_res_out(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, int action, int type,
uint64_t key, uint64_t sa_key,
ctl_tag_type tag_type, uint8_t control)
{
struct scsi_per_res_out *cdb;
struct scsi_per_res_out_parms *params;
ctl_scsi_zero_io(io);
cdb = (struct scsi_per_res_out *)io->scsiio.cdb;
params = (struct scsi_per_res_out_parms *)data_ptr;
cdb->opcode = PERSISTENT_RES_OUT;
if (action == 5)
cdb->action = 6;
else
cdb->action = action;
switch(type)
{
case 0:
cdb->scope_type = 1;
break;
case 1:
cdb->scope_type = 3;
break;
case 2:
cdb->scope_type = 5;
break;
case 3:
cdb->scope_type = 6;
break;
case 4:
cdb->scope_type = 7;
break;
case 5:
cdb->scope_type = 8;
break;
}
scsi_ulto4b(data_len, cdb->length);
cdb->control = control;
scsi_u64to8b(key, params->res_key.key);
scsi_u64to8b(sa_key, params->serv_act_res_key);
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.flags = CTL_FLAG_DATA_OUT;
io->scsiio.tag_type = tag_type;
io->scsiio.ext_data_ptr = data_ptr;
io->scsiio.ext_data_len = data_len;
io->scsiio.ext_sg_entries = 0;
io->scsiio.ext_data_filled = 0;
io->scsiio.sense_len = SSD_FULL_SIZE;
}
void
ctl_scsi_maintenance_in(union ctl_io *io, uint8_t *data_ptr, uint32_t data_len,
uint8_t action, ctl_tag_type tag_type, uint8_t control)
{
struct scsi_maintenance_in *cdb;
ctl_scsi_zero_io(io);
cdb = (struct scsi_maintenance_in *)io->scsiio.cdb;
cdb->opcode = MAINTENANCE_IN;
cdb->byte2 = action;
scsi_ulto4b(data_len, cdb->length);
cdb->control = control;
io->io_hdr.io_type = CTL_IO_SCSI;
io->io_hdr.flags = CTL_FLAG_DATA_IN;
io->scsiio.tag_type = tag_type;
io->scsiio.ext_data_ptr = data_ptr;
io->scsiio.ext_data_len = data_len;
io->scsiio.ext_sg_entries = 0;
io->scsiio.ext_data_filled = 0;
io->scsiio.sense_len = SSD_FULL_SIZE;
}
#ifndef _KERNEL
union ctl_io *
ctl_scsi_alloc_io(struct ctl_id initid)
{
union ctl_io *io;
io = (union ctl_io *)malloc(sizeof(*io));
if (io == NULL)
goto bailout;
io->io_hdr.nexus.initid = initid;
bailout:
return (io);
}
void
ctl_scsi_free_io(union ctl_io *io)
{
free(io);
}
#endif /* !_KERNEL */
void
ctl_scsi_zero_io(union ctl_io *io)
{
void *pool_ref;
if (io == NULL)
return;
pool_ref = io->io_hdr.pool;
memset(io, 0, sizeof(*io));
io->io_hdr.pool = pool_ref;
}
const char *
ctl_scsi_task_string(struct ctl_taskio *taskio)
{
unsigned int i;
for (i = 0; i < (sizeof(ctl_task_table)/sizeof(ctl_task_table[0]));
i++) {
if (taskio->task_action == ctl_task_table[i].task_action) {
return (ctl_task_table[i].description);
}
}
return (NULL);
}
void
ctl_io_error_sbuf(union ctl_io *io, struct scsi_inquiry_data *inq_data,
struct sbuf *sb)
{
struct ctl_status_desc *status_desc;
char path_str[64];
unsigned int i;
status_desc = NULL;
for (i = 0; i < (sizeof(ctl_status_table)/sizeof(ctl_status_table[0]));
i++) {
if ((io->io_hdr.status & CTL_STATUS_MASK) ==
ctl_status_table[i].status) {
status_desc = &ctl_status_table[i];
break;
}
}
ctl_scsi_path_string(io, path_str, sizeof(path_str));
switch (io->io_hdr.io_type) {
case CTL_IO_SCSI:
sbuf_cat(sb, path_str);
ctl_scsi_command_string(&io->scsiio, NULL, sb);
sbuf_printf(sb, "\n");
sbuf_printf(sb, "%sTag: 0x%04x, Type: %d\n", path_str,
io->scsiio.tag_num, io->scsiio.tag_type);
break;
case CTL_IO_TASK: {
const char *task_desc;
sbuf_cat(sb, path_str);
task_desc = ctl_scsi_task_string(&io->taskio);
if (task_desc == NULL)
sbuf_printf(sb, "Unknown Task Action %d (%#x)",
io->taskio.task_action,
io->taskio.task_action);
else
sbuf_printf(sb, "Task Action: %s", task_desc);
sbuf_printf(sb, "\n");
switch (io->taskio.task_action) {
case CTL_TASK_ABORT_TASK:
case CTL_TASK_ABORT_TASK_SET:
case CTL_TASK_CLEAR_TASK_SET:
sbuf_printf(sb, "%sTag: 0x%04x, Type: %d\n", path_str,
io->taskio.tag_num,
io->taskio.tag_type);
break;
default:
break;
}
break;
}
default:
break;
}
sbuf_cat(sb, path_str);
if (status_desc == NULL)
sbuf_printf(sb, "CTL Status: Unknown status %#x\n",
io->io_hdr.status);
else
sbuf_printf(sb, "CTL Status: %s\n", status_desc->description);
if ((io->io_hdr.io_type == CTL_IO_SCSI)
&& ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SCSI_ERROR)) {
sbuf_cat(sb, path_str);
sbuf_printf(sb, "SCSI Status: %s\n",
ctl_scsi_status_string(&io->scsiio));
if (io->scsiio.scsi_status == SCSI_STATUS_CHECK_COND)
ctl_scsi_sense_sbuf(&io->scsiio, inq_data,
sb, SSS_FLAG_NONE);
}
}
char *
ctl_io_error_string(union ctl_io *io, struct scsi_inquiry_data *inq_data,
char *str, int str_len)
{
struct sbuf sb;
sbuf_new(&sb, str, str_len, SBUF_FIXEDLEN);
ctl_io_error_sbuf(io, inq_data, &sb);
sbuf_finish(&sb);
return (sbuf_data(&sb));
}
#ifdef _KERNEL
void
ctl_io_error_print(union ctl_io *io, struct scsi_inquiry_data *inq_data)
{
char str[512];
#ifdef NEEDTOPORT
char *message;
char *line;
message = io_error_string(io, inq_data, str, sizeof(str));
for (line = strsep(&message, "\n"); line != NULL;
line = strsep(&message, "\n")) {
csevent_log(CSC_CTL | CSC_SHELF_SW | CTL_ERROR_REPORT,
csevent_LogType_Trace,
csevent_Severity_Information,
csevent_AlertLevel_Green,
csevent_FRU_Firmware,
csevent_FRU_Unknown, "%s", line);
}
#else
printf("%s", ctl_io_error_string(io, inq_data, str, sizeof(str)));
#endif
}
#else /* _KERNEL */
void
ctl_io_error_print(union ctl_io *io, struct scsi_inquiry_data *inq_data,
FILE *ofile)
{
char str[512];
fprintf(ofile, "%s", ctl_io_error_string(io, inq_data, str,
sizeof(str)));
}
#endif /* _KERNEL */
/*
* vim: ts=8
*/

119
sys/cam/ctl/ctl_util.h Normal file
View File

@ -0,0 +1,119 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_util.h#2 $
* $FreeBSD$
*/
/*
* CAM Target Layer SCSI library interface
*
* Author: Ken Merry <ken@FreeBSD.org>
*/
#ifndef _CTL_UTIL_H
#define _CTL_UTIL_H 1
__BEGIN_DECLS
void ctl_scsi_tur(union ctl_io *io, ctl_tag_type tag_type, uint8_t control);
void ctl_scsi_inquiry(union ctl_io *io, uint8_t *data_ptr, int32_t data_len,
uint8_t byte2, uint8_t page_code, ctl_tag_type tag_type,
uint8_t control);
void ctl_scsi_request_sense(union ctl_io *io, uint8_t *data_ptr,
int32_t data_len, uint8_t byte2,
ctl_tag_type tag_type, uint8_t control);
void ctl_scsi_report_luns(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, uint8_t select_report,
ctl_tag_type tag_type, uint8_t control);
void ctl_scsi_read_write_buffer(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, int read_buffer,
uint8_t mode, uint8_t buffer_id,
uint32_t buffer_offset, ctl_tag_type tag_type,
uint8_t control);
void ctl_scsi_read_write(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, int read_op, uint8_t byte2,
int minimum_cdb_size, uint64_t lba,
uint32_t num_blocks, ctl_tag_type tag_type,
uint8_t control);
void ctl_scsi_read_capacity(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, uint32_t addr, int reladr,
int pmi, ctl_tag_type tag_type, uint8_t control);
void ctl_scsi_read_capacity_16(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, uint64_t addr, int reladr,
int pmi, ctl_tag_type tag_type, uint8_t control);
void ctl_scsi_mode_sense(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, int dbd, int llbaa,
uint8_t page_code, uint8_t pc, uint8_t subpage,
int minimum_cdb_size, ctl_tag_type tag_type,
uint8_t control);
void ctl_scsi_start_stop(union ctl_io *io, int start, int load_eject,
int immediate, int power_conditions, int onoffline,
ctl_tag_type tag_type, uint8_t control);
void ctl_scsi_sync_cache(union ctl_io *io, int immed, int reladr,
int minimum_cdb_size, uint64_t starting_lba,
uint32_t block_count, ctl_tag_type tag_type,
uint8_t control);
void ctl_scsi_persistent_res_in(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, int action,
ctl_tag_type tag_type, uint8_t control);
void ctl_scsi_persistent_res_out(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, int action, int type,
uint64_t key, uint64_t sa_key,
ctl_tag_type tag_type, uint8_t control);
void ctl_scsi_maintenance_in(union ctl_io *io, uint8_t *data_ptr,
uint32_t data_len, uint8_t action,
ctl_tag_type tag_type, uint8_t control);
#ifndef _KERNEL
union ctl_io *ctl_scsi_alloc_io(struct ctl_id initid);
void ctl_scsi_free_io(union ctl_io *io);
#endif /* !_KERNEL */
void ctl_scsi_zero_io(union ctl_io *io);
const char *ctl_scsi_task_string(struct ctl_taskio *taskio);
void ctl_io_error_sbuf(union ctl_io *io,
struct scsi_inquiry_data *inq_data, struct sbuf *sb);
char *ctl_io_error_string(union ctl_io *io,
struct scsi_inquiry_data *inq_data, char *str,
int str_len);
#ifdef _KERNEL
void ctl_io_error_print(union ctl_io *io, struct scsi_inquiry_data *inq_data);
#else /* _KERNEL */
void
ctl_io_error_print(union ctl_io *io, struct scsi_inquiry_data *inq_data,
FILE *ofile);
#endif /* _KERNEL */
__END_DECLS
#endif /* _CTL_UTIL_H */
/*
* vim: ts=8
*/

2049
sys/cam/ctl/scsi_ctl.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5057,14 +5057,7 @@ scsi_inquiry(struct ccb_scsiio *csio, u_int32_t retries,
scsi_cmd->byte2 |= SI_EVPD;
scsi_cmd->page_code = page_code;
}
/*
* A 'transfer units' count of 256 is coded as
* zero for all commands with a single byte count
* field.
*/
if (inq_len == 256)
inq_len = 0;
scsi_cmd->length = inq_len;
scsi_ulto2b(inq_len, scsi_cmd->length);
}
void

View File

@ -175,8 +175,7 @@ struct scsi_inquiry
#define SI_EVPD 0x01
#define SI_CMDDT 0x02
u_int8_t page_code;
u_int8_t reserved;
u_int8_t length;
u_int8_t length[2];
u_int8_t control;
};
@ -532,6 +531,55 @@ struct scsi_caching_page {
uint8_t non_cache_seg_size[3];
};
/*
* XXX KDM move this off to a vendor shim.
*/
struct copan_power_subpage {
uint8_t page_code;
#define PWR_PAGE_CODE 0x00
uint8_t subpage;
#define PWR_SUBPAGE_CODE 0x02
uint8_t page_length[2];
uint8_t page_version;
#define PWR_VERSION 0x01
uint8_t total_luns;
uint8_t max_active_luns;
#define PWR_DFLT_MAX_LUNS 0x07
uint8_t reserved[25];
};
/*
* XXX KDM move this off to a vendor shim.
*/
struct copan_aps_subpage {
uint8_t page_code;
#define APS_PAGE_CODE 0x00
uint8_t subpage;
#define APS_SUBPAGE_CODE 0x03
uint8_t page_length[2];
uint8_t page_version;
#define APS_VERSION 0x00
uint8_t lock_active;
#define APS_LOCK_ACTIVE 0x01
#define APS_LOCK_INACTIVE 0x00
uint8_t reserved[26];
};
/*
* XXX KDM move this off to a vendor shim.
*/
struct copan_debugconf_subpage {
uint8_t page_code;
#define DBGCNF_PAGE_CODE 0x00
uint8_t subpage;
#define DBGCNF_SUBPAGE_CODE 0xF0
uint8_t page_length[2];
uint8_t page_version;
#define DBGCNF_VERSION 0x00
uint8_t ctl_time_io_secs[2];
};
struct scsi_info_exceptions_page {
u_int8_t page_code;
#define SIEP_PAGE_SAVABLE 0x80 /* Page is savable */

View File

@ -421,6 +421,56 @@ union disk_pages /* this is the structure copied from osf */
} flexible_disk;
};
/*
* XXX KDM
* Here for CTL compatibility, reconcile this.
*/
struct scsi_format_page {
uint8_t page_code;
uint8_t page_length;
uint8_t tracks_per_zone[2];
uint8_t alt_sectors_per_zone[2];
uint8_t alt_tracks_per_zone[2];
uint8_t alt_tracks_per_lun[2];
uint8_t sectors_per_track[2];
uint8_t bytes_per_sector[2];
uint8_t interleave[2];
uint8_t track_skew[2];
uint8_t cylinder_skew[2];
uint8_t flags;
#define SFP_SSEC 0x80
#define SFP_HSEC 0x40
#define SFP_RMB 0x20
#define SFP_SURF 0x10
uint8_t reserved[3];
};
/*
* XXX KDM
* Here for CTL compatibility, reconcile this.
*/
struct scsi_rigid_disk_page {
uint8_t page_code;
#define SMS_RIGID_DISK_PAGE 0x04
uint8_t page_length;
uint8_t cylinders[3];
uint8_t heads;
uint8_t start_write_precomp[3];
uint8_t start_reduced_current[3];
uint8_t step_rate[2];
uint8_t landing_zone_cylinder[3];
uint8_t rpl;
#define SRDP_RPL_DISABLED 0x00
#define SRDP_RPL_SLAVE 0x01
#define SRDP_RPL_MASTER 0x02
#define SRDP_RPL_MASTER_CONTROL 0x03
uint8_t rotational_offset;
uint8_t reserved1;
uint8_t rotation_rate[2];
uint8_t reserved2[2];
};
struct scsi_da_rw_recovery_page {
u_int8_t page_code;
#define SMS_RW_ERROR_RECOVERY_PAGE 0x01

View File

@ -604,7 +604,7 @@ targbhdone(struct cam_periph *periph, union ccb *done_ccb)
atio->ccb_h.flags |= CAM_DIR_IN;
descr->data = &no_lun_inq_data;
descr->data_resid = MIN(sizeof(no_lun_inq_data),
SCSI_CDB6_LEN(inq->length));
scsi_2btoul(inq->length));
descr->data_increment = descr->data_resid;
descr->timeout = 5 * 1000;
descr->status = SCSI_STATUS_OK;

View File

@ -1297,6 +1297,7 @@ device targ #SCSI Target Mode Code
device targbh #SCSI Target Mode Blackhole Device
device pass #CAM passthrough driver
device sg #Linux SCSI passthrough
device ctl #CAM Target Layer
# CAM OPTIONS:
# debugging options:

View File

@ -115,6 +115,19 @@ cam/scsi/scsi_all.c optional scbus
cam/scsi/scsi_cd.c optional cd
cam/scsi/scsi_ch.c optional ch
cam/ata/ata_da.c optional ada | da
cam/ctl/ctl.c optional ctl
cam/ctl/ctl_backend.c optional ctl
cam/ctl/ctl_backend_block.c optional ctl
cam/ctl/ctl_backend_ramdisk.c optional ctl
cam/ctl/ctl_cmd_table.c optional ctl
cam/ctl/ctl_frontend.c optional ctl
cam/ctl/ctl_frontend_cam_sim.c optional ctl
cam/ctl/ctl_frontend_internal.c optional ctl
cam/ctl/ctl_mem_pool.c optional ctl
cam/ctl/ctl_scsi_all.c optional ctl
cam/ctl/ctl_error.c optional ctl
cam/ctl/ctl_util.c optional ctl
cam/ctl/scsi_ctl.c optional ctl
cam/scsi/scsi_da.c optional da
cam/scsi/scsi_low.c optional ct | ncv | nsp | stg
cam/scsi/scsi_low_pisa.c optional ct | ncv | nsp | stg

View File

@ -576,9 +576,10 @@ atapi_action(struct cam_sim *sim, union ccb *ccb)
struct scsi_inquiry *inq = (struct scsi_inquiry *) &request->u.atapi.ccb[0];
if (inq->byte2 == 0 && inq->page_code == 0 &&
inq->length > SHORT_INQUIRY_LENGTH) {
scsi_2btoul(inq->length) > SHORT_INQUIRY_LENGTH) {
bzero(buf, len);
len = inq->length = SHORT_INQUIRY_LENGTH;
len = SHORT_INQUIRY_LENGTH;
scsi_ulto2b(len, inq->length);
}
break;
}

View File

@ -1614,7 +1614,7 @@ ciss_inquiry_logical(struct ciss_softc *sc, struct ciss_ldrive *ld)
inq->opcode = INQUIRY;
inq->byte2 = SI_EVPD;
inq->page_code = CISS_VPD_LOGICAL_DRIVE_GEOMETRY;
inq->length = sizeof(ld->cl_geometry);
scsi_ulto2b(sizeof(ld->cl_geometry), inq->length);
if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) {
ciss_printf(sc, "error getting geometry (%d)\n", error);

View File

@ -138,7 +138,8 @@ device da # Direct Access (disks)
device sa # Sequential Access (tape etc)
device cd # CD
device pass # Passthrough device (direct ATA/SCSI access)
device ses # SCSI Environmental Services (and SAF-TE)
device ses # Enclosure Services (SES and SAF-TE)
device ctl # CAM Target Layer
# RAID controllers interfaced to the SCSI subsystem
device amr # AMI MegaRAID

View File

@ -23,6 +23,7 @@ device ispfw
# address properly may cause data corruption when used in a machine with more
# than 4 gigabytes of memory.
nodevice ahb
nodevice sym
nodevice trm
@ -37,6 +38,8 @@ nodevice ncv
nodevice nsp
nodevice stg
nodevice ctl
nodevice asr
nodevice dpt
nodevice mly

View File

@ -113,7 +113,8 @@ device ch # Media changer
device da # Direct Access (ie disk)
device pass # Passthrough (direct ATA/SCSI access)
device sa # Sequential Access (ie tape)
device ses # Environmental Services (and SAF-TE)
device ses # Enclosure Services (SES and SAF-TE)
device ctl # CAM Target Layer
# RAID controllers
device aac # Adaptec FSA RAID

View File

@ -119,6 +119,7 @@ device sa # Sequential Access (tape etc)
device cd # CD
device pass # Passthrough device (direct ATA/SCSI access)
device ses # SCSI Environmental Services (and SAF-TE)
device ctl # CAM Target Layer
# RAID controllers
#device amr # AMI MegaRAID

View File

@ -33,6 +33,7 @@ SUBDIR= alias \
compress \
cpuset \
csplit \
ctlstat \
cut \
dirname \
du \

8
usr.bin/ctlstat/Makefile Normal file
View File

@ -0,0 +1,8 @@
# $FreeBSD$
PROG= ctlstat
MAN= ctlstat.8
SDIR= ${.CURDIR}/../../sys
CFLAGS+= -I${SDIR}
.include <bsd.prog.mk>

122
usr.bin/ctlstat/ctlstat.8 Normal file
View File

@ -0,0 +1,122 @@
.\"
.\" Copyright (c) 2010 Silicon Graphics International Corp.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions, and the following disclaimer,
.\" without modification.
.\" 2. Redistributions in binary form must reproduce at minimum a disclaimer
.\" substantially similar to the "NO WARRANTY" disclaimer below
.\" ("Disclaimer") and any redistribution must be conditioned upon
.\" including a substantially similar Disclaimer requirement for further
.\" binary redistribution.
.\"
.\" NO WARRANTY
.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
.\" HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGES.
.\"
.\" ctlstat utility man page.
.\"
.\" Author: Ken Merry <ken@FreeBSD.org>
.\"
.\" $Id: //depot/users/kenm/FreeBSD-test2/usr.bin/ctlstat/ctlstat.8#2 $
.\" $FreeBSD$
.\"
.Dd June 4, 2010
.Dt CTLSTAT 8
.Os
.Sh NAME
.Nm ctlstat
.Nd CAM Target Layer statistics utility
.Sh SYNOPSIS
.Nm
.Op Fl t
.Op Fl c Ar count
.Op Fl C
.Op Fl d
.Op Fl D
.Op Fl j
.Op Fl l Ar lun
.Op Fl n Ar numdevs
.Op Fl w Ar wait
.Sh DESCRIPTION
The
.Nm
utility provides statistics information for the CAM Target Layer.
The first display (except for dump and JSON modes) shows average statistics
since system startup.
Subsequent displays show average statistics during the measurement
interval.
.Pp
The options are as follows:
.Bl -tag -width 10n
.It Fl t
Total mode.
This displays separate columns with the total CTL read and write output,
and a combined total column that also includes non I/O operations.
.It Fl c Ar count
Display statistics this many times.
.It Fl C
Disable display of CPU statistics.
.It Fl d
Display DMA operation time (latency) instead of overall I/O time (latency).
.It Fl D
Text dump mode.
Dump all available statistics every 30 seconds in a text format suitable
for parsing.
No statistics are computed in this mode, only raw numbers are displayed.
.It Fl h
Suppress display of the header.
.It Fl j
JSON dump mode.
Dump all available statistics every 30 seconds in JavaScript Object
Notation (JSON) format.
No statistics are computed in this mode, only raw numbers are displayed.
.It Fl l Ar lun
Request statistics for the specified LUN.
This option is incompatible with total (
.Fl t )
mode.
.It Fl n Ar numdevs
Display statistics for this many devices.
.It Fl w Ar wait
Wait this many seconds in between displays.
If this option is not specified,
.Nm
defaults to a 1 second interval.
.El
.Sh EXAMPLES
.Dl ctlstat -t
.Pp
Display total statistics for the system with a one second interval.
.Pp
.Dl ctlstat -d -l 5 -C
.Pp
Display average DMA time for LUN 5 and omit CPU utilization.
.Pp
.Dl ctlstat -n 7 -w 10
.Pp
Display statistics for the first 7 LUNs, and display average statistics
every 10 seconds.
.Sh SEE ALSO
.Xr cam 3 ,
.Xr cam 4 ,
.Xr xpt 4 ,
.Xr camcontrol 8 ,
.Xr ctladm 8 ,
.Xr iostat 8
.Sh AUTHORS
.An Ken Merry Aq ken@FreeBSD.org
.An Will Andrews Aq will@FreeBSD.org

730
usr.bin/ctlstat/ctlstat.c Normal file
View File

@ -0,0 +1,730 @@
/*-
* Copyright (c) 2004, 2008, 2009 Silicon Graphics International Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/users/kenm/FreeBSD-test2/usr.bin/ctlstat/ctlstat.c#4 $
*/
/*
* CAM Target Layer statistics program
*
* Authors: Ken Merry <ken@FreeBSD.org>, Will Andrews <will@FreeBSD.org>
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/sysctl.h>
#include <sys/resource.h>
#include <sys/queue.h>
#include <sys/callout.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#include <string.h>
#include <errno.h>
#include <err.h>
#include <ctype.h>
#include <bitstring.h>
#include <cam/scsi/scsi_all.h>
#include <cam/ctl/ctl.h>
#include <cam/ctl/ctl_io.h>
#include <cam/ctl/ctl_scsi_all.h>
#include <cam/ctl/ctl_util.h>
#include <cam/ctl/ctl_frontend_internal.h>
#include <cam/ctl/ctl_backend.h>
#include <cam/ctl/ctl_ioctl.h>
/*
* The default amount of space we allocate for LUN storage space. We
* dynamically allocate more if needed.
*/
#define CTL_STAT_NUM_LUNS 30
/*
* The default number of LUN selection bits we allocate. This is large
* because we don't currently increase it if the user specifies a LUN
* number of 1024 or larger.
*/
#define CTL_STAT_LUN_BITS 1024L
static const char *ctlstat_opts = "Cc:Ddhjl:n:tw:";
static const char *ctlstat_usage = "Usage: ctlstat [-CDdjht] [-l lunnum]"
"[-c count] [-n numdevs] [-w wait]\n";
struct ctl_cpu_stats {
uint64_t user;
uint64_t nice;
uint64_t system;
uint64_t intr;
uint64_t idle;
};
typedef enum {
CTLSTAT_MODE_STANDARD,
CTLSTAT_MODE_DUMP,
CTLSTAT_MODE_JSON,
} ctlstat_mode_types;
#define CTLSTAT_FLAG_CPU (1 << 0)
#define CTLSTAT_FLAG_HEADER (1 << 1)
#define CTLSTAT_FLAG_FIRST_RUN (1 << 2)
#define CTLSTAT_FLAG_TOTALS (1 << 3)
#define CTLSTAT_FLAG_DMA_TIME (1 << 4)
#define CTLSTAT_FLAG_LUN_TIME_VALID (1 << 5)
#define F_CPU(ctx) ((ctx)->flags & CTLSTAT_FLAG_CPU)
#define F_HDR(ctx) ((ctx)->flags & CTLSTAT_FLAG_HEADER)
#define F_FIRST(ctx) ((ctx)->flags & CTLSTAT_FLAG_FIRST_RUN)
#define F_TOTALS(ctx) ((ctx)->flags & CTLSTAT_FLAG_TOTALS)
#define F_DMA(ctx) ((ctx)->flags & CTLSTAT_FLAG_DMA_TIME)
#define F_LUNVAL(ctx) ((ctx)->flags & CTLSTAT_FLAG_LUN_TIME_VALID)
struct ctlstat_context {
ctlstat_mode_types mode;
int flags;
struct ctl_lun_io_stats *cur_lun_stats, *prev_lun_stats,
*tmp_lun_stats;
struct ctl_lun_io_stats cur_total_stats[3], prev_total_stats[3];
struct timespec cur_time, prev_time;
struct ctl_cpu_stats cur_cpu, prev_cpu;
uint64_t cur_total_jiffies, prev_total_jiffies;
uint64_t cur_idle, prev_idle;
bitstr_t bit_decl(lun_mask, CTL_STAT_LUN_BITS);
int num_luns;
int numdevs;
int header_interval;
};
#ifndef min
#define min(x,y) (((x) < (y)) ? (x) : (y))
#endif
static void usage(int error);
static int getstats(int fd, int *num_luns, struct ctl_lun_io_stats **xlun_stats,
struct timespec *cur_time, int *lun_time_valid);
static int getcpu(struct ctl_cpu_stats *cpu_stats);
static void compute_stats(struct ctl_lun_io_stats *cur_stats,
struct ctl_lun_io_stats *prev_stats,
long double etime, long double *mbsec,
long double *kb_per_transfer,
long double *transfers_per_second,
long double *ms_per_transfer,
long double *ms_per_dma,
long double *dmas_per_second);
static void
usage(int error)
{
fprintf(error ? stderr : stdout, ctlstat_usage);
}
static int
getstats(int fd, int *num_luns, struct ctl_lun_io_stats **xlun_stats,
struct timespec *cur_time, int *flags)
{
struct ctl_lun_io_stats *lun_stats;
struct ctl_stats stats;
int more_space_count;
more_space_count = 0;
if (*num_luns == 0)
*num_luns = CTL_STAT_NUM_LUNS;
lun_stats = *xlun_stats;
retry:
if (lun_stats == NULL) {
lun_stats = (struct ctl_lun_io_stats *)malloc(
sizeof(*lun_stats) * *num_luns);
}
memset(&stats, 0, sizeof(stats));
stats.alloc_len = *num_luns * sizeof(*lun_stats);
memset(lun_stats, 0, stats.alloc_len);
stats.lun_stats = lun_stats;
if (ioctl(fd, CTL_GETSTATS, &stats) == -1)
err(1, "error returned from CTL_GETSTATS ioctl");
switch (stats.status) {
case CTL_SS_OK:
break;
case CTL_SS_ERROR:
err(1, "CTL_SS_ERROR returned from CTL_GETSTATS ioctl");
break;
case CTL_SS_NEED_MORE_SPACE:
if (more_space_count > 0) {
errx(1, "CTL_GETSTATS returned NEED_MORE_SPACE again");
}
*num_luns = stats.num_luns;
free(lun_stats);
lun_stats = NULL;
more_space_count++;
goto retry;
break; /* NOTREACHED */
default:
errx(1, "unknown status %d returned from CTL_GETSTATS ioctl",
stats.status);
break;
}
*xlun_stats = lun_stats;
*num_luns = stats.num_luns;
cur_time->tv_sec = stats.timestamp.tv_sec;
cur_time->tv_nsec = stats.timestamp.tv_nsec;
if (stats.flags & CTL_STATS_FLAG_TIME_VALID)
*flags |= CTLSTAT_FLAG_LUN_TIME_VALID;
else
*flags &= ~CTLSTAT_FLAG_LUN_TIME_VALID;
return (0);
}
static int
getcpu(struct ctl_cpu_stats *cpu_stats)
{
long cp_time[CPUSTATES];
size_t cplen;
cplen = sizeof(cp_time);
if (sysctlbyname("kern.cp_time", &cp_time, &cplen, NULL, 0) == -1) {
warn("sysctlbyname(kern.cp_time...) failed");
return (1);
}
cpu_stats->user = cp_time[CP_USER];
cpu_stats->nice = cp_time[CP_NICE];
cpu_stats->system = cp_time[CP_SYS];
cpu_stats->intr = cp_time[CP_INTR];
cpu_stats->idle = cp_time[CP_IDLE];
return (0);
}
static void
compute_stats(struct ctl_lun_io_stats *cur_stats,
struct ctl_lun_io_stats *prev_stats, long double etime,
long double *mbsec, long double *kb_per_transfer,
long double *transfers_per_second, long double *ms_per_transfer,
long double *ms_per_dma, long double *dmas_per_second)
{
uint64_t total_bytes = 0, total_operations = 0, total_dmas = 0;
uint32_t port;
struct bintime total_time_bt, total_dma_bt;
struct timespec total_time_ts, total_dma_ts;
int i;
bzero(&total_time_bt, sizeof(total_time_bt));
bzero(&total_dma_bt, sizeof(total_dma_bt));
bzero(&total_time_ts, sizeof(total_time_ts));
bzero(&total_dma_ts, sizeof(total_dma_ts));
for (port = 0; port < CTL_MAX_PORTS; port++) {
for (i = 0; i < CTL_STATS_NUM_TYPES; i++) {
total_bytes += cur_stats->ports[port].bytes[i];
total_operations +=
cur_stats->ports[port].operations[i];
total_dmas += cur_stats->ports[port].num_dmas[i];
bintime_add(&total_time_bt,
&cur_stats->ports[port].time[i]);
bintime_add(&total_dma_bt,
&cur_stats->ports[port].dma_time[i]);
if (prev_stats != NULL) {
total_bytes -=
prev_stats->ports[port].bytes[i];
total_operations -=
prev_stats->ports[port].operations[i];
total_dmas -=
prev_stats->ports[port].num_dmas[i];
bintime_sub(&total_time_bt,
&prev_stats->ports[port].time[i]);
bintime_sub(&total_dma_bt,
&prev_stats->ports[port].dma_time[i]);
}
}
}
*mbsec = total_bytes;
*mbsec /= 1024 * 1024;
if (etime > 0.0)
*mbsec /= etime;
else
*mbsec = 0;
*kb_per_transfer = total_bytes;
*kb_per_transfer /= 1024;
if (total_operations > 0)
*kb_per_transfer /= total_operations;
else
*kb_per_transfer = 0;
*transfers_per_second = total_operations;
*dmas_per_second = total_dmas;
if (etime > 0.0) {
*transfers_per_second /= etime;
*dmas_per_second /= etime;
} else {
*transfers_per_second = 0;
*dmas_per_second = 0;
}
bintime2timespec(&total_time_bt, &total_time_ts);
bintime2timespec(&total_dma_bt, &total_dma_ts);
if (total_operations > 0) {
/*
* Convert the timespec to milliseconds.
*/
*ms_per_transfer = total_time_ts.tv_sec * 1000;
*ms_per_transfer += total_time_ts.tv_nsec / 1000000;
*ms_per_transfer /= total_operations;
} else
*ms_per_transfer = 0;
if (total_dmas > 0) {
/*
* Convert the timespec to milliseconds.
*/
*ms_per_dma = total_dma_ts.tv_sec * 1000;
*ms_per_dma += total_dma_ts.tv_nsec / 1000000;
*ms_per_dma /= total_dmas;
} else
*ms_per_dma = 0;
}
/* The dump_stats() and json_stats() functions perform essentially the same
* purpose, but dump the statistics in different formats. JSON is more
* conducive to programming, however.
*/
#define PRINT_BINTIME(prefix, bt) \
printf("%s %jd s %ju frac\n", prefix, (intmax_t)(bt).sec, \
(uintmax_t)(bt).frac)
const char *iotypes[] = {"NO IO", "READ", "WRITE"};
static void
ctlstat_dump(struct ctlstat_context *ctx) {
int iotype, lun, port;
struct ctl_lun_io_stats *stats = ctx->cur_lun_stats;
for (lun = 0; lun < ctx->num_luns;lun++) {
printf("lun %d\n", lun);
for (port = 0; port < CTL_MAX_PORTS; port++) {
printf(" port %d\n",
stats[lun].ports[port].targ_port);
for (iotype = 0; iotype < CTL_STATS_NUM_TYPES;
iotype++) {
printf(" io type %d (%s)\n", iotype,
iotypes[iotype]);
printf(" bytes %ju\n", (uintmax_t)
stats[lun].ports[port].bytes[iotype]);
printf(" operations %ju\n", (uintmax_t)
stats[lun].ports[port].operations[iotype]);
PRINT_BINTIME(" io time",
stats[lun].ports[port].time[iotype]);
printf(" num dmas %ju\n", (uintmax_t)
stats[lun].ports[port].num_dmas[iotype]);
PRINT_BINTIME(" dma time",
stats[lun].ports[port].dma_time[iotype]);
}
}
}
}
#define JSON_BINTIME(prefix, bt) \
printf("\"%s\":{\"sec\":%jd,\"frac\":%ju},", \
prefix, (intmax_t)(bt).sec, (uintmax_t)(bt).frac)
static void
ctlstat_json(struct ctlstat_context *ctx) {
int iotype, lun, port;
struct ctl_lun_io_stats *stats = ctx->cur_lun_stats;
printf("{\"luns\":[");
for (lun = 0; lun < ctx->num_luns; lun++) {
printf("{\"ports\":[");
for (port = 0; port < CTL_MAX_PORTS;port++) {
printf("{\"num\":%d,\"io\":[",
stats[lun].ports[port].targ_port);
for (iotype = 0; iotype < CTL_STATS_NUM_TYPES;
iotype++) {
printf("{\"type\":\"%s\",", iotypes[iotype]);
printf("\"bytes\":%ju,", (uintmax_t)stats[
lun].ports[port].bytes[iotype]);
printf("\"operations\":%ju,", (uintmax_t)stats[
lun].ports[port].operations[iotype]);
JSON_BINTIME("io time",
stats[lun].ports[port].time[iotype]);
JSON_BINTIME("dma time",
stats[lun].ports[port].dma_time[iotype]);
printf("\"num dmas\":%ju}", (uintmax_t)
stats[lun].ports[port].num_dmas[iotype]);
if (iotype < (CTL_STATS_NUM_TYPES - 1))
printf(","); /* continue io array */
}
printf("]}"); /* close port */
if (port < (CTL_MAX_PORTS - 1))
printf(","); /* continue port array */
}
printf("]}"); /* close lun */
if (lun < (ctx->num_luns - 1))
printf(","); /* continue lun array */
}
printf("]}"); /* close luns and toplevel */
}
static void
ctlstat_standard(struct ctlstat_context *ctx) {
long double cur_secs, prev_secs, etime;
uint64_t delta_jiffies, delta_idle;
uint32_t port;
long double cpu_percentage;
int i;
int j;
cpu_percentage = 0;
if (F_CPU(ctx) && (getcpu(&ctx->cur_cpu) != 0))
errx(1, "error returned from getcpu()");
cur_secs = ctx->cur_time.tv_sec + (ctx->cur_time.tv_nsec / 1000000000);
prev_secs = ctx->prev_time.tv_sec +
(ctx->prev_time.tv_nsec / 1000000000);
etime = cur_secs - prev_secs;
if (F_CPU(ctx)) {
ctx->prev_total_jiffies = ctx->cur_total_jiffies;
ctx->cur_total_jiffies = ctx->cur_cpu.user +
ctx->cur_cpu.nice + ctx->cur_cpu.system +
ctx->cur_cpu.intr + ctx->cur_cpu.idle;
delta_jiffies = ctx->cur_total_jiffies;
if (F_FIRST(ctx) == 0)
delta_jiffies -= ctx->prev_total_jiffies;
ctx->prev_idle = ctx->cur_idle;
ctx->cur_idle = ctx->cur_cpu.idle;
delta_idle = ctx->cur_idle - ctx->prev_idle;
cpu_percentage = delta_jiffies - delta_idle;
cpu_percentage /= delta_jiffies;
cpu_percentage *= 100;
}
if (F_HDR(ctx)) {
ctx->header_interval--;
if (ctx->header_interval <= 0) {
int hdr_devs;
hdr_devs = 0;
if (F_TOTALS(ctx)) {
fprintf(stdout, "%s System Read %s"
"System Write %sSystem Total%s\n",
(F_LUNVAL(ctx) != 0) ? " " : "",
(F_LUNVAL(ctx) != 0) ? " " : "",
(F_LUNVAL(ctx) != 0) ? " " : "",
(F_CPU(ctx) == 0) ? " CPU" : "");
hdr_devs = 3;
} else {
if (F_CPU(ctx))
fprintf(stdout, " CPU ");
for (i = 0; i < min(CTL_STAT_LUN_BITS,
ctx->num_luns); i++) {
int lun;
/*
* Obviously this won't work with
* LUN numbers greater than a signed
* integer.
*/
lun = (int)ctx->cur_lun_stats[i
].lun_number;
if (bit_test(ctx->lun_mask, lun) == 0)
continue;
fprintf(stdout, "%15.6s%d ",
"lun", lun);
hdr_devs++;
}
fprintf(stdout, "\n");
}
for (i = 0; i < hdr_devs; i++)
fprintf(stdout, "%s %sKB/t %s MB/s ",
((F_CPU(ctx) != 0) && (i == 0) &&
(F_TOTALS(ctx) == 0)) ? " " : "",
(F_LUNVAL(ctx) != 0) ? " ms " : "",
(F_DMA(ctx) == 0) ? "tps" : "dps");
fprintf(stdout, "\n");
ctx->header_interval = 20;
}
}
if (F_TOTALS(ctx) != 0) {
long double mbsec[3];
long double kb_per_transfer[3];
long double transfers_per_sec[3];
long double ms_per_transfer[3];
long double ms_per_dma[3];
long double dmas_per_sec[3];
for (i = 0; i < 3; i++)
ctx->prev_total_stats[i] = ctx->cur_total_stats[i];
memset(&ctx->cur_total_stats, 0, sizeof(ctx->cur_total_stats));
/* Use macros to make the next loop more readable. */
#define ADD_STATS_BYTES(st, p, i, j) \
ctx->cur_total_stats[st].ports[p].bytes[j] += \
ctx->cur_lun_stats[i].ports[p].bytes[j]
#define ADD_STATS_OPERATIONS(st, p, i, j) \
ctx->cur_total_stats[st].ports[p].operations[j] += \
ctx->cur_lun_stats[i].ports[p].operations[j]
#define ADD_STATS_NUM_DMAS(st, p, i, j) \
ctx->cur_total_stats[st].ports[p].num_dmas[j] += \
ctx->cur_lun_stats[i].ports[p].num_dmas[j]
#define ADD_STATS_TIME(st, p, i, j) \
bintime_add(&ctx->cur_total_stats[st].ports[p].time[j], \
&ctx->cur_lun_stats[i].ports[p].time[j])
#define ADD_STATS_DMA_TIME(st, p, i, j) \
bintime_add(&ctx->cur_total_stats[st].ports[p].dma_time[j], \
&ctx->cur_lun_stats[i].ports[p].dma_time[j])
for (i = 0; i < ctx->num_luns; i++) {
for (port = 0; port < CTL_MAX_PORTS; port++) {
for (j = 0; j < CTL_STATS_NUM_TYPES; j++) {
ADD_STATS_BYTES(2, port, i, j);
ADD_STATS_OPERATIONS(2, port, i, j);
ADD_STATS_NUM_DMAS(2, port, i, j);
ADD_STATS_TIME(2, port, i, j);
ADD_STATS_DMA_TIME(2, port, i, j);
}
ADD_STATS_BYTES(0, port, i, CTL_STATS_READ);
ADD_STATS_OPERATIONS(0, port, i,
CTL_STATS_READ);
ADD_STATS_NUM_DMAS(0, port, i, CTL_STATS_READ);
ADD_STATS_TIME(0, port, i, CTL_STATS_READ);
ADD_STATS_DMA_TIME(0, port, i, CTL_STATS_READ);
ADD_STATS_BYTES(1, port, i, CTL_STATS_WRITE);
ADD_STATS_OPERATIONS(1, port, i,
CTL_STATS_WRITE);
ADD_STATS_NUM_DMAS(1, port, i, CTL_STATS_WRITE);
ADD_STATS_TIME(1, port, i, CTL_STATS_WRITE);
ADD_STATS_DMA_TIME(1, port, i, CTL_STATS_WRITE);
}
}
for (i = 0; i < 3; i++) {
compute_stats(&ctx->cur_total_stats[i],
F_FIRST(ctx) ? NULL : &ctx->prev_total_stats[i],
etime, &mbsec[i], &kb_per_transfer[i],
&transfers_per_sec[i],
&ms_per_transfer[i], &ms_per_dma[i],
&dmas_per_sec[i]);
if (F_DMA(ctx) != 0)
fprintf(stdout, " %2.2Lf",
ms_per_dma[i]);
else if (F_LUNVAL(ctx) != 0)
fprintf(stdout, " %2.2Lf",
ms_per_transfer[i]);
fprintf(stdout, " %5.2Lf %3.0Lf %5.2Lf ",
kb_per_transfer[i],
(F_DMA(ctx) == 0) ? transfers_per_sec[i] :
dmas_per_sec[i], mbsec[i]);
}
if (F_CPU(ctx))
fprintf(stdout, " %5.1Lf%%", cpu_percentage);
} else {
if (F_CPU(ctx))
fprintf(stdout, "%5.1Lf%% ", cpu_percentage);
for (i = 0; i < min(CTL_STAT_LUN_BITS, ctx->num_luns); i++) {
long double mbsec, kb_per_transfer;
long double transfers_per_sec;
long double ms_per_transfer;
long double ms_per_dma;
long double dmas_per_sec;
if (bit_test(ctx->lun_mask,
(int)ctx->cur_lun_stats[i].lun_number) == 0)
continue;
compute_stats(&ctx->cur_lun_stats[i], F_FIRST(ctx) ?
NULL : &ctx->prev_lun_stats[i], etime,
&mbsec, &kb_per_transfer,
&transfers_per_sec, &ms_per_transfer,
&ms_per_dma, &dmas_per_sec);
if (F_DMA(ctx))
fprintf(stdout, " %2.2Lf",
ms_per_dma);
else if (F_LUNVAL(ctx) != 0)
fprintf(stdout, " %2.2Lf",
ms_per_transfer);
fprintf(stdout, " %5.2Lf %3.0Lf %5.2Lf ",
kb_per_transfer, (F_DMA(ctx) == 0) ?
transfers_per_sec : dmas_per_sec, mbsec);
}
}
}
int
main(int argc, char **argv)
{
int c;
int count, waittime;
int set_lun;
int fd, retval;
struct ctlstat_context ctx;
/* default values */
retval = 0;
waittime = 1;
count = -1;
memset(&ctx, 0, sizeof(ctx));
ctx.numdevs = 3;
ctx.mode = CTLSTAT_MODE_STANDARD;
ctx.flags |= CTLSTAT_FLAG_CPU;
ctx.flags |= CTLSTAT_FLAG_FIRST_RUN;
ctx.flags |= CTLSTAT_FLAG_HEADER;
while ((c = getopt(argc, argv, ctlstat_opts)) != -1) {
switch (c) {
case 'C':
ctx.flags &= ~CTLSTAT_FLAG_CPU;
break;
case 'c':
count = atoi(optarg);
break;
case 'd':
ctx.flags |= CTLSTAT_FLAG_DMA_TIME;
break;
case 'D':
ctx.mode = CTLSTAT_MODE_DUMP;
waittime = 30;
break;
case 'h':
ctx.flags &= ~CTLSTAT_FLAG_HEADER;
break;
case 'j':
ctx.mode = CTLSTAT_MODE_JSON;
waittime = 30;
break;
case 'l': {
int cur_lun;
cur_lun = atoi(optarg);
if (cur_lun > CTL_STAT_LUN_BITS)
errx(1, "Invalid LUN number %d", cur_lun);
bit_ffs(ctx.lun_mask, CTL_STAT_LUN_BITS, &set_lun);
if (set_lun == -1)
ctx.numdevs = 1;
else
ctx.numdevs++;
bit_set(ctx.lun_mask, cur_lun);
break;
}
case 'n':
ctx.numdevs = atoi(optarg);
break;
case 't':
ctx.flags |= CTLSTAT_FLAG_TOTALS;
ctx.numdevs = 3;
break;
case 'w':
waittime = atoi(optarg);
break;
default:
retval = 1;
usage(retval);
exit(retval);
break;
}
}
bit_ffs(ctx.lun_mask, CTL_STAT_LUN_BITS, &set_lun);
if ((F_TOTALS(&ctx))
&& (set_lun != -1)) {
errx(1, "Total Mode (-t) is incompatible with individual "
"LUN mode (-l)");
} else if (set_lun == -1) {
/*
* Note that this just selects the first N LUNs to display,
* but at this point we have no knoweledge of which LUN
* numbers actually exist. So we may select LUNs that
* aren't there.
*/
bit_nset(ctx.lun_mask, 0, min(ctx.numdevs - 1,
CTL_STAT_LUN_BITS - 1));
}
if ((fd = open(CTL_DEFAULT_DEV, O_RDWR)) == -1)
err(1, "cannot open %s", CTL_DEFAULT_DEV);
for (;count != 0;) {
ctx.tmp_lun_stats = ctx.prev_lun_stats;
ctx.prev_lun_stats = ctx.cur_lun_stats;
ctx.cur_lun_stats = ctx.tmp_lun_stats;
ctx.prev_time = ctx.cur_time;
ctx.prev_cpu = ctx.cur_cpu;
if (getstats(fd, &ctx.num_luns, &ctx.cur_lun_stats,
&ctx.cur_time, &ctx.flags) != 0)
errx(1, "error returned from getstats()");
switch(ctx.mode) {
case CTLSTAT_MODE_STANDARD:
ctlstat_standard(&ctx);
break;
case CTLSTAT_MODE_DUMP:
ctlstat_dump(&ctx);
break;
case CTLSTAT_MODE_JSON:
ctlstat_json(&ctx);
break;
default:
break;
}
fprintf(stdout, "\n");
ctx.flags &= ~CTLSTAT_FLAG_FIRST_RUN;
if (count != 1)
sleep(waittime);
if (count > 0)
count--;
}
exit (retval);
}
/*
* vim: ts=8
*/

View File

@ -16,6 +16,7 @@ SUBDIR= adduser \
clear_locks \
crashinfo \
cron \
ctladm \
daemon \
dconschat \
devinfo \

21
usr.sbin/ctladm/Makefile Normal file
View File

@ -0,0 +1,21 @@
# $FreeBSD$
PROG= ctladm
SRCS= ctladm.c util.c ctl_util.c ctl_scsi_all.c
.PATH: ${.CURDIR}/../../sys/cam/ctl
SDIR= ${.CURDIR}/../../sys
CFLAGS+= -I${SDIR}
# This is necessary because of these warnings:
# warning: cast increases required alignment of target type
# The solution is to either upgrade the compiler (preferred), or do void
# pointer gymnastics to get around the warning. For now, disable the
# warning instead of doing the void pointer workaround.
.if ${MACHINE_CPUARCH} == "arm"
WARNS?= 3
.endif
DPADD= ${LIBCAM} ${LIBSBUF}
LDADD= -lcam -lsbuf -lbsdxml
MAN= ctladm.8
.include <bsd.prog.mk>

963
usr.sbin/ctladm/ctladm.8 Normal file
View File

@ -0,0 +1,963 @@
.\"
.\" Copyright (c) 2003 Silicon Graphics International Corp.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions, and the following disclaimer,
.\" without modification.
.\" 2. Redistributions in binary form must reproduce at minimum a disclaimer
.\" substantially similar to the "NO WARRANTY" disclaimer below
.\" ("Disclaimer") and any redistribution must be conditioned upon
.\" including a substantially similar Disclaimer requirement for further
.\" binary redistribution.
.\"
.\" NO WARRANTY
.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
.\" HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGES.
.\"
.\" ctladm utility man page.
.\"
.\" Author: Ken Merry <ken@FreeBSD.org>
.\"
.\" $Id: //depot/users/kenm/FreeBSD-test2/usr.sbin/ctladm/ctladm.8#3 $
.\" $FreeBSD$
.\"
.Dd July 8, 2011
.Dt CTLADM 8
.Os
.Sh NAME
.Nm ctladm
.Nd CAM Target Layer control utility
.Sh SYNOPSIS
.Nm
.Aq Ar command
.Op target:lun
.Op generic args
.Op command args
.Nm
.Ic tur
.Aq target:lun
.Op general options
.Nm
.Ic inquiry
.Aq target:lun
.Op general options
.Nm
.Ic reqsense
.Aq target:lun
.Op general options
.Nm
.Ic reportluns
.Aq target:lun
.Op general options
.Nm
.Ic read
.Aq target:lun
.Op general options
.Aq Fl l Ar lba
.Aq Fl d Ar datalen
.Aq Fl f Ar file|-
.Aq Fl b Ar blocksize_bytes
.Op Fl c Ar cdbsize
.Op Fl N
.Nm
.Ic write
.Aq target:lun
.Op general options
.Aq Fl l Ar lba
.Aq Fl d Ar datalen
.Aq Fl f Ar file|-
.Aq Fl b Ar blocksize_bytes
.Op Fl c Ar cdbsize
.Op Fl N
.Nm
.Ic bbrread
.Aq target:lun
.Op general options
.Aq Fl -l Ar lba
.Aq Fl -d Ar datalen
.Nm
.Ic readcap
.Aq target:lun
.Op general options
.Op Fl c Ar cdbsize
.Nm
.Ic modesense
.Aq target:lun
.Aq Fl m Ar page | Fl l
.Op Fl P Ar pc
.Op Fl d
.Op Fl S Ar subpage
.Op Fl c Ar size
.Nm
.Ic start
.Aq target:lun
.Op general options
.Op Fl i
.Op Fl o
.Nm
.Ic stop
.Aq target:lun
.Op general options
.Op Fl i
.Op Fl o
.Nm
.Ic synccache
.Aq target:lun
.Op general options
.Op Fl l Ar lba
.Op Fl b Ar blockcount
.Op Fl r
.Op Fl i
.Op Fl c Ar cdbsize
.Nm
.Ic shutdown
.Op general options
.Nm
.Ic startup
.Op general options
.Nm
.Ic hardstop
.Nm
.Ic hardstart
.Nm
.Ic lunlist
.Nm
.Ic delay
.Aq target:lun
.Aq Fl l Ar datamove|done
.Aq Fl t Ar secs
.Op Fl T Ar oneshot|cont
.Nm
.Ic realsync Aq on|off|query
.Nm
.Ic setsync interval
.Aq target:lun
.Aq Fl i Ar interval
.Nm
.Ic getsync
.Aq target:lun
.Nm
.Ic inject
.Aq Fl i Ar action
.Aq Fl p Ar pattern
.Op Fl r Ar lba,len
.Op Fl s Ar len fmt Op Ar args
.Op Fl c
.Op Fl d Ar delete_id
.Nm
.Ic create
.Aq Fl b Ar backend
.Op Fl B Ar blocksize
.Op Fl d Ar device_id
.Op Fl l Ar lun_id
.Op Fl o Ar name=value
.Op Fl s Ar size_bytes
.Op Fl S Ar serial_num
.Op Fl t Ar device_type
.Nm
.Ic remove
.Aq Fl b Ar backend
.Aq Fl l Ar lun_id
.Op Fl o Ar name=value
.Nm
.Ic devlist
.Op Fl b Ar backend
.Op Fl v
.Op Fl x
.Nm
.Ic port
.Op Fl l
.Op Fl o Ar on|off
.Op Fl w Ar wwpn
.Op Fl W Ar wwnn
.Op Fl p Ar targ_port
.Op Fl t Ar fe_type
.Op Fl q
.Op Fl x
.Nm
.Ic dumpooa
.Nm
.Ic dumpstructs
.Nm
.Ic help
.Sh DESCRIPTION
The
.Nm
utility is designed to provide a way to access and control the CAM Target
Layer (CTL).
It provides a way to send
.Tn SCSI
commands to the CTL layer, and also provides
some meta-commands that utilize
.Tn SCSI
commands.
(For instance, the
.Ic lunlist
command is implemented using the
.Tn SCSI
REPORT LUNS and INQUIRY commands.)
.Pp
The
.Nm
utility has a number of primary functions, many of which require a device
identifier.
The device identifier takes the following form:
.Bl -tag -width 14n
.It target:lun
Specify the target (almost always 0) and LUN number to operate on.
.El
Many of the primary functions of the
.Nm
utility take the following optional arguments:
.Pp
.Bl -tag -width 10n
.It Fl C Ar retries
Specify the number of times to retry a command in the event of failure.
.It Fl D Ar device
Specify the device to open. This allows opening a device other than the
default device,
.Pa /dev/cam/ctl ,
to be opened for sending commands.
.It Fl I Ar id
Specify the initiator number to use.
By default,
.Nm
will use 7 as the initiator number.
.El
.Pp
Primary commands:
.Bl -tag -width 11n
.It Ic tur
Send the
.Tn SCSI
TEST UNIT READY command to the device and report whether or not it is
ready.
.It Ic inquiry
Send the
.Tn SCSI
INQUIRY command to the device and display some of the returned inquiry
data.
.It Ic reqsense
Send the
.Tn SCSI
REQUEST SENSE command to the device and display the returned sense
information.
.It Ic reportluns
Send the
.Tn SCSI
REPORT LUNS command to the device and display supported LUNs.
.It Ic read
Send a
.Tn SCSI
READ command to the device, and write the requested data to a file or
stdout.
.Bl -tag -width 12n
.It Fl l Ar lba
Specify the starting Logical Block Address for the READ. This can be
specified in decimal, octal (starting with 0), hexadecimal (starting with
0x) or any other base supported by
.Xr strtoull 3 .
.It Fl d Ar datalen
Specify the length, in 512 byte blocks, of the READ request.
.It Fl f Ar file
Specify the destination for the data read by the READ command. Either a
filename or
.Sq -
for stdout may be specified.
.It Fl c Ar cdbsize
Specify the minimum
.Tn SCSI
CDB (Command Data Block) size to be used for the READ request. Allowable
values are 6, 10, 12 and 16. Depending upon the LBA and amount of data
requested, a larger CDB size may be used to satisfy the request. (e.g.,
for LBAs above 0xffffffff, READ(16) must be used to satisfy the request.)
.It Fl b Ar blocksize
Specify the blocksize of the underlying
.Tn SCSI
device, so the transfer length
can be calculated accurately. The blocksize can be obtained via the
.Tn SCSI
READ CAPACITY command.
.It Fl N
Do not copy data to
.Nm
from the kernel when doing a read, just execute the command without copying
data.
This is to be used for performance testing.
.El
.It Ic write
Read data from a file or stdin, and write the data to the device using the
.Tn SCSI
WRITE command.
.Bl -tag -width 12n
.It Fl l Ar lba
Specify the starting Logical Block Address for the WRITE. This can be
specified in decimal, octal (starting with 0), hexadecimal (starting with
0x) or any other base supported by
.Xr strtoull 3 .
.It Fl d Ar atalen
Specify the length, in 512 byte blocks, of the WRITE request.
.It Fl f Ar file
Specify the source for the data to be written by the WRITE command. Either a
filename or
.Sq -
for stdin may be specified.
.It Fl c Ar cdbsize
Specify the minimum
.Tn SCSI
CDB (Command Data Block) size to be used for the READ request. Allowable
values are 6, 10, 12 and 16. Depending upon the LBA and amount of data
requested, a larger CDB size may be used to satisfy the request. (e.g.,
for LBAs above 0xffffffff, READ(16) must be used to satisfy the request.)
.It Fl b Ar blocksize
Specify the blocksize of the underlying
.Tn SCSI
device, so the transfer length
can be calculated accurately. The blocksize can be obtained via the
.Tn SCSI
READ CAPACITY command.
.It Fl N
Do not copy data to
.Nm
to the kernel when doing a write, just execute the command without copying
data.
This is to be used for performance testing.
.El
.It Ic bbrread
Issue a SCSI READ command to the logical device to potentially force a bad
block on a disk in the RAID set to be reconstructed from the other disks in
the array. This command should only be used on an array that is in the
normal state. If used on a critical array, it could cause the array to go
offline if the bad block to be remapped is on one of the disks that is
still active in the array.
.Pp
The data for this particular command will be discarded, and not returned to
the user.
.Pp
In order to determine which LUN to read from, the user should first
determine which LUN the disk with a bad block belongs to. Then he should
map the bad disk block back to the logical block address for the array in
order to determine which LBA to pass in to the
.Ic bbrread
command.
.Pp
This command is primarily intended for testing. In practice, bad block
remapping will generally be triggered by the in-kernel Disk Aerobics and
Disk Scrubbing code.
.Bl -tag -width 10n
.It Fl l Ar lba
Specify the starting Logical Block Address.
.It Fl d Ar datalen
Specify the amount of data in bytes to read from the LUN. This must be a
multiple of the LUN blocksize.
.El
.It Ic readcap
Send the
.Tn SCSI
READ CAPACITY command to the device and display the device size and device
block size. By default, READ CAPACITY(10) is
used. If the device returns a maximum LBA of 0xffffffff, however,
.Nm
will automatically issue a READ CAPACITY(16), which is implemented as a
service action of the SERVICE ACTION IN(16) opcode. The user can specify
the minimum CDB size with the
.Fl c
argument. Valid values for the
.Fl c
option are 10 and 16. If a 10 byte CDB is specified, the request will be
automatically reissued with a 16 byte CDB if the maximum LBA returned is
0xffffffff.
.It Ic modesense
Send a
.Tn SCSI
MODE SENSE command to the device, and display the requested mode page(s) or
page list.
.Bl -tag -width 10n
.It Fl m Ar page
Specify the mode page to display. This option and the
.Fl l
option are mutually exclusive. One of the two must be specified, though.
Mode page numbers may be specified in decimal or hexadecimal.
.It Fl l
Request that the list of mode pages supported by the device be returned.
This option and the
.Fl m
option are mutually exclusive. One of the two must be specified, though.
.It Fl P Ar pc
Specify the mode page page control value. Possible values are:
.Bl -tag -width 2n -compact
.It 0
Current values.
.It 1
Changeable value bitmask.
.It 2
Default values.
.It 3
Saved values.
.El
.It Fl d
Disable block descriptors when sending the mode sense request.
.It Fl S Ar subpage
Specify the subpage used with the mode sense request.
.It Fl c Ar cdbsize
Specify the CDB size used for the mode sense request. Supported values are
6 and 10.
.El
.It Ic start
Send the
.Tn SCSI
START STOP UNIT command to the specified LUN with the start
bit set.
.Bl -tag -width 4n
.It Fl i
Set the immediate bit in the CDB. Note that CTL does not support the
immediate bit, so this is primarily useful for making sure that CTL returns
the proper error.
.It Fl o
Set the Copan proprietary on/offline bit in the CDB. When this flag is
used, the LUN will be marked online again (see the description of the
.Ic shutdown
and
.Ic startup
commands). When this flag is used with a
start command, the LUN will NOT be spun up. You need to use a start
command without the
.Fl o
flag to spin up the disks in the LUN.
.El
.It Ic stop
Send the
.Tn SCSI
START STOP UNIT command to the specified LUN with the start
bit cleared. We use an ordered tag to stop the LUN, so we can guarantee
that all pending I/O executes before it is stopped. (CTL guarantees this
anyway, but
.Nm
sends an ordered tag for completeness.)
.Bl -tag -width 4n
.It Fl i
Set the immediate bit in the CDB. Note that CTL does not support the
immediate bit, so this is primarily useful for making sure that CTL returns
the proper error.
.It Fl o
Set the Copan proprietary on/offline bit in the CDB. When this flag is
used, the LUN will be spun down and taken offline ("Logical unit not ready,
manual intervention required"). See the description of the
.Ic shutdown
and
.Ic startup
options.
.El
.It Ic synccache
Send the
.Tn SCSI
SYNCHRONIZE CACHE command to the device. By default, SYNCHRONIZE
CACHE(10) is used. If the specified starting LBA is greater than
0xffffffff or the length is greater than 0xffff, though,
SYNCHRONIZE CACHE(16) will be used. The 16 byte command will also be used
if the user specifies a 16 byte CDB with the
.Fl c
argument.
.Bl -tag -width 14n
.It Fl l Ar lba
Specify the starting LBA of the cache region to synchronize. This option is a
no-op for CTL. If you send a SYNCHRONIZE CACHE command, it will sync the
cache for the entire LUN.
.It Fl b Ar blockcount
Specify the length of the cache region to synchronize. This option is a
no-op for CTL. If you send a SYNCHRONIZE CACHE command, it will sync the
cache for the entire LUN.
.It Fl r
Specify relative addressing for the starting LBA. CTL does not support
relative addressing, since it only works for linked commands, and CTL
doesn't support linked commands.
.It Fl i
Tell the target to return status immediately after issuing the SYHCHRONIZE CACHE
command rather than waiting for the cache to finish syncing. CTL does not
support this bit.
.It Fl c Ar cdbsize
Specify the minimum CDB size. Valid values are 10 and 16 bytes.
.El
.It Ic shutdown
Issue a
.Tn SCSI
START STOP UNIT command with the start bit cleared and the on/offline bit
set to all direct access LUNs. This will spin down all direct access LUNs,
and mark them offline ("Logical unit not ready, manual intervention
required"). Once marked offline, the state can only be cleared by sending
a START STOP UNIT command with the start bit set and the on/offline bit
set. The
.Nm
commands
.Ic startup
and
.Ic start
will accomplish this. Note that the
on/offline bit is a non-standard Copan extension to the
.Tn SCSI
START STOP UNIT command, so merely sending a normal start command from an
initiator will not clear the condition. (This is by design.)
.It Ic startup
Issue a
.Tn SCSI
START STOP UNIT command with the start bit set and the on/offline bit set
to all direct access LUNs. This will mark all direct access LUNs "online"
again. It will not cause any LUNs to start up. A separate start command
without the on/offline bit set is necessary for that.
.It Ic hardstop
Use the kernel facility for stopping all direct access LUNs and setting the
offline bit. Unlike the
.Ic shutdown
command above, this command allows shutting down LUNs with I/O active. It
will also issue a LUN reset to any reserved LUNs to break the reservation
so that the LUN can be stopped.
.Ic shutdown
command instead.
.It Ic hardstart
This command is functionally identical to the
.Ic startup
command described above. The primary difference is that the LUNs are
enumerated and commands sent by the in-kernel Front End Target Driver
instead of by
.Nm .
.It Ic lunlist
List all LUNs registered with CTL.
Because this command uses the ioctl port, it will only work when the FETDs
(Front End Target Drivers) are enabled.
This command is the equivalent of doing a REPORT LUNS on one LUN and then
and then an INQUIRY on each LUN in the system.
.It Ic delay
Delay commands at the given location. There are two places where commands
may be delayed currently: before data is transferred
.Pq Dq datamove
and just prior to sending status to the host
.Pq Dq done .
One of the two must be supplied as an argument to the
.Fl l
option. The
.Fl t
option must also be specified.
.Bl -tag -width 12n
.It Fl l Ar delayloc
Delay command(s) at the specified location.
This can either be at the data movement stage (datamove) or prior to
command completion (done).
.It Fl t Ar delaytime
Delay command(s) for the specified number of seconds. This must be
specified. If set to 0, it will clear out any previously set delay for
this particular location (datamove or done).
.It Fl T Ar delaytype
Specify the delay type.
By default, the
.Ic delay
option will delay the next command sent to the given LUN.
With the
.Fl T Ar cont
option, every command will be delayed by the specified period of time.
With the
.Fl T Ar oneshot
the next command sent to the given LUN will be delayed and all subsequent
commands will be completed normally.
This is the default.
.El
.It Ic realsync
Query and control CTL's SYNCHRONIZE CACHE behavior. The
.Sq query
argument
will show whether SYNCHRONIZE CACHE commands are being sent to the backend
or not.
The default is to send SYNCHRONIZE CACHE commands to the backend.
The
.Sq on
argument will cause all SYNCHRONIZE CACHE commands sent to all LUNs to be
sent to the backend.
The
.Sq off
argument will cause all SYNCHRONIZE CACHE commands sent to all LUNs to be
immediately returned to the initiator with successful status.
.It Ic setsync
For a given lun, only actually service every Nth SYNCHRONIZE CACHE command
that is sent. This can be used for debugging the optimal time period for
sending SYNCHRONIZE cache commands. An interval of 0 means that the cache
will be flushed for this LUN every time a SYNCHRONIZE CACHE command is
received.
.Pp
You must specify the target and LUN you want to modify.
.It Ic getsync
Get the interval at which we actually service the SYNCHRONIZE CACHE
command, as set by the
.Ic setsync
command above.
The reported number means that we will actually flush the cache on every
Nth SYNCHRONIZE CACHE command. A value of 0 means that we will flush the
cache every time.
.Pp
You must specify the target and LUN you want to query.
.It Ic inject
Inject the specified type of error for the LUN specified, when a command
that matches the given pattern is seen.
The sense data returned is in either fixed or descriptor format, depending
upon the status of the D_SENSE bit in the control mode page (page 0xa) for
the LUN.
.Pp
Errors are only injected for commands that have not already failed for
other reasons.
By default, only the first command matching the pattern specified is
returned with the supplied error.
.Pp
If the
.Fl c
flag is specified, all commands matching the pattern will be returned with
the specified error until the error injection command is deleted with
.Fl d
flag.
.Bl -tag -width 17n
.It Fl i Ar action
Specify the error to return:
.Bl -tag -width 10n
.It aborted
Return the next matching command on the specified LUN with the sense key
ABORTED COMMAND (0x0b), and the ASC/ASCQ 0x45,0x00 ("Select or reselect
failure").
.It mediumerr
Return the next matching command on the specified LUN with the sense key
MEDIUM ERROR (0x03) and the ASC/ASCQ 0x11,0x00 ("Unrecovered read error") for
reads, or ASC/ASCQ 0x0c,0x02 ("Write error - auto reallocation failed")
for write errors.
.It ua
Return the next matching command on the specified LUN with the sense key
UNIT ATTENTION (0x06) and the ASC/ASCQ 0x29,0x00 ("POWER ON, RESET, OR BUS
DEVICE RESET OCCURRED").
.It custom
Return the next matching command on the specified LUN with the supplied
sense data.
The
.Fl s
argument must be specified.
.El
.It Fl p Ar pattern
Specify which commands should be returned with the given error.
.Bl -tag -width 10n
.It read
The error should apply to READ(6), READ(10), READ(12), READ(16), etc.
.It write
The error should apply to WRITE(6), WRITE(10), WRITE(12), WRITE(16), WRITE
AND VERIFY(10), etc.
.It rw
The error should apply to both read and write type commands.
.It readcap
The error should apply to READ CAPACITY(10) and READ CAPACITY(16) commands.
.It tur
The error should apply to TEST UNIT READY commands.
.It any
The error should apply to any command.
.El
.It Fl r Ar lba,len
Specify the starting lba and length of the range of LBAs which should
trigger an error.
This option is only applies when read and/or write patterns are specified.
If used with other command types, the error will never be triggered.
.It Fl s Ar len fmt Op Ar args
Specify the sense data that is to be returned for custom actions.
If the format is
.Sq - ,
len bytes of sense data will be read from standard input and written to the
sense buffer.
If len is longer than 252 bytes (the maximum allowable
.Tn SCSI
sense data length), it will be truncated to that length.
The sense data format is described in
.Xr cam_cdparse 3 .
.It Fl c
The error injection should be persistent, instead of happening once.
Persistent errors must be deleted with the
.Fl d
argument.
.It Fl d Ar delete_id
Delete the specified error injection serial number.
The serial number is returned when the error is injected.
.El
.It Ic port
Perform one of several CTL frontend port operations.
Either get a list of frontend ports
.Pq Fl l ,
turn one or more frontends on
or off
.Pq Fl o Ar on|off ,
or set the World Wide Node Name
.Pq Fl w Ar wwnn
or World Wide Port Name
.Pq Fl W Ar wwpn
for a given port.
One of
.Fl l ,
.Fl o ,
or
.Fl w
or
.Fl W
must be specified.
The WWNN and WWPN may both be specified at the same time, but cannot be
combined with enabling/disabling or listing ports.
.Bl -tag -width 12n
.It Fl l
List all CTL frontend ports or a specific port type or number.
.It Fl o Ar on|off
Turn the specified CTL frontend ports off or on.
If no port number or port type is specified, all ports are turned on or
off.
.It Fl p Ar targ_port
Specify the frontend port number.
The port numbers can be found in the frontend port list.
.It Fl q
Omit the header in the port list output.
.It Fl t Ar fe_type
Specify the frontend type.
Currently defined port types are
.Dq fc
(Fibre Channel),
.Dq scsi
(Parallel SCSI),
.Dq ioctl
(CTL ioctl interface),
and
.Dq internal
(CTL CAM SIM).
.It Fl w Ar wwnn
Set the World Wide Node Name for the given port.
The
.Fl n
argument must be specified, since this is only possible to implement on a
single port.
As a general rule, the WWNN should be the same across all ports on the
system.
.It Fl W Ar wwpn
Set the World Wide Node Name for the given port.
The
.Fl n
argument must be specified, since this is only possible to implement on a
single port.
As a general rule, the WWPN must be different for every port in the system.
.It Fl x
Output the port list in XML format.
.El
.It Ic dumpooa
Dump the OOA (Order Of Arrival) queue for each LUN registered with CTL.
.It Ic dumpstructs
Dump the CTL structures to the console.
.It Ic create
Create a new LUN.
The backend must be specified, and depending upon the backend requested,
some of the other options may be required.
If the LUN is created successfully, the LUN configuration will be
displayed.
If LUN creation fails, a message will be displayed describing the failure.
.Bl -tag -width 14n
.It Fl b Ar backend
The
.Fl b
flag is required.
This specifies the name backend to use when creating the LUN.
Examples are
.Dq ramdisk
and
.Dq block .
.It Fl B Ar blocksize
Specify the blocksize of the backend in bytes.
.It Fl d Ar device_id
Specify the LUN-associated string to use in the
.Tn SCSI
INQUIRY VPD page 0x83 data.
.It Fl l Ar lun_id
Request that a particular LUN number be assigned.
If the requested LUN number is not available, the request will fail.
.It Fl o Ar name=value
Specify a backend-specific name/value pair.
Multiple
.Fl o
arguments may be specified.
Refer to the backend documentation for arguments that may be used.
.It Fl s Ar size_bytes
Specify the size of the LUN in bytes.
Some backends may allow setting the size (e.g. the ramdisk backend) and for
others the size may be implicit (e.g. the block backend).
.It Fl S Ar serial_num
Specify the serial number to be used in the
.Tn SCSI
INQUIRY VPD page 0x80 data.
.It Fl t Ar device_type
Specify the numeric SCSI device type to use when creating the LUN.
For example, the Direct Access type is 0.
If this flag is not used, the type of LUN created is backend-specific.
Not all LUN types are supported.
Currently CTL only supports Direct Access (type 0) and Processor (type 3)
LUNs.
The backend requested may or may not support all of the LUN types that CTL
supports.
.El
.It Ic remove
Remove a LUN.
The backend must be specified, and the LUN number must also be specified.
Backend-specific options may also be specified with the
.Fl o
flag.
.Bl -tag -width 14n
.It Fl b Ar backend
Specify the backend that owns the LUN to be removed.
Examples are
.Dq ramdisk
and
.Dq block .
.It Fl l Ar lun_id
Specify the LUN number to remove.
.It Fl o Ar name=value
Specify a backend-specific name/value pair.
Multiple
.Fl o
arguments may be specified.
Refer to the backend documentation for arguments that may be used.
.El
.It Ic devlist
Get a list of all configured LUNs.
This also includes the LUN size and blocksize, serial number and device ID.
.Bl -tag -width 11n
.It Fl b Ar backend
Specify the backend.
This restricts the LUN list to the named backend.
Examples are
.Dq ramdisk
and
.Dq block .
.It Fl v
Be verbose.
This will also display any backend-specific LUN attributes in addition to
the standard per-LUN information.
.It Fl x
Dump the raw XML.
The LUN list information from the kernel comes in XML format, and this
option allows the display of the raw XML data.
This option and the
.Fl v
and
.Fl b
options are mutually exclusive.
If you specify
.Fl x ,
the entire LUN database is displayed in XML format.
.El
.It Ic help
Display
.Nm
usage information.
.El
.Sh EXAMPLES
.Dl ctladm tur 0:1
.Pp
Send a
.Tn SCSI
TEST UNIT READY command to LUN 1.
.Pp
.Dl ctladm modesense 0:1 -l
.Pp
Display the list of mode pages supported by LUN 1.
.Pp
.Dl ctladm modesense 0:0 -m 10 -P 3 -d -c 10
.Pp
Display the saved version of the Control mode page (page 10) on LUN 0.
Disable fetching block descriptors, and use a 10 byte MODE SENSE command
instead of the default 6 byte command.
.Pp
.Bd -literal
ctladm read 0:2 -l 0 -d 1 -b 512 -f - > foo
.Ed
.Pp
Read the first 512 byte block from LUN 2 and dump it to the file
.Pa foo .
.Bd -literal
ctladm write 0:3 -l 0xff432140 -d 20 -b 512 -f /tmp/bar
.Ed
.Pp
Read 10240 bytes from the file
.Pa /tmp/bar
and write it to target 0, LUN 3.
starting at LBA 0xff432140.
.Pp
.Dl ctladm create -b ramdisk -s 10485760000000000
.Pp
Create a LUN with the
.Dq fake
ramdisk as a backing store.
The LUN will claim to have a size of approximately 10 terabytes.
.Pp
.Dl ctladm create -b block -o file=src/usr.sbin/ctladm/ctladm.8
.Pp
Create a LUN using the block backend, and specify the file
.Pa src/usr.sbin/ctladm/ctladm.8
as the backing store.
The size of the LUN will be derived from the size of the file.
.Pp
.Dl ctladm create -b block -o file=src/usr.sbin/ctladm/ctladm.8 -S MYSERIAL321 -d MYDEVID123
.Pp
Create a LUN using the block backend, specify the file
.Pa src/usr.sbin/ctladm/ctladm.8
as the backing store, and specify the
.Tn SCSI
VPD page 0x80 and 0x83 serial number (
.Fl S)
and device ID (
.Fl d).
.Pp
.Dl ctladm remove -b block -l 12
.Pp
Remove LUN 12, which is handled by the block backend, from the system.
.Pp
.Dl ctladm devlist
.Pp
List configured LUNs in the system, along with their backend and serial
number.
This works when the Front End Target Drivers are enabled or disabled.
.Pp
.Dl ctladm lunlist
.Pp
List all LUNs in the system, along with their inquiry data and device type.
This only works when the FETDs are enabled, since the commands go through the
ioctl port.
.Pp
.Dl ctladm inject 0:6 -i mediumerr -p read -r 0,512 -c
.Pp
Inject a medium error on LUN 6 for every read that covers the first 512
blocks of the LUN.
.Pp
.Bd -literal -offset indent
ctladm inject 0:6 -i custom -p tur -s 18 "f0 0 02 s12 04 02"
.Ed
.Pp
Inject a custom error on LUN 6 for the next TEST UNIT READY command only.
This will result in a sense key of NOT READY (0x02), and an ASC/ASCQ of
0x04,0x02 ("Logical unit not ready, initializing command required").
.Sh SEE ALSO
.Xr cam 3 ,
.Xr cam_cdbparse 3 ,
.Xr cam 4 ,
.Xr xpt 4 ,
.Xr camcontrol 8
.Sh HISTORY
The
.Nm
utility was originally written during the Winter/Spring of 2003 as an
interface to CTL.
.Sh AUTHORS
.An Ken Merry Aq ken@FreeBSD.org

4005
usr.sbin/ctladm/ctladm.c Normal file

File diff suppressed because it is too large Load Diff

50
usr.sbin/ctladm/ctladm.h Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Kenneth D. Merry.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
* $Id: //depot/users/kenm/FreeBSD-test2/usr.sbin/ctladm/ctladm.h#1 $
*/
#ifndef _CTLADM_H
#define _CTLADM_H
/*
* get_hook: Structure for evaluating args in a callback.
*/
struct get_hook
{
int argc;
char **argv;
int got;
};
char *cget(void *hook, char *name);
int iget(void *hook, char *name);
void arg_put(void *hook, int letter, void *arg, int count, char *name);
void usage(int error);
#endif /* _CTLADM_H */

156
usr.sbin/ctladm/util.c Normal file
View File

@ -0,0 +1,156 @@
/*
* Written By Julian ELischer
* Copyright julian Elischer 1993.
* Permission is granted to use or redistribute this file in any way as long
* as this notice remains. Julian Elischer does not guarantee that this file
* is totally correct for any given task and users of this file must
* accept responsibility for any damage that occurs from the application of this
* file.
*
* (julian@tfs.com julian@dialix.oz.au)
*
* User SCSI hooks added by Peter Dufault:
*
* Copyright (c) 1994 HD Associates
* (contact: dufault@hda.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of HD Associates
* may not be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Taken from the original scsi(8) program.
* from: scsi.c,v 1.17 1998/01/12 07:57:57 charnier Exp $";
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/stdint.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <camlib.h>
#include "ctladm.h"
int verbose;
/* iget: Integer argument callback
*/
int
iget(void *hook, char *name)
{
struct get_hook *h = (struct get_hook *)hook;
int arg;
if (h->got >= h->argc)
{
fprintf(stderr, "Expecting an integer argument.\n");
usage(0);
exit(1);
}
arg = strtol(h->argv[h->got], 0, 0);
h->got++;
if (verbose && name && *name)
printf("%s: %d\n", name, arg);
return arg;
}
/* cget: char * argument callback
*/
char *
cget(void *hook, char *name)
{
struct get_hook *h = (struct get_hook *)hook;
char *arg;
if (h->got >= h->argc)
{
fprintf(stderr, "Expecting a character pointer argument.\n");
usage(0);
exit(1);
}
arg = h->argv[h->got];
h->got++;
if (verbose && name)
printf("cget: %s: %s", name, arg);
return arg;
}
/* arg_put: "put argument" callback
*/
void
arg_put(void *hook __unused, int letter, void *arg, int count, char *name)
{
if (verbose && name && *name)
printf("%s: ", name);
switch(letter)
{
case 'i':
case 'b':
printf("%jd ", (intmax_t)(intptr_t)arg);
break;
case 'c':
case 'z':
{
char *p;
p = malloc(count + 1);
if (p == NULL) {
fprintf(stderr, "can't malloc memory for p\n");
exit(1);
}
bzero(p, count +1);
strncpy(p, (char *)arg, count);
if (letter == 'z')
{
int i;
for (i = count - 1; i >= 0; i--)
if (p[i] == ' ')
p[i] = 0;
else
break;
}
printf("%s ", p);
free(p);
}
break;
default:
printf("Unknown format letter: '%c'\n", letter);
}
if (verbose)
putchar('\n');
}

View File

@ -253,7 +253,7 @@ mlx_scsi_inquiry(int unit, int channel, int target, char **vendor, char **device
/* build the cdb */
inq_cmd->opcode = INQUIRY;
inq_cmd->length = SHORT_INQUIRY_LENGTH;
scsi_ulto2b(SHORT_INQUIRY_LENGTH, inq_cmd->length);
/* hand it off for processing */
mlx_perform(unit, mlx_command, &cmd);