root/usr/src/uts/common/io/comstar/stmf/stmf_impl.h
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
 * Copyright (c) 2013 by Delphix. All rights reserved.
 */
#ifndef _STMF_IMPL_H
#define _STMF_IMPL_H

#include <sys/stmf_defines.h>
#include <sys/stmf_ioctl.h>

#ifdef  __cplusplus
extern "C" {
#endif

typedef uint32_t stmf_event_handle_t;
#define STMF_MAX_NUM_EVENTS             (sizeof (stmf_event_handle_t) * 8)
#define STMF_EVENT_ADD(h, e)            (atomic_or_32(&(h), \
                                                ((uint32_t)1) << (e)))
#define STMF_EVENT_REMOVE(h, e)         (atomic_and_32(&(h), \
                                                ~(((uint32_t)1) << (e))))
#define STMF_EVENT_ENABLED(h, e)        (((h) & ((uint32_t)1) << (e)) != 0)
#define STMF_EVENT_CLEAR_ALL(h)         ((h) = 0)
#define STMF_EVENT_ALLOC_HANDLE(h)      ((h) = 0)
#define STMF_EVENT_FREE_HANDLE(h)       ((h) = 0)

#define STMF_TGT_NAME_LEN               256
#define STMF_GUID_INPUT                 32

#define STMF_UPDATE_KSTAT_IO(kip, dbuf)                                 \
        if (dbuf->db_flags & DB_DIRECTION_TO_RPORT) {                   \
                kip->reads++;                                           \
                kip->nread += dbuf->db_data_size;                       \
        } else {                                                        \
                kip->writes++;                                          \
                kip->nwritten += dbuf->db_data_size;                    \
        }

struct stmf_i_scsi_task;
struct stmf_itl_data;

typedef struct stmf_i_lu_provider {
        stmf_lu_provider_t      *ilp_lp;
        uint32_t                ilp_alloc_size;
        uint32_t                ilp_nlus;       /* # LUNs being exported */
        uint32_t                ilp_cb_in_progress:1,
                                ilp_rsvd:31;
        struct stmf_i_lu_provider *ilp_next;
        struct stmf_pp_data     *ilp_ppd;
} stmf_i_lu_provider_t;

typedef struct stmf_i_lu {
        stmf_lu_t       *ilu_lu;
        uint32_t        ilu_alloc_size;
        uint32_t        ilu_flags;
        uint32_t        ilu_ref_cnt;
        uint8_t         ilu_state;
        uint8_t         ilu_prev_state;
        uint8_t         ilu_access;
        uint8_t         ilu_alua;
        stmf_event_handle_t ilu_event_hdl;
        struct stmf_i_lu *ilu_next;
        struct stmf_i_lu *ilu_prev;
        char            *ilu_alias;
        char            ilu_ascii_hex_guid[STMF_GUID_INPUT + 1];
        kmutex_t        ilu_task_lock;
        uint32_t        ilu_task_cntr1;
        uint32_t        ilu_task_cntr2;
        uint32_t        *ilu_cur_task_cntr;
        uint32_t        ilu_ntasks;      /* # of tasks in the ilu_task list */
        uint32_t        ilu_ntasks_free;        /* # of tasks that are free */
        uint32_t        ilu_ntasks_min_free; /* # minimal free tasks */
        uint32_t        ilu_additional_ref;
        uint32_t        ilu_proxy_registered;
        uint64_t        ilu_reg_msgid;
        struct stmf_i_scsi_task *ilu_tasks;
        struct stmf_i_scsi_task *ilu_free_tasks;
        struct stmf_itl_data    *ilu_itl_list;
        kstat_t         *ilu_kstat_info;
        kstat_t         *ilu_kstat_io;
        kmutex_t        ilu_kstat_lock;
        kcondvar_t      ilu_offline_pending_cv;

        /* point to the luid entry in stmf_state.stmf_luid_list */
        void            *ilu_luid;
} stmf_i_lu_t;

/*
 * ilu_flags
 */
#define ILU_STALL_DEREGISTER            0x0001
#define ILU_RESET_ACTIVE                0x0002

typedef struct stmf_i_port_provider {
        stmf_port_provider_t    *ipp_pp;
        uint32_t                ipp_alloc_size;
        uint32_t                ipp_npps;
        uint32_t                ipp_cb_in_progress:1,
                                ipp_rsvd:31;
        struct stmf_i_port_provider *ipp_next;
        struct stmf_pp_data     *ipp_ppd;
} stmf_i_port_provider_t;

