freebsd-dev/sys/cam/scsi/scsi_enc_internal.h
Alexander Motin 90bcc81bc3 Delay GEOM disk_create() until CAM periph probe completes.
Before this patch CAM periph drivers called both disk_alloc() and
disk_create() same time on periph creation.  But then prevented disks
from opening until the periph probe completion with cam_periph_hold().
As result, especially if disk misbehaves during the probe, GEOM event
thread, triggered to taste the disk, got blocked on open attempt,
potentially for a long time, unable to process other events.

This patch moves disk_create() call from periph creation to the end of
the probe. To allow disk_create() calls from non-sleepable CAM contexts
some of its duties requiring memory allocations are moved either back
to disk_alloc() or forward to g_disk_create(), so now disk_alloc() and
disk_add_alias() are the only disk methods that require sleeping.  If
disk fails during the probe disk_create() may just be skipped, going
directly to disk_destroy().  Other method calls during that time are
just ignored.  Since GEOM may now see the disks after CAM bus scan is
already completed, introduce per-periph boot hold functions. Enclosure
driver already had such mechanism, so just generalize it.

Reviewed by:	imp
MFC after:	1 month
Sponsored by:	iXsystems, Inc.
Differential Revision:	https://reviews.freebsd.org/D35784
2022-07-14 16:17:36 -04:00

