freebsd-dev/sys/cam/ctl/ctl_frontend_internal.c

1789 lines
44 KiB
C
Raw Normal View History

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
2012-01-12 00:34:33 +00:00
/*-
* Copyright (c) 2004, 2005 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.c#5 $
*/
/*
* CTL kernel internal frontend target driver. This allows kernel-level
* clients to send commands into CTL.
*
* This has elements of a FETD (e.g. it has to set tag numbers, initiator,
* port, target, and LUN) and elements of an initiator (LUN discovery and
* probing, error recovery, command initiation). Even though this has some
* initiator type elements, this is not intended to be a full fledged
* initiator layer. It is only intended to send a limited number of
* commands to a well known target layer.
*
* To be able to fulfill the role of a full initiator layer, it would need
* a whole lot more functionality.
*
* 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/sbuf.h>
#include <sys/sysctl.h>
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
2012-01-12 00:34:33 +00:00
#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_util.h>
#include <cam/ctl/ctl_ha.h>
#include <cam/ctl/ctl_private.h>
#include <cam/ctl/ctl_mem_pool.h>
#include <cam/ctl/ctl_debug.h>
#include <cam/ctl/ctl_scsi_all.h>
#include <cam/ctl/ctl_error.h>
/*
* Task structure:
* - overall metatask, different potential metatask types (e.g. forced
* shutdown, gentle shutdown)
* - forced shutdown metatask:
* - states: report luns, pending, done?
* - list of luns pending, with the relevant I/O for that lun attached.
* This would allow moving ahead on LUNs with no errors, and going
* into error recovery on LUNs with problems. Per-LUN states might
* include inquiry, stop/offline, done.
*
* Use LUN enable for LUN list instead of getting it manually? We'd still
* need inquiry data for each LUN.
*
* How to handle processor LUN w.r.t. found/stopped counts?
*/
#ifdef oldapi
typedef enum {
CFI_TASK_NONE,
CFI_TASK_SHUTDOWN,
CFI_TASK_STARTUP
} cfi_tasktype;
struct cfi_task_startstop {
int total_luns;
int luns_complete;
int luns_failed;
cfi_cb_t callback;
void *callback_arg;
/* XXX KDM add more fields here */
};
union cfi_taskinfo {
struct cfi_task_startstop startstop;
};
struct cfi_metatask {
cfi_tasktype tasktype;
cfi_mt_status status;
union cfi_taskinfo taskinfo;
struct ctl_mem_element *element;
void *cfi_context;
STAILQ_ENTRY(cfi_metatask) links;
};
#endif
typedef enum {
CFI_ERR_RETRY = 0x000,
CFI_ERR_FAIL = 0x001,
CFI_ERR_LUN_RESET = 0x002,
CFI_ERR_MASK = 0x0ff,
CFI_ERR_NO_DECREMENT = 0x100
} cfi_error_action;
typedef enum {
CFI_ERR_SOFT,
CFI_ERR_HARD
} cfi_error_policy;
typedef enum {
CFI_LUN_INQUIRY,
CFI_LUN_READCAPACITY,
CFI_LUN_READCAPACITY_16,
CFI_LUN_READY
} cfi_lun_state;
struct cfi_lun {
struct ctl_id target_id;
int lun_id;
struct scsi_inquiry_data inq_data;
uint64_t num_blocks;
uint32_t blocksize;
int blocksize_powerof2;
uint32_t cur_tag_num;
cfi_lun_state state;
struct ctl_mem_element *element;
struct cfi_softc *softc;
STAILQ_HEAD(, cfi_lun_io) io_list;
STAILQ_ENTRY(cfi_lun) links;
};
struct cfi_lun_io {
struct cfi_lun *lun;
struct cfi_metatask *metatask;
cfi_error_policy policy;
void (*done_function)(union ctl_io *io);
union ctl_io *ctl_io;
struct cfi_lun_io *orig_lun_io;
STAILQ_ENTRY(cfi_lun_io) links;
};
typedef enum {
CFI_NONE = 0x00,
CFI_ONLINE = 0x01,
} cfi_flags;
struct cfi_softc {
struct ctl_frontend fe;
char fe_name[40];
struct mtx lock;
cfi_flags flags;
STAILQ_HEAD(, cfi_lun) lun_list;
STAILQ_HEAD(, cfi_metatask) metatask_list;
struct ctl_mem_pool lun_pool;
struct ctl_mem_pool metatask_pool;
};
MALLOC_DEFINE(M_CTL_CFI, "ctlcfi", "CTL CFI");
static struct cfi_softc fetd_internal_softc;
extern int ctl_disable;
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
2012-01-12 00:34:33 +00:00
void cfi_init(void);
void cfi_shutdown(void) __unused;
static void cfi_online(void *arg);
static void cfi_offline(void *arg);
static int cfi_targ_enable(void *arg, struct ctl_id targ_id);
static int cfi_targ_disable(void *arg, struct ctl_id targ_id);
static int cfi_lun_enable(void *arg, struct ctl_id target_id, int lun_id);
static int cfi_lun_disable(void *arg, struct ctl_id target_id, int lun_id);
static void cfi_datamove(union ctl_io *io);
static cfi_error_action cfi_checkcond_parse(union ctl_io *io,
struct cfi_lun_io *lun_io);
static cfi_error_action cfi_error_parse(union ctl_io *io,
struct cfi_lun_io *lun_io);
static void cfi_init_io(union ctl_io *io, struct cfi_lun *lun,
struct cfi_metatask *metatask, cfi_error_policy policy,
int retries, struct cfi_lun_io *orig_lun_io,
void (*done_function)(union ctl_io *io));
static void cfi_done(union ctl_io *io);
static void cfi_lun_probe_done(union ctl_io *io);
static void cfi_lun_probe(struct cfi_lun *lun, int have_lock);
static void cfi_metatask_done(struct cfi_softc *softc,
struct cfi_metatask *metatask);
static void cfi_metatask_bbr_errorparse(struct cfi_metatask *metatask,
union ctl_io *io);
static void cfi_metatask_io_done(union ctl_io *io);
static void cfi_err_recovery_done(union ctl_io *io);
static void cfi_lun_io_done(union ctl_io *io);
SYSINIT(cfi_init, SI_SUB_CONFIGURE, SI_ORDER_FOURTH, cfi_init, NULL);
void
cfi_init(void)
{
struct cfi_softc *softc;
struct ctl_frontend *fe;
int retval;
softc = &fetd_internal_softc;
fe = &softc->fe;
retval = 0;
/* If we're disabled, don't initialize */
if (ctl_disable != 0)
return;
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
2012-01-12 00:34:33 +00:00
if (sizeof(struct cfi_lun_io) > CTL_PORT_PRIV_SIZE) {
printf("%s: size of struct cfi_lun_io %zd > "
"CTL_PORT_PRIV_SIZE %d\n", __func__,
sizeof(struct cfi_lun_io),
CTL_PORT_PRIV_SIZE);
}
memset(softc, 0, sizeof(softc));
mtx_init(&softc->lock, "CTL frontend mutex", NULL, MTX_DEF);
softc->flags |= CTL_FLAG_MASTER_SHELF;
STAILQ_INIT(&softc->lun_list);
STAILQ_INIT(&softc->metatask_list);
sprintf(softc->fe_name, "CTL internal");
fe->port_type = CTL_PORT_INTERNAL;
fe->num_requested_ctl_io = 100;
fe->port_name = softc->fe_name;
fe->port_online = cfi_online;
fe->port_offline = cfi_offline;
fe->onoff_arg = softc;
fe->targ_enable = cfi_targ_enable;
fe->targ_disable = cfi_targ_disable;
fe->lun_enable = cfi_lun_enable;
fe->lun_disable = cfi_lun_disable;
fe->targ_lun_arg = softc;
fe->fe_datamove = cfi_datamove;
fe->fe_done = cfi_done;
fe->max_targets = 15;
fe->max_target_id = 15;
if (ctl_frontend_register(fe, (softc->flags & CTL_FLAG_MASTER_SHELF)) != 0)
{
printf("%s: internal frontend registration failed\n", __func__);
retval = 1;
goto bailout;
}
if (ctl_init_mem_pool(&softc->lun_pool,
sizeof(struct cfi_lun),
CTL_MEM_POOL_PERM_GROW, /*grow_inc*/ 3,
/* initial_pool_size */ CTL_MAX_LUNS) != 0) {
printf("%s: can't initialize LUN memory pool\n", __func__);
retval = 1;
goto bailout_error;
}
if (ctl_init_mem_pool(&softc->metatask_pool,
sizeof(struct cfi_metatask),
CTL_MEM_POOL_PERM_GROW, /*grow_inc*/ 3,
/*initial_pool_size*/ 10) != 0) {
printf("%s: can't initialize metatask memory pool\n", __func__);
retval = 2;
goto bailout_error;
}
bailout:
return;
bailout_error:
switch (retval) {
case 3:
ctl_shrink_mem_pool(&softc->metatask_pool);
/* FALLTHROUGH */
case 2:
ctl_shrink_mem_pool(&softc->lun_pool);
/* FALLTHROUGH */
case 1:
ctl_frontend_deregister(fe);
break;
default:
break;
}
}
void
cfi_shutdown(void)
{
struct cfi_softc *softc;
softc = &fetd_internal_softc;
/*
* XXX KDM need to clear out any I/O pending on each LUN.
*/
if (ctl_frontend_deregister(&softc->fe) != 0)
printf("%s: ctl_frontend_deregister() failed\n", __func__);
if (ctl_shrink_mem_pool(&softc->lun_pool) != 0)
printf("%s: error shrinking LUN pool\n", __func__);
if (ctl_shrink_mem_pool(&softc->metatask_pool) != 0)
printf("%s: error shrinking LUN pool\n", __func__);
}
static void
cfi_online(void *arg)
{
struct cfi_softc *softc;
struct cfi_lun *lun;
softc = (struct cfi_softc *)arg;
softc->flags |= CFI_ONLINE;
/*
* Go through and kick off the probe for each lun. Should we check
* the LUN flags here to determine whether or not to probe it?
*/
mtx_lock(&softc->lock);
STAILQ_FOREACH(lun, &softc->lun_list, links)
cfi_lun_probe(lun, /*have_lock*/ 1);
mtx_unlock(&softc->lock);
}
static void
cfi_offline(void *arg)
{
struct cfi_softc *softc;
softc = (struct cfi_softc *)arg;
softc->flags &= ~CFI_ONLINE;
}
static int
cfi_targ_enable(void *arg, struct ctl_id targ_id)
{
return (0);
}
static int
cfi_targ_disable(void *arg, struct ctl_id targ_id)
{
return (0);
}
static int
cfi_lun_enable(void *arg, struct ctl_id target_id, int lun_id)
{
struct ctl_mem_element *element;
struct cfi_softc *softc;
struct cfi_lun *lun;
int found;
softc = (struct cfi_softc *)arg;
found = 0;
mtx_lock(&softc->lock);
STAILQ_FOREACH(lun, &softc->lun_list, links) {
if ((lun->target_id.id == target_id.id)
&& (lun->lun_id == lun_id)) {
found = 1;
break;
}
}
mtx_unlock(&softc->lock);
/*
* If we already have this target/LUN, there is no reason to add
* it to our lists again.
*/
if (found != 0)
return (0);
element = ctl_alloc_mem_element(&softc->lun_pool, /*can_wait*/ 0);
if (element == NULL) {
printf("%s: unable to allocate LUN structure\n", __func__);
return (1);
}
lun = (struct cfi_lun *)element->bytes;
lun->element = element;
lun->target_id = target_id;
lun->lun_id = lun_id;
lun->cur_tag_num = 0;
lun->state = CFI_LUN_INQUIRY;
lun->softc = softc;
STAILQ_INIT(&lun->io_list);
mtx_lock(&softc->lock);
STAILQ_INSERT_TAIL(&softc->lun_list, lun, links);
mtx_unlock(&softc->lock);
cfi_lun_probe(lun, /*have_lock*/ 0);
return (0);
}
static int
cfi_lun_disable(void *arg, struct ctl_id target_id, int lun_id)
{
struct cfi_softc *softc;
struct cfi_lun *lun;
int found;
softc = (struct cfi_softc *)arg;
found = 0;
/*
* XXX KDM need to do an invalidate and then a free when any
* pending I/O has completed. Or do we? CTL won't free a LUN
* while any I/O is pending. So we won't get this notification
* unless any I/O we have pending on a LUN has completed.
*/
mtx_lock(&softc->lock);
STAILQ_FOREACH(lun, &softc->lun_list, links) {
if ((lun->target_id.id == target_id.id)
&& (lun->lun_id == lun_id)) {
found = 1;
break;
}
}
if (found != 0)
STAILQ_REMOVE(&softc->lun_list, lun, cfi_lun, links);
mtx_unlock(&softc->lock);
if (found == 0) {
printf("%s: can't find target %ju lun %d\n", __func__,
(uintmax_t)target_id.id, lun_id);
return (1);
}
ctl_free_mem_element(lun->element);
return (0);
}
/*
* XXX KDM run this inside a thread, or inside the caller's context?
*/
static void
cfi_datamove(union ctl_io *io)
{
struct ctl_sg_entry *ext_sglist, *kern_sglist;
struct ctl_sg_entry ext_entry, kern_entry;
int ext_sglen, ext_sg_entries, kern_sg_entries;
int ext_sg_start, ext_offset;
int len_to_copy, len_copied;
int kern_watermark, ext_watermark;
int ext_sglist_malloced;
struct ctl_scsiio *ctsio;
int i, j;
ext_sglist_malloced = 0;
ext_sg_start = 0;
ext_offset = 0;
ext_sglist = NULL;
CTL_DEBUG_PRINT(("%s\n", __func__));
ctsio = &io->scsiio;
/*
* If this is the case, we're probably doing a BBR read and don't
* actually need to transfer the data. This will effectively
* bit-bucket the data.
*/
if (ctsio->ext_data_ptr == NULL)
goto bailout;
/*
* To simplify things here, if we have a single buffer, stick it in
* a S/G entry and just make it a single entry S/G list.
*/
if (ctsio->io_hdr.flags & CTL_FLAG_EDPTR_SGLIST) {
int len_seen;
ext_sglen = ctsio->ext_sg_entries * sizeof(*ext_sglist);
/*
* XXX KDM GFP_KERNEL, don't know what the caller's context
* is. Need to figure that out.
*/
ext_sglist = (struct ctl_sg_entry *)malloc(ext_sglen, M_CTL_CFI,
M_WAITOK);
if (ext_sglist == NULL) {
ctl_set_internal_failure(ctsio,
/*sks_valid*/ 0,
/*retry_count*/ 0);
return;
}
ext_sglist_malloced = 1;
if (memcpy(ext_sglist, ctsio->ext_data_ptr, ext_sglen) != 0) {
ctl_set_internal_failure(ctsio,
/*sks_valid*/ 0,
/*retry_count*/ 0);
goto bailout;
}
ext_sg_entries = ctsio->ext_sg_entries;
len_seen = 0;
for (i = 0; i < ext_sg_entries; i++) {
if ((len_seen + ext_sglist[i].len) >=
ctsio->ext_data_filled) {
ext_sg_start = i;
ext_offset = ctsio->ext_data_filled - len_seen;
break;
}
len_seen += ext_sglist[i].len;
}
} else {
ext_sglist = &ext_entry;
ext_sglist->addr = ctsio->ext_data_ptr;
ext_sglist->len = ctsio->ext_data_len;
ext_sg_entries = 1;
ext_sg_start = 0;
ext_offset = ctsio->ext_data_filled;
}
if (ctsio->kern_sg_entries > 0) {
kern_sglist = (struct ctl_sg_entry *)ctsio->kern_data_ptr;
kern_sg_entries = ctsio->kern_sg_entries;
} else {
kern_sglist = &kern_entry;
kern_sglist->addr = ctsio->kern_data_ptr;
kern_sglist->len = ctsio->kern_data_len;
kern_sg_entries = 1;
}
kern_watermark = 0;
ext_watermark = ext_offset;
len_copied = 0;
for (i = ext_sg_start, j = 0;
i < ext_sg_entries && j < kern_sg_entries;) {
uint8_t *ext_ptr, *kern_ptr;
len_to_copy = ctl_min(ext_sglist[i].len - ext_watermark,
kern_sglist[j].len - kern_watermark);
ext_ptr = (uint8_t *)ext_sglist[i].addr;
ext_ptr = ext_ptr + ext_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
kern_ptr = (uint8_t *)kern_sglist[j].addr;
kern_ptr = kern_ptr + kern_watermark;
kern_watermark += len_to_copy;
ext_watermark += len_to_copy;
if ((ctsio->io_hdr.flags & CTL_FLAG_DATA_MASK) ==
CTL_FLAG_DATA_IN) {
CTL_DEBUG_PRINT(("%s: copying %d bytes to user\n",
__func__, len_to_copy));
CTL_DEBUG_PRINT(("%s: from %p to %p\n", __func__,
kern_ptr, ext_ptr));
memcpy(ext_ptr, kern_ptr, len_to_copy);
} else {
CTL_DEBUG_PRINT(("%s: copying %d bytes from user\n",
__func__, len_to_copy));
CTL_DEBUG_PRINT(("%s: from %p to %p\n", __func__,
ext_ptr, kern_ptr));
memcpy(kern_ptr, ext_ptr, len_to_copy);
}
len_copied += len_to_copy;
if (ext_sglist[i].len == ext_watermark) {
i++;
ext_watermark = 0;
}
if (kern_sglist[j].len == kern_watermark) {
j++;
kern_watermark = 0;
}
}
ctsio->ext_data_filled += len_copied;
CTL_DEBUG_PRINT(("%s: ext_sg_entries: %d, kern_sg_entries: %d\n",
__func__, ext_sg_entries, kern_sg_entries));
CTL_DEBUG_PRINT(("%s: ext_data_len = %d, kern_data_len = %d\n",
__func__, ctsio->ext_data_len, ctsio->kern_data_len));
/* XXX KDM set residual?? */
bailout:
if (ext_sglist_malloced != 0)
free(ext_sglist, M_CTL_CFI);
io->scsiio.be_move_done(io);
return;
}
/*
* For any sort of check condition, busy, etc., we just retry. We do not
* decrement the retry count for unit attention type errors. These are
* normal, and we want to save the retry count for "real" errors. Otherwise,
* we could end up with situations where a command will succeed in some
* situations and fail in others, depending on whether a unit attention is
* pending. Also, some of our error recovery actions, most notably the
* LUN reset action, will cause a unit attention.
*
* We can add more detail here later if necessary.
*/
static cfi_error_action
cfi_checkcond_parse(union ctl_io *io, struct cfi_lun_io *lun_io)
{
cfi_error_action error_action;
int error_code, sense_key, asc, ascq;
/*
* Default to retrying the command.
*/
error_action = CFI_ERR_RETRY;
scsi_extract_sense_len(&io->scsiio.sense_data,
io->scsiio.sense_len,
&error_code,
&sense_key,
&asc,
&ascq,
/*show_errors*/ 1);
switch (error_code) {
case SSD_DEFERRED_ERROR:
case SSD_DESC_DEFERRED_ERROR:
error_action |= CFI_ERR_NO_DECREMENT;
break;
case SSD_CURRENT_ERROR:
case SSD_DESC_CURRENT_ERROR:
default: {
switch (sense_key) {
case SSD_KEY_UNIT_ATTENTION:
error_action |= CFI_ERR_NO_DECREMENT;
break;
case SSD_KEY_HARDWARE_ERROR:
/*
* This is our generic "something bad happened"
* error code. It often isn't recoverable.
*/
if ((asc == 0x44) && (ascq == 0x00))
error_action = CFI_ERR_FAIL;
break;
case SSD_KEY_NOT_READY:
/*
* If the LUN is powered down, there likely isn't
* much point in retrying right now.
*/
if ((asc == 0x04) && (ascq == 0x02))
error_action = CFI_ERR_FAIL;
/*
* If the LUN is offline, there probably isn't much
* point in retrying, either.
*/
if ((asc == 0x04) && (ascq == 0x03))
error_action = CFI_ERR_FAIL;
break;
}
}
}
return (error_action);
}
static cfi_error_action
cfi_error_parse(union ctl_io *io, struct cfi_lun_io *lun_io)
{
cfi_error_action error_action;
error_action = CFI_ERR_RETRY;
switch (io->io_hdr.io_type) {
case CTL_IO_SCSI:
switch (io->io_hdr.status & CTL_STATUS_MASK) {
case CTL_SCSI_ERROR:
switch (io->scsiio.scsi_status) {
case SCSI_STATUS_RESERV_CONFLICT:
/*
* For a reservation conflict, we'll usually
* want the hard error recovery policy, so
* we'll reset the LUN.
*/
if (lun_io->policy == CFI_ERR_HARD)
error_action =
CFI_ERR_LUN_RESET;
else
error_action =
CFI_ERR_RETRY;
break;
case SCSI_STATUS_CHECK_COND:
default:
error_action = cfi_checkcond_parse(io, lun_io);
break;
}
break;
default:
error_action = CFI_ERR_RETRY;
break;
}
break;
case CTL_IO_TASK:
/*
* In theory task management commands shouldn't fail...
*/
error_action = CFI_ERR_RETRY;
break;
default:
printf("%s: invalid ctl_io type %d\n", __func__,
io->io_hdr.io_type);
panic("%s: invalid ctl_io type %d\n", __func__,
io->io_hdr.io_type);
break;
}
return (error_action);
}
static void
cfi_init_io(union ctl_io *io, struct cfi_lun *lun,
struct cfi_metatask *metatask, cfi_error_policy policy, int retries,
struct cfi_lun_io *orig_lun_io,
void (*done_function)(union ctl_io *io))
{
struct cfi_lun_io *lun_io;
io->io_hdr.nexus.initid.id = 7;
io->io_hdr.nexus.targ_port = lun->softc->fe.targ_port;
io->io_hdr.nexus.targ_target.id = lun->target_id.id;
io->io_hdr.nexus.targ_lun = lun->lun_id;
io->io_hdr.retries = retries;
lun_io = (struct cfi_lun_io *)io->io_hdr.port_priv;
io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = lun_io;
lun_io->lun = lun;
lun_io->metatask = metatask;
lun_io->ctl_io = io;
lun_io->policy = policy;
lun_io->orig_lun_io = orig_lun_io;
lun_io->done_function = done_function;
/*
* We only set the tag number for SCSI I/Os. For task management
* commands, the tag number is only really needed for aborts, so
* the caller can set it if necessary.
*/
switch (io->io_hdr.io_type) {
case CTL_IO_SCSI:
io->scsiio.tag_num = lun->cur_tag_num++;
break;
case CTL_IO_TASK:
default:
break;
}
}
static void
cfi_done(union ctl_io *io)
{
struct cfi_lun_io *lun_io;
struct cfi_softc *softc;
struct cfi_lun *lun;
lun_io = (struct cfi_lun_io *)
io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
lun = lun_io->lun;
softc = lun->softc;
/*
* Very minimal retry logic. We basically retry if we got an error
* back, and the retry count is greater than 0. If we ever want
* more sophisticated initiator type behavior, the CAM error
* recovery code in ../common might be helpful.
*/
if (((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)
&& (io->io_hdr.retries > 0)) {
ctl_io_status old_status;
cfi_error_action error_action;
error_action = cfi_error_parse(io, lun_io);
switch (error_action & CFI_ERR_MASK) {
case CFI_ERR_FAIL:
goto done;
break; /* NOTREACHED */
case CFI_ERR_LUN_RESET: {
union ctl_io *new_io;
struct cfi_lun_io *new_lun_io;
new_io = ctl_alloc_io(softc->fe.ctl_pool_ref);
if (new_io == NULL) {
printf("%s: unable to allocate ctl_io for "
"error recovery\n", __func__);
goto done;
}
ctl_zero_io(new_io);
new_io->io_hdr.io_type = CTL_IO_TASK;
new_io->taskio.task_action = CTL_TASK_LUN_RESET;
cfi_init_io(new_io,
/*lun*/ lun_io->lun,
/*metatask*/ NULL,
/*policy*/ CFI_ERR_SOFT,
/*retries*/ 0,
/*orig_lun_io*/lun_io,
/*done_function*/ cfi_err_recovery_done);
new_lun_io = (struct cfi_lun_io *)
new_io->io_hdr.port_priv;
mtx_lock(&lun->softc->lock);
STAILQ_INSERT_TAIL(&lun->io_list, new_lun_io, links);
mtx_unlock(&lun->softc->lock);
io = new_io;
break;
}
case CFI_ERR_RETRY:
default:
if ((error_action & CFI_ERR_NO_DECREMENT) == 0)
io->io_hdr.retries--;
break;
}
old_status = io->io_hdr.status;
io->io_hdr.status = CTL_STATUS_NONE;
#if 0
io->io_hdr.flags &= ~CTL_FLAG_ALREADY_DONE;
#endif
io->io_hdr.flags &= ~CTL_FLAG_ABORT;
io->io_hdr.flags &= ~CTL_FLAG_SENT_2OTHER_SC;
if (ctl_queue(io) != CTL_RETVAL_COMPLETE) {
printf("%s: error returned from ctl_queue()!\n",
__func__);
io->io_hdr.status = old_status;
} else
return;
}
done:
lun_io->done_function(io);
}
static void
cfi_lun_probe_done(union ctl_io *io)
{
struct cfi_lun *lun;
struct cfi_lun_io *lun_io;
lun_io = (struct cfi_lun_io *)
io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
lun = lun_io->lun;
switch (lun->state) {
case CFI_LUN_INQUIRY: {
if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS) {
/* print out something here?? */
printf("%s: LUN %d probe failed because inquiry "
"failed\n", __func__, lun->lun_id);
ctl_io_error_print(io, NULL);
} else {
if (SID_TYPE(&lun->inq_data) != T_DIRECT) {
char path_str[40];
lun->state = CFI_LUN_READY;
ctl_scsi_path_string(io, path_str,
sizeof(path_str));
printf("%s", path_str);
scsi_print_inquiry(&lun->inq_data);
} else {
lun->state = CFI_LUN_READCAPACITY;
cfi_lun_probe(lun, /*have_lock*/ 0);
}
}
mtx_lock(&lun->softc->lock);
STAILQ_REMOVE(&lun->io_list, lun_io, cfi_lun_io, links);
mtx_unlock(&lun->softc->lock);
ctl_free_io(io);
break;
}
case CFI_LUN_READCAPACITY:
case CFI_LUN_READCAPACITY_16: {
uint64_t maxlba;
uint32_t blocksize;
maxlba = 0;
blocksize = 0;
if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS) {
printf("%s: LUN %d probe failed because READ CAPACITY "
"failed\n", __func__, lun->lun_id);
ctl_io_error_print(io, NULL);
} else {
if (lun->state == CFI_LUN_READCAPACITY) {
struct scsi_read_capacity_data *rdcap;
rdcap = (struct scsi_read_capacity_data *)
io->scsiio.ext_data_ptr;
maxlba = scsi_4btoul(rdcap->addr);
blocksize = scsi_4btoul(rdcap->length);
if (blocksize == 0) {
printf("%s: LUN %d has invalid "
"blocksize 0, probe aborted\n",
__func__, lun->lun_id);
} else if (maxlba == 0xffffffff) {
lun->state = CFI_LUN_READCAPACITY_16;
cfi_lun_probe(lun, /*have_lock*/ 0);
} else
lun->state = CFI_LUN_READY;
} else {
struct scsi_read_capacity_data_long *rdcap_long;
rdcap_long = (struct
scsi_read_capacity_data_long *)
io->scsiio.ext_data_ptr;
maxlba = scsi_8btou64(rdcap_long->addr);
blocksize = scsi_4btoul(rdcap_long->length);
if (blocksize == 0) {
printf("%s: LUN %d has invalid "
"blocksize 0, probe aborted\n",
__func__, lun->lun_id);
} else
lun->state = CFI_LUN_READY;
}
}
if (lun->state == CFI_LUN_READY) {
char path_str[40];
lun->num_blocks = maxlba + 1;
lun->blocksize = blocksize;
/*
* If this is true, the blocksize is a power of 2.
* We already checked for 0 above.
*/
if (((blocksize - 1) & blocksize) == 0) {
int i;
for (i = 0; i < 32; i++) {
if ((blocksize & (1 << i)) != 0) {
lun->blocksize_powerof2 = i;
break;
}
}
}
ctl_scsi_path_string(io, path_str,sizeof(path_str));
printf("%s", path_str);
scsi_print_inquiry(&lun->inq_data);
printf("%s %ju blocks, blocksize %d\n", path_str,
(uintmax_t)maxlba + 1, blocksize);
}
mtx_lock(&lun->softc->lock);
STAILQ_REMOVE(&lun->io_list, lun_io, cfi_lun_io, links);
mtx_unlock(&lun->softc->lock);
free(io->scsiio.ext_data_ptr, M_CTL_CFI);
ctl_free_io(io);
break;
}
case CFI_LUN_READY:
default:
mtx_lock(&lun->softc->lock);
/* How did we get here?? */
STAILQ_REMOVE(&lun->io_list, lun_io, cfi_lun_io, links);
mtx_unlock(&lun->softc->lock);
ctl_free_io(io);
break;
}
}
static void
cfi_lun_probe(struct cfi_lun *lun, int have_lock)
{
if (have_lock == 0)
mtx_lock(&lun->softc->lock);
if ((lun->softc->flags & CFI_ONLINE) == 0) {
if (have_lock == 0)
mtx_unlock(&lun->softc->lock);
return;
}
if (have_lock == 0)
mtx_unlock(&lun->softc->lock);
switch (lun->state) {
case CFI_LUN_INQUIRY: {
struct cfi_lun_io *lun_io;
union ctl_io *io;
io = ctl_alloc_io(lun->softc->fe.ctl_pool_ref);
if (io == NULL) {
printf("%s: unable to alloc ctl_io for target %ju "
"lun %d probe\n", __func__,
(uintmax_t)lun->target_id.id, lun->lun_id);
return;
}
ctl_scsi_inquiry(io,
/*data_ptr*/(uint8_t *)&lun->inq_data,
/*data_len*/ sizeof(lun->inq_data),
/*byte2*/ 0,
/*page_code*/ 0,
/*tag_type*/ CTL_TAG_SIMPLE,
/*control*/ 0);
cfi_init_io(io,
/*lun*/ lun,
/*metatask*/ NULL,
/*policy*/ CFI_ERR_SOFT,
/*retries*/ 5,
/*orig_lun_io*/ NULL,
/*done_function*/
cfi_lun_probe_done);
lun_io = (struct cfi_lun_io *)io->io_hdr.port_priv;
if (have_lock == 0)
mtx_lock(&lun->softc->lock);
STAILQ_INSERT_TAIL(&lun->io_list, lun_io, links);
if (have_lock == 0)
mtx_unlock(&lun->softc->lock);
if (ctl_queue(io) != CTL_RETVAL_COMPLETE) {
printf("%s: error returned from ctl_queue()!\n",
__func__);
STAILQ_REMOVE(&lun->io_list, lun_io,
cfi_lun_io, links);
ctl_free_io(io);
}
break;
}
case CFI_LUN_READCAPACITY:
case CFI_LUN_READCAPACITY_16: {
struct cfi_lun_io *lun_io;
uint8_t *dataptr;
union ctl_io *io;
io = ctl_alloc_io(lun->softc->fe.ctl_pool_ref);
if (io == NULL) {
printf("%s: unable to alloc ctl_io for target %ju "
"lun %d probe\n", __func__,
(uintmax_t)lun->target_id.id, lun->lun_id);
return;
}
dataptr = malloc(sizeof(struct scsi_read_capacity_data_long),
M_CTL_CFI, M_NOWAIT);
if (dataptr == NULL) {
printf("%s: unable to allocate SCSI read capacity "
"buffer for target %ju lun %d\n", __func__,
(uintmax_t)lun->target_id.id, lun->lun_id);
return;
}
if (lun->state == CFI_LUN_READCAPACITY) {
ctl_scsi_read_capacity(io,
/*data_ptr*/ dataptr,
/*data_len*/
sizeof(struct scsi_read_capacity_data_long),
/*addr*/ 0,
/*reladr*/ 0,
/*pmi*/ 0,
/*tag_type*/ CTL_TAG_SIMPLE,
/*control*/ 0);
} else {
ctl_scsi_read_capacity_16(io,
/*data_ptr*/ dataptr,
/*data_len*/
sizeof(struct scsi_read_capacity_data_long),
/*addr*/ 0,
/*reladr*/ 0,
/*pmi*/ 0,
/*tag_type*/ CTL_TAG_SIMPLE,
/*control*/ 0);
}
cfi_init_io(io,
/*lun*/ lun,
/*metatask*/ NULL,
/*policy*/ CFI_ERR_SOFT,
/*retries*/ 7,
/*orig_lun_io*/ NULL,
/*done_function*/ cfi_lun_probe_done);
lun_io = (struct cfi_lun_io *)io->io_hdr.port_priv;
if (have_lock == 0)
mtx_lock(&lun->softc->lock);
STAILQ_INSERT_TAIL(&lun->io_list, lun_io, links);
if (have_lock == 0)
mtx_unlock(&lun->softc->lock);
if (ctl_queue(io) != CTL_RETVAL_COMPLETE) {
printf("%s: error returned from ctl_queue()!\n",
__func__);
STAILQ_REMOVE(&lun->io_list, lun_io,
cfi_lun_io, links);
free(dataptr, M_CTL_CFI);
ctl_free_io(io);
}
break;
}
case CFI_LUN_READY:
default:
/* Why were we called? */
break;
}
}
static void
cfi_metatask_done(struct cfi_softc *softc, struct cfi_metatask *metatask)
{
mtx_lock(&softc->lock);
STAILQ_REMOVE(&softc->metatask_list, metatask, cfi_metatask, links);
mtx_unlock(&softc->lock);
/*
* Return status to the caller. Caller allocated storage, and is
* responsible for calling cfi_free_metatask to release it once
* they've seen the status.
*/
metatask->callback(metatask->callback_arg, metatask);
}
static void
cfi_metatask_bbr_errorparse(struct cfi_metatask *metatask, union ctl_io *io)
{
int error_code, sense_key, asc, ascq;
if (metatask->tasktype != CFI_TASK_BBRREAD)
return;
if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
metatask->status = CFI_MT_SUCCESS;
metatask->taskinfo.bbrread.status = CFI_BBR_SUCCESS;
return;
}
if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SCSI_ERROR) {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status = CFI_BBR_ERROR;
return;
}
metatask->taskinfo.bbrread.scsi_status = io->scsiio.scsi_status;
memcpy(&metatask->taskinfo.bbrread.sense_data, &io->scsiio.sense_data,
ctl_min(sizeof(metatask->taskinfo.bbrread.sense_data),
sizeof(io->scsiio.sense_data)));
if (io->scsiio.scsi_status == SCSI_STATUS_RESERV_CONFLICT) {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status = CFI_BBR_RESERV_CONFLICT;
return;
}
if (io->scsiio.scsi_status != SCSI_STATUS_CHECK_COND) {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status = CFI_BBR_SCSI_ERROR;
return;
}
scsi_extract_sense_len(&io->scsiio.sense_data,
io->scsiio.sense_len,
&error_code,
&sense_key,
&asc,
&ascq,
/*show_errors*/ 1);
switch (error_code) {
case SSD_DEFERRED_ERROR:
case SSD_DESC_DEFERRED_ERROR:
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status = CFI_BBR_SCSI_ERROR;
break;
case SSD_CURRENT_ERROR:
case SSD_DESC_CURRENT_ERROR:
default: {
struct scsi_sense_data *sense;
sense = &io->scsiio.sense_data;
if ((asc == 0x04) && (ascq == 0x02)) {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status = CFI_BBR_LUN_STOPPED;
} else if ((asc == 0x04) && (ascq == 0x03)) {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status =
CFI_BBR_LUN_OFFLINE_CTL;
} else if ((asc == 0x44) && (ascq == 0x00)) {
#ifdef NEEDTOPORT
if (sense->sense_key_spec[0] & SSD_SCS_VALID) {
uint16_t retry_count;
retry_count = sense->sense_key_spec[1] << 8 |
sense->sense_key_spec[2];
if (((retry_count & 0xf000) == CSC_RAIDCORE)
&& ((retry_count & 0x0f00) == CSC_SHELF_SW)
&& ((retry_count & 0xff) ==
RC_STS_DEVICE_OFFLINE)) {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status =
CFI_BBR_LUN_OFFLINE_RC;
} else {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status =
CFI_BBR_SCSI_ERROR;
}
} else {
#endif /* NEEDTOPORT */
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status =
CFI_BBR_SCSI_ERROR;
#ifdef NEEDTOPORT
}
#endif
} else {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status = CFI_BBR_SCSI_ERROR;
}
break;
}
}
}
static void
cfi_metatask_io_done(union ctl_io *io)
{
struct cfi_lun_io *lun_io;
struct cfi_metatask *metatask;
struct cfi_softc *softc;
struct cfi_lun *lun;
lun_io = (struct cfi_lun_io *)
io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
lun = lun_io->lun;
softc = lun->softc;
metatask = lun_io->metatask;
switch (metatask->tasktype) {
case CFI_TASK_STARTUP:
case CFI_TASK_SHUTDOWN: {
int failed, done, is_start;
failed = 0;
done = 0;
if (metatask->tasktype == CFI_TASK_STARTUP)
is_start = 1;
else
is_start = 0;
mtx_lock(&softc->lock);
if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS)
metatask->taskinfo.startstop.luns_complete++;
else {
metatask->taskinfo.startstop.luns_failed++;
failed = 1;
}
if ((metatask->taskinfo.startstop.luns_complete +
metatask->taskinfo.startstop.luns_failed) >=
metatask->taskinfo.startstop.total_luns)
done = 1;
mtx_unlock(&softc->lock);
if (failed != 0) {
printf("%s: LUN %d %s request failed\n", __func__,
lun_io->lun->lun_id, (is_start == 1) ? "start" :
"stop");
ctl_io_error_print(io, &lun_io->lun->inq_data);
}
if (done != 0) {
if (metatask->taskinfo.startstop.luns_failed > 0)
metatask->status = CFI_MT_ERROR;
else
metatask->status = CFI_MT_SUCCESS;
cfi_metatask_done(softc, metatask);
}
mtx_lock(&softc->lock);
STAILQ_REMOVE(&lun->io_list, lun_io, cfi_lun_io, links);
mtx_unlock(&softc->lock);
ctl_free_io(io);
break;
}
case CFI_TASK_BBRREAD: {
/*
* Translate the SCSI error into an enumeration.
*/
cfi_metatask_bbr_errorparse(metatask, io);
mtx_lock(&softc->lock);
STAILQ_REMOVE(&lun->io_list, lun_io, cfi_lun_io, links);
mtx_unlock(&softc->lock);
ctl_free_io(io);
cfi_metatask_done(softc, metatask);
break;
}
default:
/*
* This shouldn't happen.
*/
mtx_lock(&softc->lock);
STAILQ_REMOVE(&lun->io_list, lun_io, cfi_lun_io, links);
mtx_unlock(&softc->lock);
ctl_free_io(io);
break;
}
}
static void
cfi_err_recovery_done(union ctl_io *io)
{
struct cfi_lun_io *lun_io, *orig_lun_io;
struct cfi_lun *lun;
union ctl_io *orig_io;
lun_io = (struct cfi_lun_io *)io->io_hdr.port_priv;
orig_lun_io = lun_io->orig_lun_io;
orig_io = orig_lun_io->ctl_io;
lun = lun_io->lun;
if (io->io_hdr.status != CTL_SUCCESS) {
printf("%s: error recovery action failed. Original "
"error:\n", __func__);
ctl_io_error_print(orig_lun_io->ctl_io, &lun->inq_data);
printf("%s: error from error recovery action:\n", __func__);
ctl_io_error_print(io, &lun->inq_data);
printf("%s: trying original command again...\n", __func__);
}
mtx_lock(&lun->softc->lock);
STAILQ_REMOVE(&lun->io_list, lun_io, cfi_lun_io, links);
mtx_unlock(&lun->softc->lock);
ctl_free_io(io);
orig_io->io_hdr.retries--;
orig_io->io_hdr.status = CTL_STATUS_NONE;
if (ctl_queue(orig_io) != CTL_RETVAL_COMPLETE) {
printf("%s: error returned from ctl_queue()!\n", __func__);
STAILQ_REMOVE(&lun->io_list, orig_lun_io,
cfi_lun_io, links);
ctl_free_io(orig_io);
}
}
static void
cfi_lun_io_done(union ctl_io *io)
{
struct cfi_lun *lun;
struct cfi_lun_io *lun_io;
lun_io = (struct cfi_lun_io *)
io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
lun = lun_io->lun;
if (lun_io->metatask == NULL) {
printf("%s: I/O has no metatask pointer, discarding\n",
__func__);
STAILQ_REMOVE(&lun->io_list, lun_io, cfi_lun_io, links);
ctl_free_io(io);
return;
}
cfi_metatask_io_done(io);
}
void
cfi_action(struct cfi_metatask *metatask)
{
struct cfi_softc *softc;
softc = &fetd_internal_softc;
mtx_lock(&softc->lock);
STAILQ_INSERT_TAIL(&softc->metatask_list, metatask, links);
if ((softc->flags & CFI_ONLINE) == 0) {
mtx_unlock(&softc->lock);
metatask->status = CFI_MT_PORT_OFFLINE;
cfi_metatask_done(softc, metatask);
return;
} else
mtx_unlock(&softc->lock);
switch (metatask->tasktype) {
case CFI_TASK_STARTUP:
case CFI_TASK_SHUTDOWN: {
union ctl_io *io;
int da_luns, ios_allocated, do_start;
struct cfi_lun *lun;
STAILQ_HEAD(, ctl_io_hdr) tmp_io_list;
da_luns = 0;
ios_allocated = 0;
STAILQ_INIT(&tmp_io_list);
if (metatask->tasktype == CFI_TASK_STARTUP)
do_start = 1;
else
do_start = 0;
mtx_lock(&softc->lock);
STAILQ_FOREACH(lun, &softc->lun_list, links) {
if (lun->state != CFI_LUN_READY)
continue;
if (SID_TYPE(&lun->inq_data) != T_DIRECT)
continue;
da_luns++;
io = ctl_alloc_io(softc->fe.ctl_pool_ref);
if (io != NULL) {
ios_allocated++;
STAILQ_INSERT_TAIL(&tmp_io_list, &io->io_hdr,
links);
}
}
if (ios_allocated < da_luns) {
printf("%s: error allocating ctl_io for %s\n",
__func__, (do_start == 1) ? "startup" :
"shutdown");
da_luns = ios_allocated;
}
metatask->taskinfo.startstop.total_luns = da_luns;
STAILQ_FOREACH(lun, &softc->lun_list, links) {
struct cfi_lun_io *lun_io;
if (lun->state != CFI_LUN_READY)
continue;
if (SID_TYPE(&lun->inq_data) != T_DIRECT)
continue;
io = (union ctl_io *)STAILQ_FIRST(&tmp_io_list);
if (io == NULL)
break;
STAILQ_REMOVE(&tmp_io_list, &io->io_hdr, ctl_io_hdr,
links);
ctl_scsi_start_stop(io,
/*start*/ do_start,
/*load_eject*/ 0,
/*immediate*/ 0,
/*power_conditions*/
SSS_PC_START_VALID,
/*onoffline*/ 1,
/*ctl_tag_type*/ CTL_TAG_ORDERED,
/*control*/ 0);
cfi_init_io(io,
/*lun*/ lun,
/*metatask*/ metatask,
/*policy*/ CFI_ERR_HARD,
/*retries*/ 3,
/*orig_lun_io*/ NULL,
/*done_function*/ cfi_lun_io_done);
lun_io = (struct cfi_lun_io *) io->io_hdr.port_priv;
STAILQ_INSERT_TAIL(&lun->io_list, lun_io, links);
if (ctl_queue(io) != CTL_RETVAL_COMPLETE) {
printf("%s: error returned from ctl_queue()!\n",
__func__);
STAILQ_REMOVE(&lun->io_list, lun_io,
cfi_lun_io, links);
ctl_free_io(io);
metatask->taskinfo.startstop.total_luns--;
}
}
if (STAILQ_FIRST(&tmp_io_list) != NULL) {
printf("%s: error: tmp_io_list != NULL\n", __func__);
for (io = (union ctl_io *)STAILQ_FIRST(&tmp_io_list);
io != NULL;
io = (union ctl_io *)STAILQ_FIRST(&tmp_io_list)) {
STAILQ_REMOVE(&tmp_io_list, &io->io_hdr,
ctl_io_hdr, links);
ctl_free_io(io);
}
}
mtx_unlock(&softc->lock);
break;
}
case CFI_TASK_BBRREAD: {
union ctl_io *io;
struct cfi_lun *lun;
struct cfi_lun_io *lun_io;
cfi_bbrread_status status;
int req_lun_num;
uint32_t num_blocks;
status = CFI_BBR_SUCCESS;
req_lun_num = metatask->taskinfo.bbrread.lun_num;
mtx_lock(&softc->lock);
STAILQ_FOREACH(lun, &softc->lun_list, links) {
if (lun->lun_id != req_lun_num)
continue;
if (lun->state != CFI_LUN_READY) {
status = CFI_BBR_LUN_UNCONFIG;
break;
} else
break;
}
if (lun == NULL)
status = CFI_BBR_NO_LUN;
if (status != CFI_BBR_SUCCESS) {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status = status;
mtx_unlock(&softc->lock);
cfi_metatask_done(softc, metatask);
break;
}
/*
* Convert the number of bytes given into blocks and check
* that the number of bytes is a multiple of the blocksize.
* CTL will verify that the LBA is okay.
*/
if (lun->blocksize_powerof2 != 0) {
if ((metatask->taskinfo.bbrread.len &
(lun->blocksize - 1)) != 0) {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status =
CFI_BBR_BAD_LEN;
cfi_metatask_done(softc, metatask);
break;
}
num_blocks = metatask->taskinfo.bbrread.len >>
lun->blocksize_powerof2;
} else {
/*
* XXX KDM this could result in floating point
* division, which isn't supported in the kernel on
* x86 at least.
*/
if ((metatask->taskinfo.bbrread.len %
lun->blocksize) != 0) {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status =
CFI_BBR_BAD_LEN;
cfi_metatask_done(softc, metatask);
break;
}
/*
* XXX KDM this could result in floating point
* division in some cases.
*/
num_blocks = metatask->taskinfo.bbrread.len /
lun->blocksize;
}
io = ctl_alloc_io(softc->fe.ctl_pool_ref);
if (io == NULL) {
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status = CFI_BBR_NO_MEM;
mtx_unlock(&softc->lock);
cfi_metatask_done(softc, metatask);
break;
}
/*
* XXX KDM need to do a read capacity to get the blocksize
* for this device.
*/
ctl_scsi_read_write(io,
/*data_ptr*/ NULL,
/*data_len*/ metatask->taskinfo.bbrread.len,
/*read_op*/ 1,
/*byte2*/ 0,
/*minimum_cdb_size*/ 0,
/*lba*/ metatask->taskinfo.bbrread.lba,
/*num_blocks*/ num_blocks,
/*tag_type*/ CTL_TAG_SIMPLE,
/*control*/ 0);
cfi_init_io(io,
/*lun*/ lun,
/*metatask*/ metatask,
/*policy*/ CFI_ERR_SOFT,
/*retries*/ 3,
/*orig_lun_io*/ NULL,
/*done_function*/ cfi_lun_io_done);
lun_io = (struct cfi_lun_io *)io->io_hdr.port_priv;
STAILQ_INSERT_TAIL(&lun->io_list, lun_io, links);
if (ctl_queue(io) != CTL_RETVAL_COMPLETE) {
printf("%s: error returned from ctl_queue()!\n",
__func__);
STAILQ_REMOVE(&lun->io_list, lun_io, cfi_lun_io, links);
ctl_free_io(io);
metatask->status = CFI_MT_ERROR;
metatask->taskinfo.bbrread.status = CFI_BBR_ERROR;
mtx_unlock(&softc->lock);
cfi_metatask_done(softc, metatask);
break;
}
mtx_unlock(&softc->lock);
break;
}
default:
panic("invalid metatask type %d", metatask->tasktype);
break; /* NOTREACHED */
}
}
#ifdef oldapi
void
cfi_shutdown_shelf(cfi_cb_t callback, void *callback_arg)
{
struct ctl_mem_element *element;
struct cfi_softc *softc;
struct cfi_metatask *metatask;
softc = &fetd_internal_softc;
element = ctl_alloc_mem_element(&softc->metatask_pool, /*can_wait*/ 0);
if (element == NULL) {
callback(callback_arg,
/*status*/ CFI_MT_ERROR,
/*sluns_found*/ 0,
/*sluns_complete*/ 0,
/*sluns_failed*/ 0);
return;
}
metatask = (struct cfi_metatask *)element->bytes;
memset(metatask, 0, sizeof(*metatask));
metatask->tasktype = CFI_TASK_SHUTDOWN;
metatask->status = CFI_MT_NONE;
metatask->taskinfo.startstop.callback = callback;
metatask->taskinfo.startstop.callback_arg = callback_arg;
metatask->element = element;
cfi_action(softc, metatask);
/*
* - send a report luns to lun 0, get LUN list.
* - send an inquiry to each lun
* - send a stop/offline to each direct access LUN
* - if we get a reservation conflict, reset the LUN and then
* retry sending the stop/offline
* - return status back to the caller
*/
}
void
cfi_start_shelf(cfi_cb_t callback, void *callback_arg)
{
struct ctl_mem_element *element;
struct cfi_softc *softc;
struct cfi_metatask *metatask;
softc = &fetd_internal_softc;
element = ctl_alloc_mem_element(&softc->metatask_pool, /*can_wait*/ 0);
if (element == NULL) {
callback(callback_arg,
/*status*/ CFI_MT_ERROR,
/*sluns_found*/ 0,
/*sluns_complete*/ 0,
/*sluns_failed*/ 0);
return;
}
metatask = (struct cfi_metatask *)element->bytes;
memset(metatask, 0, sizeof(*metatask));
metatask->tasktype = CFI_TASK_STARTUP;
metatask->status = CFI_MT_NONE;
metatask->taskinfo.startstop.callback = callback;
metatask->taskinfo.startstop.callback_arg = callback_arg;
metatask->element = element;
cfi_action(softc, metatask);
/*
* - send a report luns to lun 0, get LUN list.
* - send an inquiry to each lun
* - send a stop/offline to each direct access LUN
* - if we get a reservation conflict, reset the LUN and then
* retry sending the stop/offline
* - return status back to the caller
*/
}
#endif
struct cfi_metatask *
cfi_alloc_metatask(int can_wait)
{
struct ctl_mem_element *element;
struct cfi_metatask *metatask;
struct cfi_softc *softc;
softc = &fetd_internal_softc;
element = ctl_alloc_mem_element(&softc->metatask_pool, can_wait);
if (element == NULL)
return (NULL);
metatask = (struct cfi_metatask *)element->bytes;
memset(metatask, 0, sizeof(*metatask));
metatask->status = CFI_MT_NONE;
metatask->element = element;
return (metatask);
}
void
cfi_free_metatask(struct cfi_metatask *metatask)
{
ctl_free_mem_element(metatask->element);
}
/*
* vim: ts=8
*/