#define MAX_ILPORT                      0x10000

typedef struct stmf_i_local_port {
        stmf_local_port_t       *ilport_lport;
        uint32_t                ilport_alloc_size;
        uint32_t                ilport_nsessions;
        struct stmf_i_scsi_session *ilport_ss_list;
        krwlock_t               ilport_lock;
        struct stmf_i_local_port *ilport_next;
        struct stmf_i_local_port *ilport_prev;
        uint8_t                 ilport_state;
        uint8_t                 ilport_prev_state;
        uint8_t                 ilport_standby;
        uint8_t                 ilport_alua;
        uint16_t                ilport_rtpid; /* relative tpid */
        uint16_t                ilport_proxy_registered;
        uint64_t                ilport_reg_msgid;
        uint8_t                 ilport_no_standby_lu;
        uint32_t                ilport_unexpected_comp;
        stmf_event_handle_t     ilport_event_hdl;
        clock_t                 ilport_last_online_clock;
        clock_t                 ilport_avg_interval;
        uint32_t                ilport_online_times;
        uint32_t                ilport_flags;
        kstat_t                 *ilport_kstat_info;
        kstat_t                 *ilport_kstat_io;
        kmutex_t                ilport_kstat_lock;
        char                    ilport_kstat_tgt_name[STMF_TGT_NAME_LEN];
        /* which target group this port belongs to in stmf_state.stmf_tg_list */
        void                    *ilport_tg;
        id_t                    ilport_instance;
        /* XXX Need something to track all the remote ports also */
} stmf_i_local_port_t;

#define STMF_AVG_ONLINE_INTERVAL        (30 * drv_usectohz(1000000))

#define MAX_IRPORT                      0x10000

typedef struct stmf_i_remote_port {
        struct scsi_devid_desc  *irport_id;
        kmutex_t                irport_mutex;
        int                     irport_refcnt;
        id_t                    irport_instance;
        avl_node_t              irport_ln;
        /* number of active read tasks */
        uint32_t                irport_nread_tasks;
        /* number of active write tasks */
        uint32_t                irport_nwrite_tasks;
        hrtime_t                irport_rdstart_timestamp;
        hrtime_t                irport_rddone_timestamp;
        hrtime_t                irport_wrstart_timestamp;
        hrtime_t                irport_wrdone_timestamp;
        kstat_t                 *irport_kstat_info;
        kstat_t                 *irport_kstat_io;
        kstat_t                 *irport_kstat_estat;    /* extended stats */
        boolean_t               irport_info_dirty;
} stmf_i_remote_port_t;

/*
 * ilport flags
 */
#define ILPORT_FORCED_OFFLINE           0x01
#define ILPORT_SS_GOT_INITIAL_LUNS      0x02

typedef struct stmf_i_scsi_session {
        stmf_scsi_session_t     *iss_ss;
        uint32_t                iss_alloc_size;
        uint32_t                iss_flags;
        stmf_i_remote_port_t    *iss_irport;
        struct stmf_i_scsi_session *iss_next;
        /*
         * Ideally we should maintain 2 maps. One would indicate a new map
         * which will become available only upon receipt of a REPORT LUN
         * cmd.
         */
        struct stmf_lun_map     *iss_sm;
        /*
         * which host group the host of this session belongs to in
         * stmf_state.stmf_hg_list
         */
        void                    *iss_hg;
        krwlock_t               *iss_lockp;
        time_t                  iss_creation_time;
} stmf_i_scsi_session_t;

/*
 * iss flags
 */
#define ISS_LUN_INVENTORY_CHANGED               0x0001
#define ISS_RESET_ACTIVE                        0x0002
#define ISS_BEING_CREATED                       0x0004
#define ISS_GOT_INITIAL_LUNS                    0x0008
#define ISS_EVENT_ACTIVE                        0x0010
#define ISS_NULL_TPTID                          0x0020

#define ITASK_MAX_NCMDS                 14
#define ITASK_DEFAULT_POLL_TIMEOUT      0

#define ITASK_TASK_AUDIT_DEPTH          32 /* Must be a power of 2 */

typedef enum {
        TE_UNDEFINED,
        TE_TASK_START,
        TE_XFER_START,
        TE_XFER_DONE,
        TE_SEND_STATUS,
        TE_SEND_STATUS_DONE,
        TE_TASK_FREE,
        TE_TASK_ABORT,
        TE_TASK_LPORT_ABORTED,
        TE_TASK_LU_ABORTED,
        TE_PROCESS_CMD
} task_audit_event_t;