231 lines
7.2 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2000 Matthew Jacob
* 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.
*
* $FreeBSD$
*/
/*
* This file contains definitions only intended for use within
* sys/cam/scsi/scsi_enc*.c, and not in other kernel components.
*/
#ifndef __SCSI_ENC_INTERNAL_H__
#define __SCSI_ENC_INTERNAL_H__
#include <sys/sysctl.h>
typedef struct enc_element {
u_int elm_idx; /* index of element */
uint8_t elm_type; /* element type */
uint8_t subenclosure; /* subenclosure id */
uint8_t type_elm_idx; /* index of element within type */
uint8_t svalid; /* enclosure information valid */
uint8_t encstat[4]; /* state && stats */
u_int physical_path_len; /* Length of device path data. */
uint8_t *physical_path; /* Device physical path data. */
void *elm_private; /* per-type object data */
uint16_t priv;
} enc_element_t;
typedef enum {
ENC_NONE,
ENC_SES,
ENC_SES_PASSTHROUGH,
ENC_SAFT,
ENC_SEMB_SES,
ENC_SEMB_SAFT
} enctyp;
/* Platform Independent Driver Internal Definitions for enclosure devices. */
typedef struct enc_softc enc_softc_t;
struct enc_fsm_state;
typedef int fsm_fill_handler_t(enc_softc_t *ssc,
struct enc_fsm_state *state,
union ccb *ccb,
uint8_t *buf);
typedef int fsm_error_handler_t(union ccb *ccb, uint32_t cflags,
uint32_t sflags);
typedef int fsm_done_handler_t(enc_softc_t *ssc,
struct enc_fsm_state *state, union ccb *ccb,
uint8_t **bufp, int error, int xfer_len);
struct enc_fsm_state {
const char *name;
int page_code;
size_t buf_size;
uint32_t timeout;
fsm_fill_handler_t *fill;
fsm_done_handler_t *done;
fsm_error_handler_t *error;
};
typedef int (enc_softc_init_t)(enc_softc_t *);
typedef void (enc_softc_invalidate_t)(enc_softc_t *);
typedef void (enc_softc_cleanup_t)(enc_softc_t *);
typedef int (enc_init_enc_t)(enc_softc_t *);
typedef int (enc_set_enc_status_t)(enc_softc_t *, encioc_enc_status_t, int);
typedef int (enc_get_elm_status_t)(enc_softc_t *, encioc_elm_status_t *, int);
typedef int (enc_set_elm_status_t)(enc_softc_t *, encioc_elm_status_t *, int);
typedef int (enc_get_elm_desc_t)(enc_softc_t *, encioc_elm_desc_t *);
typedef int (enc_get_elm_devnames_t)(enc_softc_t *, encioc_elm_devnames_t *);
typedef int (enc_handle_string_t)(enc_softc_t *, encioc_string_t *,
unsigned long);
typedef void (enc_device_found_t)(enc_softc_t *);
typedef void (enc_poll_status_t)(enc_softc_t *);
struct enc_vec {
enc_softc_invalidate_t *softc_invalidate;
enc_softc_cleanup_t *softc_cleanup;
enc_init_enc_t *init_enc;
enc_set_enc_status_t *set_enc_status;
enc_get_elm_status_t *get_elm_status;
enc_set_elm_status_t *set_elm_status;
enc_get_elm_desc_t *get_elm_desc;
enc_get_elm_devnames_t *get_elm_devnames;
enc_handle_string_t *handle_string;
enc_device_found_t *device_found;
enc_poll_status_t *poll_status;
};
typedef struct enc_cache {
enc_element_t *elm_map; /* objects */
int nelms; /* number of objects */
encioc_enc_status_t enc_status; /* overall status */
void *private; /* per-type private data */
} enc_cache_t;
/* Enclosure instance toplevel structure */
struct enc_softc {
enctyp enc_type; /* type of enclosure */
struct enc_vec enc_vec; /* vector to handlers */
void *enc_private; /* per-type private data */
/**
* "Published" configuration and state data available to
* external consumers.
*/
enc_cache_t enc_cache;
/**
* Configuration and state data being actively updated
* by the enclosure daemon.
*/
enc_cache_t enc_daemon_cache;
struct sx enc_cache_lock;
uint8_t enc_flags;
#define ENC_FLAG_INVALID 0x01
#define ENC_FLAG_INITIALIZED 0x02
#define ENC_FLAG_SHUTDOWN 0x04
struct cdev *enc_dev;
struct cam_periph *periph;
int open_count;
/* Bitmap of pending operations. */
uint32_t pending_actions;
/* The action on which the state machine is currently working. */
uint32_t current_action;
#define ENC_UPDATE_NONE 0x00
#define ENC_UPDATE_INVALID 0xff
/* Callout for auto-updating enclosure status */
struct callout status_updater;
struct proc *enc_daemon;
struct enc_fsm_state *enc_fsm_states;
#define ENC_ANNOUNCE_SZ 400
char announce_buf[ENC_ANNOUNCE_SZ];
};
static inline enc_cache_t *
enc_other_cache(enc_softc_t *enc, enc_cache_t *primary)
{
return (primary == &enc->enc_cache
? &enc->enc_daemon_cache : &enc->enc_cache);
}
/* SES Management mode page - SES2r20 Table 59 */
struct ses_mgmt_mode_page {
struct scsi_mode_header_6 header;
struct scsi_mode_blk_desc blk_desc;
uint8_t byte0; /* ps : 1, spf : 1, page_code : 6 */
#define SES_MGMT_MODE_PAGE_CODE 0x14
uint8_t length;
#define SES_MGMT_MODE_PAGE_LEN 6
uint8_t reserved[3];
uint8_t byte5; /* reserved : 7, enbltc : 1 */
#define SES_MGMT_TIMED_COMP_EN 0x1
uint8_t max_comp_time[2];
};
/* Enclosure core interface for sub-drivers */
int enc_runcmd(struct enc_softc *, char *, int, char *, int *);
void enc_log(struct enc_softc *, const char *, ...);
int enc_error(union ccb *, uint32_t, uint32_t);
void enc_update_request(enc_softc_t *, uint32_t);
/* SES Native interface */
enc_softc_init_t ses_softc_init;
/* SAF-TE interface */
enc_softc_init_t safte_softc_init;
SYSCTL_DECL(_kern_cam_enc);
extern int enc_verbose;
/* Helper macros */
MALLOC_DECLARE(M_SCSIENC);
#define ENC_CFLAGS CAM_RETRY_SELTO
#define ENC_FLAGS SF_NO_PRINT | SF_RETRY_UA
#define STRNCMP strncmp
#define PRINTF printf
#define ENC_LOG enc_log
#if defined(DEBUG) || defined(ENC_DEBUG)
#define ENC_DLOG enc_log
#else
#define ENC_DLOG if (0) enc_log
#endif
#define ENC_VLOG if (enc_verbose) enc_log
#define ENC_MALLOC(amt) malloc(amt, M_SCSIENC, M_NOWAIT)
#define ENC_MALLOCZ(amt) malloc(amt, M_SCSIENC, M_ZERO|M_NOWAIT)
/* Cast away const avoiding GCC warnings. */
#define ENC_FREE(ptr) free((void *)((uintptr_t)ptr), M_SCSIENC)
#define ENC_FREE_AND_NULL(ptr) do { \
if (ptr != NULL) { \
ENC_FREE(ptr); \
ptr = NULL; \
} \
} while(0)
#define MEMZERO bzero
#define MEMCPY(dest, src, amt) bcopy(src, dest, amt)
#endif /* __SCSI_ENC_INTERNAL_H__ */