scsi: mpi3mr: Add support for device add/remove event handling

Firmware can report various MPI Events. Enable support for processing the
following events related to device addition/removal to the driver:

 - MPI3_EVENT_DEVICE_ADDED
 - MPI3_EVENT_DEVICE_INFO_CHANGED
 - MPI3_EVENT_DEVICE_STATUS_CHANGE
 - MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE
 - MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST
 - MPI3_EVENT_SAS_DISCOVERY
 - MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR

Link: https://lore.kernel.org/r/20210520152545.2710479-7-kashyap.desai@broadcom.com
Cc: sathya.prakash@broadcom.com
Reported-by: kernel test robot <lkp@intel.com>
Reviewed-by: Tomas Henzl <thenzl@redhat.com>
Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Kashyap Desai <kashyap.desai@broadcom.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Kashyap Desai 2021-05-20 20:55:27 +05:30 committed by Martin K. Petersen
parent 672ae26c82
commit 13ef29ea4a
5 changed files with 3768 additions and 3 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2016-2021 Broadcom Inc. All rights reserved.
*
*/
#ifndef MPI30_SAS_H
#define MPI30_SAS_H 1
#define MPI3_SAS_DEVICE_INFO_SSP_TARGET (0x00000100)
#define MPI3_SAS_DEVICE_INFO_STP_SATA_TARGET (0x00000080)
#define MPI3_SAS_DEVICE_INFO_SMP_TARGET (0x00000040)
#define MPI3_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000020)
#define MPI3_SAS_DEVICE_INFO_STP_INITIATOR (0x00000010)
#define MPI3_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000008)
#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK (0x00000007)
#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_NO_DEVICE (0x00000000)
#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_END_DEVICE (0x00000001)
#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_EXPANDER (0x00000002)
struct mpi3_smp_passthrough_request {
__le16 host_tag;
u8 ioc_use_only02;
u8 function;
__le16 ioc_use_only04;
u8 ioc_use_only06;
u8 msg_flags;
__le16 change_count;
u8 reserved0a;
u8 io_unit_port;
__le32 reserved0c[3];
__le64 sas_address;
struct mpi3_sge_common request_sge;
struct mpi3_sge_common response_sge;
};
#endif

View file