#define CMD_OR_IOF_NA   0xffffffff

typedef struct stmf_task_audit_rec {
        task_audit_event_t      ta_event;
        uint32_t                ta_cmd_or_iof;
        uint32_t                ta_itask_flags;
        stmf_data_buf_t         *ta_dbuf;
        timespec_t              ta_timestamp;
} stmf_task_audit_rec_t;

struct stmf_worker;
typedef struct stmf_i_scsi_task {
        scsi_task_t             *itask_task;
        uint32_t                itask_alloc_size;
        uint32_t                itask_flags;
        kmutex_t                itask_mutex; /* protects flags and lists */
        uint64_t                itask_proxy_msg_id;
        stmf_data_buf_t         *itask_proxy_dbuf;
        struct stmf_worker      *itask_worker;
        uint32_t                *itask_ilu_task_cntr;
        struct stmf_i_scsi_task *itask_worker_next;
        struct stmf_i_scsi_task *itask_lu_next;
        struct stmf_i_scsi_task *itask_lu_prev;
        struct stmf_i_scsi_task *itask_lu_free_next;
        struct stmf_itl_data    *itask_itl_datap;
        clock_t                 itask_start_time;       /* abort and normal */
        /* For now we only support 4 parallel buffers. Should be enough. */
        stmf_data_buf_t         *itask_dbufs[4];
        clock_t                 itask_poll_timeout;
        uint8_t                 itask_cmd_stack[ITASK_MAX_NCMDS];
        uint8_t                 itask_ncmds;
        uint8_t                 itask_allocated_buf_map;
        uint16_t                itask_cdb_buf_size;

        /* Task profile data */
        hrtime_t                itask_start_timestamp;
        hrtime_t                itask_done_timestamp;
        hrtime_t                itask_xfer_done_timestamp;
        hrtime_t                itask_waitq_enter_timestamp;
        hrtime_t                itask_waitq_time;
        hrtime_t                itask_lu_read_time;
        hrtime_t                itask_lu_write_time;
        hrtime_t                itask_lport_read_time;
        hrtime_t                itask_lport_write_time;
        uint64_t                itask_read_xfer;
        uint64_t                itask_write_xfer;
        kmutex_t                itask_audit_mutex;
        uint8_t                 itask_audit_index;
        stmf_task_audit_rec_t   itask_audit_records[ITASK_TASK_AUDIT_DEPTH];
} stmf_i_scsi_task_t;

#define ITASK_DEFAULT_ABORT_TIMEOUT     5

/*
 * Common code to encode an itask onto the worker_task queue is placed
 * in this macro to simplify future maintenace activity.
 */
#define STMF_ENQUEUE_ITASK(w, i) \
        ASSERT((itask->itask_flags & ITASK_IN_FREE_LIST) == 0); \
        ASSERT(mutex_owned(&itask->itask_mutex)); \
        ASSERT(mutex_owned(&w->worker_lock)); \
        i->itask_worker_next = NULL; \
        if (w->worker_task_tail) { \
                w->worker_task_tail->itask_worker_next = i; \
        } else { \
                w->worker_task_head = i; \
        } \
        w->worker_task_tail = i; \
        if (++(w->worker_queue_depth) > w->worker_max_qdepth_pu) { \
                w->worker_max_qdepth_pu = w->worker_queue_depth; \
        } \
        atomic_inc_32(&w->worker_ref_count); \
        atomic_or_32(&itask->itask_flags, ITASK_IN_WORKER_QUEUE); \
        i->itask_waitq_enter_timestamp = gethrtime(); \
        if ((w->worker_flags & STMF_WORKER_ACTIVE) == 0) \
                cv_signal(&w->worker_cv);

#define STMF_DEQUEUE_ITASK(w, itask) \
        ASSERT(mutex_owned(&w->worker_lock)); \
        if ((itask = w->worker_task_head) != NULL) { \
                w->worker_task_head = itask->itask_worker_next; \
                if (w->worker_task_head == NULL) { \
                        w->worker_task_tail = NULL; \
                } \
        } else { \
                w->worker_task_tail = NULL; \
        }

/*
 * itask_flags
 */