@ -40,9 +40,11 @@
#include <scsi/scsi_tcq.h>
#include "mpi/mpi30_transport.h"
#include "mpi/mpi30_cnfg.h"
#include "mpi/mpi30_image.h"
#include "mpi/mpi30_init.h"
#include "mpi/mpi30_ioc.h"
#include "mpi/mpi30_sas.h"
#include "mpi3mr_debug.h"
/* Global list and lock for storing multiple adapters managed by the driver */
@ -137,6 +139,10 @@ extern struct list_head mrioc_list;
#define MPI3MR_RSP_IO_QUEUED_ON_IOC \
MPI3_SCSITASKMGMT_RSPCODE_IO_QUEUED_ON_IOC
#define MPI3MR_DEFAULT_MDTS (128 * 1024)
/* Command retry count definitions */
#define MPI3MR_DEV_RMHS_RETRY_COUNT 3
/* SGE Flag definition */
#define MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST \
(MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | MPI3_SGE_FLAGS_DLAS_SYSTEM | \
@ -316,6 +322,126 @@ struct mpi3mr_intr_info {
char name[MPI3MR_NAME_LENGTH];
};
/**
* struct tgt_dev_sas_sata - SAS/SATA device specific
* information cached from firmware given data
*
* @sas_address: World wide unique SAS address
* @dev_info: Device information bits
*/
struct tgt_dev_sas_sata {
u64 sas_address;
u16 dev_info;
};
/**
* struct tgt_dev_pcie - PCIe device specific information cached
* from firmware given data
*
* @mdts: Maximum data transfer size
* @capb: Device capabilities
* @pgsz: Device page size
* @abort_to: Timeout for abort TM
* @reset_to: Timeout for Target/LUN reset TM
*/
struct tgt_dev_pcie {
u32 mdts;
u16 capb;
u8 pgsz;
u8 abort_to;
u8 reset_to;
};
/**
* struct tgt_dev_volume - virtual device specific information
* cached from firmware given data
*
* @state: State of the VD
*/
struct tgt_dev_volume {
u8 state;
};
/**
* union _form_spec_inf - union of device specific information
*/
union _form_spec_inf {
struct tgt_dev_sas_sata sas_sata_inf;
struct tgt_dev_pcie pcie_inf;
struct tgt_dev_volume vol_inf;
};
/**
* struct mpi3mr_tgt_dev - target device data structure
*
* @list: List pointer
* @starget: Scsi_target pointer
* @dev_handle: FW device handle
* @parent_handle: FW parent device handle
* @slot: Slot number
* @encl_handle: FW enclosure handle
* @perst_id: FW assigned Persistent ID
* @dev_type: SAS/SATA/PCIE device type
* @is_hidden: Should be exposed to upper layers or not
* @host_exposed: Already exposed to host or not
* @q_depth: Device specific Queue Depth
* @wwid: World wide ID
* @dev_spec: Device type specific information
* @ref_count: Reference count
*/
struct mpi3mr_tgt_dev {
struct list_head list;
struct scsi_target *starget;
u16 dev_handle;
u16 parent_handle;
u16 slot;
u16 encl_handle;
u16 perst_id;
u8 dev_type;
u8 is_hidden;
u8 host_exposed;
u16 q_depth;
u64 wwid;
union _form_spec_inf dev_spec;
struct kref ref_count;
};
/**
* mpi3mr_tgtdev_get - k reference incrementor
* @s: Target device reference
*
* Increment target device reference count.
*/
static inline void mpi3mr_tgtdev_get(struct mpi3mr_tgt_dev *s)
{
kref_get(&s->ref_count);
}
/**
* mpi3mr_free_tgtdev - target device memory dealloctor
* @r: k reference pointer of the target device
*
* Free target device memory when no reference.
*/
static inline void mpi3mr_free_tgtdev(struct kref *r)
{
kfree(container_of(r, struct mpi3mr_tgt_dev, ref_count));
}
/**
* mpi3mr_tgtdev_put - k reference decrementor
* @s: Target device reference
*
* Decrement target device reference count.
*/
static inline void mpi3mr_tgtdev_put(struct mpi3mr_tgt_dev *s)
{
kref_put(&s->ref_count, mpi3mr_free_tgtdev);
}
/**
* struct mpi3mr_stgt_priv_data - SCSI target private structure
*
@ -361,6 +487,7 @@ struct mpi3mr_sdev_priv_data {
* @done: Completeor for wakeup
* @reply: Firmware reply for internal commands
* @sensebuf: Sensebuf for SCSI IO commands
* @iou_rc: IO Unit control reason code
* @state: Command State
* @dev_handle: Firmware handle for device specific commands
* @ioc_status: IOC status from the firmware
@ -375,6 +502,7 @@ struct mpi3mr_drv_cmd {
struct completion done;
void *reply;
u8 *sensebuf;
u8 iou_rc;
u16 state;
u16 dev_handle;
u16 ioc_status;
@ -481,6 +609,11 @@ struct scmd_priv {
* @sense_buf_q_dma: Sense buffer queue DMA address
* @sbq_lock: Sense buffer queue lock
* @sbq_host_index: Sense buffer queuehost index
* @event_masks: Event mask bitmap
* @fwevt_worker_name: Firmware event worker thread name
* @fwevt_worker_thread: Firmware event worker thread
* @fwevt_lock: Firmware event lock
* @fwevt_list: Firmware event list
* @watchdog_work_q_name: Fault watchdog worker thread name
* @watchdog_work_q: Fault watchdog worker thread
* @watchdog_work: Fault watchdog work
@ -496,6 +629,12 @@ struct scmd_priv {
* @chain_bitmap_sz: Chain buffer allocator bitmap size
* @chain_bitmap: Chain buffer allocator bitmap
* @chain_buf_lock: Chain buffer list lock
* @dev_rmhs_cmds: Command tracker for device removal commands
* @devrem_bitmap_sz: Device removal bitmap size
* @devrem_bitmap: Device removal bitmap
* @dev_handle_bitmap_sz: Device handle bitmap size
* @removepend_bitmap: Remove pending bitmap
* @delayed_rmhs_list: Delayed device removal list
* @reset_in_progress: Reset in progress flag
* @unrecoverable: Controller unrecoverable flag
* @diagsave_timeout: Diagnostic information save timeout
@ -579,6 +718,12 @@ struct mpi3mr_ioc {
dma_addr_t sense_buf_q_dma;
spinlock_t sbq_lock;
u32 sbq_host_index;
u32 event_masks[MPI3_EVENT_NOTIFY_EVENTMASK_WORDS];
char fwevt_worker_name[MPI3MR_NAME_LENGTH];
struct workqueue_struct *fwevt_worker_thread;
spinlock_t fwevt_lock;
struct list_head fwevt_list;
char watchdog_work_q_name[20];
struct workqueue_struct *watchdog_work_q;
@ -591,6 +736,8 @@ struct mpi3mr_ioc {
u8 stop_drv_processing;
u16 max_host_ios;
spinlock_t tgtdev_lock;
struct list_head tgtdev_list;
u32 chain_buf_count;
struct dma_pool *chain_buf_pool;
@ -599,6 +746,13 @@ struct mpi3mr_ioc {
void *chain_bitmap;
spinlock_t chain_buf_lock;
struct mpi3mr_drv_cmd dev_rmhs_cmds[MPI3MR_NUM_DEVRMCMD];
u16 devrem_bitmap_sz;
void *devrem_bitmap;
u16 dev_handle_bitmap_sz;
void *removepend_bitmap;
struct list_head delayed_rmhs_list;
u8 reset_in_progress;
u8 unrecoverable;
@ -611,6 +765,45 @@ struct mpi3mr_ioc {
u16 op_reply_q_offset;
};
/**
* struct mpi3mr_fwevt - Firmware event structure.
*
* @list: list head
* @work: Work structure
* @mrioc: Adapter instance reference
* @event_id: MPI3 firmware event ID
* @send_ack: Event acknowledgment required or not
* @process_evt: Bottomhalf processing required or not
* @evt_ctx: Event context to send in Ack
* @ref_count: kref count
* @event_data: Actual MPI3 event data
*/
struct mpi3mr_fwevt {
struct list_head list;
struct work_struct work;
struct mpi3mr_ioc *mrioc;
u16 event_id;
bool send_ack;
bool process_evt;
u32 evt_ctx;
struct kref ref_count;
char event_data[0] __aligned(4);
};
/**
* struct delayed_dev_rmhs_node - Delayed device removal node
*
* @list: list head
* @handle: Device handle
* @iou_rc: IO Unit Control Reason Code
*/
struct delayed_dev_rmhs_node {
struct list_head list;
u16 handle;
u8 iou_rc;
};
int mpi3mr_setup_resources(struct mpi3mr_ioc *mrioc);
void mpi3mr_cleanup_resources(struct mpi3mr_ioc *mrioc);
int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc);
@ -630,6 +823,8 @@ void *mpi3mr_get_reply_virt_addr(struct mpi3mr_ioc *mrioc,
void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc,
u64 sense_buf_dma);
void mpi3mr_os_handle_events(struct mpi3mr_ioc *mrioc,
struct mpi3_event_notification_reply *event_reply);
void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc,
struct mpi3_default_reply_descriptor *reply_desc,
u64 *reply_dma, u16 qidx);
@ -642,5 +837,14 @@ void mpi3mr_ioc_disable_intr(struct mpi3mr_ioc *mrioc);
void mpi3mr_ioc_enable_intr(struct mpi3mr_ioc *mrioc);
enum mpi3mr_iocstate mpi3mr_get_iocstate(struct mpi3mr_ioc *mrioc);
int mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event,
u32 event_ctx);
void mpi3mr_wait_for_host_io(struct mpi3mr_ioc *mrioc, u32 timeout);
void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc);
void mpi3mr_flush_host_io(struct mpi3mr_ioc *mrioc);
void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc);
void mpi3mr_rfresh_tgtdevs(struct mpi3mr_ioc *mrioc);
void mpi3mr_flush_delayed_rmhs_list(struct mpi3mr_ioc *mrioc);
#endif /*MPI3MR_H_INCLUDED*/