#define ITASK_IN_FREE_LIST              0x0001
#define ITASK_IN_TRANSITION             0x0002
#define ITASK_IN_WORKER_QUEUE           0x0004
#define ITASK_BEING_ABORTED             0x0008
#define ITASK_BEING_COMPLETED           0x0010
#define ITASK_KNOWN_TO_TGT_PORT         0x0020
#define ITASK_KNOWN_TO_LU               0x0040
#define ITASK_LU_ABORT_CALLED           0x0080
#define ITASK_TGT_PORT_ABORT_CALLED     0x0100
#define ITASK_DEFAULT_HANDLING          0x0200
#define ITASK_CAUSING_LU_RESET          0x0400
#define ITASK_CAUSING_TARGET_RESET      0x0800
#define ITASK_KSTAT_IN_RUNQ             0x1000
#define ITASK_PROXY_TASK                0x2000

/*
 * itask cmds.
 */
#define ITASK_CMD_MASK                  0x1F
#define ITASK_CMD_BUF_NDX(cmd)          (((uint8_t)(cmd)) >> 5)
#define ITASK_CMD_NEW_TASK              0x1
#define ITASK_CMD_DATA_XFER_DONE        0x2
#define ITASK_CMD_STATUS_DONE           0x3
#define ITASK_CMD_ABORT                 0x4
#define ITASK_CMD_SEND_STATUS           0x5
#define ITASK_CMD_POLL                  0x10
#define ITASK_CMD_POLL_LU               (ITASK_CMD_POLL | 1)
#define ITASK_CMD_POLL_LPORT            (ITASK_CMD_POLL | 2)

/*
 * struct maintained on a per itl basis when the lu registers ITL handle.
 */
typedef struct stmf_itl_data {
        uint32_t                        itl_counter;
        uint8_t                         itl_flags;
        uint8_t                         itl_hdlrm_reason;
        uint16_t                        itl_lun;
        void                            *itl_handle;
        struct stmf_i_lu                *itl_ilu;
        struct stmf_i_scsi_session      *itl_session;
        struct stmf_itl_data            *itl_next;
} stmf_itl_data_t;

/*
 * itl flags
 */
#define STMF_ITL_BEING_TERMINATED       0x01

/*
 * data structures to maintain provider private data.
 */
typedef struct stmf_pp_data {
        struct stmf_pp_data     *ppd_next;
        void                    *ppd_provider;
        nvlist_t                *ppd_nv;
        uint32_t                ppd_lu_provider:1,
                                ppd_port_provider:1,
                                ppd_rsvd:30;
        uint32_t                ppd_alloc_size;
        uint64_t                ppd_token;
        char                    ppd_name[8];
} stmf_pp_data_t;

typedef struct stmf_worker {
        kthread_t               *worker_tid;
        stmf_i_scsi_task_t      *worker_task_head;
        stmf_i_scsi_task_t      *worker_task_tail;
        stmf_i_scsi_task_t      *worker_wait_head;
        stmf_i_scsi_task_t      *worker_wait_tail;
        kmutex_t                worker_lock;
        kcondvar_t              worker_cv;
        uint32_t                worker_flags;
        uint32_t                worker_queue_depth;     /* ntasks cur queued */
        uint32_t                worker_max_qdepth_pu;   /* maxqd / unit time */
        uint32_t                worker_max_sys_qdepth_pu; /* for all workers */
        uint32_t                worker_ref_count;       /* # IOs referencing */
        hrtime_t                worker_signal_timestamp;
} stmf_worker_t;

/*
 * worker flags
 */
#define STMF_WORKER_STARTED             1
#define STMF_WORKER_ACTIVE              2
#define STMF_WORKER_TERMINATE           4

/*
 * data struct for managing transfers.
 */
typedef struct stmf_xfer_data {
        uint32_t        alloc_size;     /* Including this struct */
        uint32_t        size_done;
        uint32_t        size_left;
        uint8_t         buf[4];
} stmf_xfer_data_t;

/*
 * Define frequently used macros
 */
#define TASK_TO_ITASK(x_task)   \
        ((stmf_i_scsi_task_t *)(x_task)->task_stmf_private)

void stmf_dlun_init();
stmf_status_t stmf_dlun_fini();
void stmf_worker_init();
stmf_status_t stmf_worker_fini();
void stmf_task_free(scsi_task_t *task);
void stmf_do_task_abort(scsi_task_t *task);
void stmf_do_itl_dereg(stmf_lu_t *lu, stmf_itl_data_t *itl,
                uint8_t hdlrm_reason);
void stmf_generate_lu_event(stmf_i_lu_t *ilu, int eventid,
                                void *arg, uint32_t flags);
void stmf_generate_lport_event(stmf_i_local_port_t *ilport, int eventid,
                                                void *arg, uint32_t flags);

#ifdef  __cplusplus
}
#endif

#endif /* _STMF_IMPL_H */