View file

@ -160,12 +160,15 @@ static void mpi3mr_handle_events(struct mpi3mr_ioc *mrioc,
(struct mpi3_event_notification_reply *)def_reply;
mrioc->change_count = le16_to_cpu(event_reply->ioc_change_count);
mpi3mr_os_handle_events(mrioc, event_reply);
}
static struct mpi3mr_drv_cmd *
mpi3mr_get_drv_cmd(struct mpi3mr_ioc *mrioc, u16 host_tag,
struct mpi3_default_reply *def_reply)
{
u16 idx;
switch (host_tag) {
case MPI3MR_HOSTTAG_INITCMDS:
return &mrioc->init_cmds;
@ -177,6 +180,11 @@ mpi3mr_get_drv_cmd(struct mpi3mr_ioc *mrioc, u16 host_tag,
default:
break;
}
if (host_tag >= MPI3MR_HOSTTAG_DEVRMCMD_MIN &&
host_tag <= MPI3MR_HOSTTAG_DEVRMCMD_MAX) {
idx = host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN;
return &mrioc->dev_rmhs_cmds[idx];
}
return NULL;
}
@ -1910,6 +1918,13 @@ static int mpi3mr_alloc_reply_sense_bufs(struct mpi3mr_ioc *mrioc)
if (!mrioc->init_cmds.reply)
goto out_failed;
for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) {
mrioc->dev_rmhs_cmds[i].reply = kzalloc(mrioc->facts.reply_sz,
GFP_KERNEL);
if (!mrioc->dev_rmhs_cmds[i].reply)
goto out_failed;
}
mrioc->num_reply_bufs = mrioc->facts.max_reqs + MPI3MR_NUM_EVT_REPLIES;
mrioc->reply_free_qsz = mrioc->num_reply_bufs + 1;
mrioc->num_sense_bufs = mrioc->facts.max_reqs / MPI3MR_SENSEBUF_FACTOR;
@ -2119,6 +2134,163 @@ static int mpi3mr_issue_iocinit(struct mpi3mr_ioc *mrioc)
return retval;
}
/**
* mpi3mr_unmask_events - Unmask events in event mask bitmap
* @mrioc: Adapter instance reference
* @event: MPI event ID
*
* Un mask the specific event by resetting the event_mask
* bitmap.
*
* Return: 0 on success, non-zero on failures.
*/
static void mpi3mr_unmask_events(struct mpi3mr_ioc *mrioc, u16 event)
{
u32 desired_event;
u8 word;
if (event >= 128)
return;
desired_event = (1 << (event % 32));
word = event / 32;
mrioc->event_masks[word] &= ~desired_event;
}
/**
* mpi3mr_issue_event_notification - Send event notification
* @mrioc: Adapter instance reference
*
* Issue event notification MPI request through admin queue and
* wait for the completion of it or time out.
*
* Return: 0 on success, non-zero on failures.
*/
static int mpi3mr_issue_event_notification(struct mpi3mr_ioc *mrioc)
{
struct mpi3_event_notification_request evtnotify_req;
int retval = 0;
u8 i;
memset(&evtnotify_req, 0, sizeof(evtnotify_req));
mutex_lock(&mrioc->init_cmds.mutex);
if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
retval = -1;
ioc_err(mrioc, "Issue EvtNotify: Init command is in use\n");
mutex_unlock(&mrioc->init_cmds.mutex);
goto out;
}
mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
mrioc->init_cmds.is_waiting = 1;
mrioc->init_cmds.callback = NULL;
evtnotify_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
evtnotify_req.function = MPI3_FUNCTION_EVENT_NOTIFICATION;
for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
evtnotify_req.event_masks[i] =
cpu_to_le32(mrioc->event_masks[i]);
init_completion(&mrioc->init_cmds.done);
retval = mpi3mr_admin_request_post(mrioc, &evtnotify_req,
sizeof(evtnotify_req), 1);
if (retval) {
ioc_err(mrioc, "Issue EvtNotify: Admin Post failed\n");
goto out_unlock;
}
wait_for_completion_timeout(&mrioc->init_cmds.done,
(MPI3MR_INTADMCMD_TIMEOUT * HZ));
if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
ioc_err(mrioc, "Issue EvtNotify: command timed out\n");
mpi3mr_set_diagsave(mrioc);
mpi3mr_issue_reset(mrioc,
MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT);
mrioc->unrecoverable = 1;
retval = -1;
goto out_unlock;
}
if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
!= MPI3_IOCSTATUS_SUCCESS) {
ioc_err(mrioc,
"Issue EvtNotify: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
(mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
mrioc->init_cmds.ioc_loginfo);
retval = -1;
goto out_unlock;
}
out_unlock:
mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
mutex_unlock(&mrioc->init_cmds.mutex);
out:
return retval;
}
/**
* mpi3mr_send_event_ack - Send event acknowledgment
* @mrioc: Adapter instance reference
* @event: MPI3 event ID
* @event_ctx: Event context
*
* Send event acknowledgment through admin queue and wait for
* it to complete.
*
* Return: 0 on success, non-zero on failures.
*/
int mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event,
u32 event_ctx)
{
struct mpi3_event_ack_request evtack_req;
int retval = 0;
memset(&evtack_req, 0, sizeof(evtack_req));
mutex_lock(&mrioc->init_cmds.mutex);
if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
retval = -1;
ioc_err(mrioc, "Send EvtAck: Init command is in use\n");
mutex_unlock(&mrioc->init_cmds.mutex);
goto out;
}
mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
mrioc->init_cmds.is_waiting = 1;
mrioc->init_cmds.callback = NULL;
evtack_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
evtack_req.function = MPI3_FUNCTION_EVENT_ACK;
evtack_req.event = event;
evtack_req.event_context = cpu_to_le32(event_ctx);
init_completion(&mrioc->init_cmds.done);
retval = mpi3mr_admin_request_post(mrioc, &evtack_req,
sizeof(evtack_req), 1);
if (retval) {
ioc_err(mrioc, "Send EvtAck: Admin Post failed\n");
goto out_unlock;
}
wait_for_completion_timeout(&mrioc->init_cmds.done,
(MPI3MR_INTADMCMD_TIMEOUT * HZ));
if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
ioc_err(mrioc, "Issue EvtNotify: command timed out\n");
mpi3mr_soft_reset_handler(mrioc,
MPI3MR_RESET_FROM_EVTACK_TIMEOUT, 1);
retval = -1;
goto out_unlock;
}
if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
!= MPI3_IOCSTATUS_SUCCESS) {
ioc_err(mrioc,
"Send EvtAck: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
(mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
mrioc->init_cmds.ioc_loginfo);
retval = -1;
goto out_unlock;
}
out_unlock:
mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
mutex_unlock(&mrioc->init_cmds.mutex);
out:
return retval;
}
/**
* mpi3mr_alloc_chain_bufs - Allocate chain buffers
* @mrioc: Adapter instance reference
@ -2396,7 +2568,7 @@ int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc)
enum mpi3mr_iocstate ioc_state;
u64 base_info;
u32 timeout;
u32 ioc_status, ioc_config;
u32 ioc_status, ioc_config, i;
struct mpi3_ioc_facts_data facts_data;
mrioc->change_count = 0;
@ -2546,6 +2718,24 @@ int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc)
goto out_failed;
}
for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
mrioc->event_masks[i] = -1;
mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_ADDED);
mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_INFO_CHANGED);
mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_STATUS_CHANGE);
mpi3mr_unmask_events(mrioc, MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE);
mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_DISCOVERY);
mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR);
retval = mpi3mr_issue_event_notification(mrioc);
if (retval) {
ioc_err(mrioc, "Failed to issue event notification %d\n",
retval);
goto out_failed;
}
return retval;
out_failed:
@ -2627,6 +2817,11 @@ static void mpi3mr_free_mem(struct mpi3mr_ioc *mrioc)
kfree(mrioc->chain_bitmap);
mrioc->chain_bitmap = NULL;
for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) {
kfree(mrioc->dev_rmhs_cmds[i].reply);
mrioc->dev_rmhs_cmds[i].reply = NULL;
}
if (mrioc->chain_buf_pool) {
for (i = 0; i < mrioc->chain_buf_count; i++) {
if (mrioc->chain_sgl_list[i].addr) {

File diff suppressed because it is too large Load diff