Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/scsi/mpt3sas/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 124 kB image not shown  

Quelle  mpt3sas_ctl.c   Sprache: C

 
/*
 * Management Module Support for MPT (Message Passing Technology) based
 * controllers
 *
 * This code is based on drivers/scsi/mpt3sas/mpt3sas_ctl.c
 * Copyright (C) 2012-2014  LSI Corporation
 * Copyright (C) 2013-2014 Avago Technologies
 *  (mailto: MPT-FusionLinux.pdl@avagotech.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * NO WARRANTY
 * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
 * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
 * solely responsible for determining the appropriateness of using and
 * distributing the Program and assumes all risks associated with its
 * exercise of rights under this Agreement, including but not limited to
 * the risks and costs of program errors, damage to or loss of data,
 * programs or equipment, and unavailability or interruption of operations.

 * DISCLAIMER OF LIABILITY
 * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
 * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 */


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/compat.h>
#include <linux/poll.h>

#include <linux/io.h>
#include <linux/uaccess.h>

#include "mpt3sas_base.h"
#include "mpt3sas_ctl.h"


static struct fasync_struct *async_queue;
static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait);


/**
 * enum block_state - blocking state
 * @NON_BLOCKING: non blocking
 * @BLOCKING: blocking
 *
 * These states are for ioctls that need to wait for a response
 * from firmware, so they probably require sleep.
 */

enum block_state {
 NON_BLOCKING,
 BLOCKING,
};

/**
 * _ctl_display_some_debug - debug routine
 * @ioc: per adapter object
 * @smid: system request message index
 * @calling_function_name: string pass from calling function
 * @mpi_reply: reply message frame
 * Context: none.
 *
 * Function for displaying debug info helpful when debugging issues
 * in this module.
 */

static void
_ctl_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid,
 char *calling_function_name, MPI2DefaultReply_t *mpi_reply)
{
 Mpi2ConfigRequest_t *mpi_request;
 char *desc = NULL;

 if (!(ioc->logging_level & MPT_DEBUG_IOCTL))
  return;

 mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
 switch (mpi_request->Function) {
 case MPI2_FUNCTION_SCSI_IO_REQUEST:
 {
  Mpi2SCSIIORequest_t *scsi_request =
      (Mpi2SCSIIORequest_t *)mpi_request;

  snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
      "scsi_io, cmd(0x%02x), cdb_len(%d)",
      scsi_request->CDB.CDB32[0],
      le16_to_cpu(scsi_request->IoFlags) & 0xF);
  desc = ioc->tmp_string;
  break;
 }
 case MPI2_FUNCTION_SCSI_TASK_MGMT:
  desc = "task_mgmt";
  break;
 case MPI2_FUNCTION_IOC_INIT:
  desc = "ioc_init";
  break;
 case MPI2_FUNCTION_IOC_FACTS:
  desc = "ioc_facts";
  break;
 case MPI2_FUNCTION_CONFIG:
 {
  Mpi2ConfigRequest_t *config_request =
      (Mpi2ConfigRequest_t *)mpi_request;

  snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
      "config, type(0x%02x), ext_type(0x%02x), number(%d)",
      (config_request->Header.PageType &
       MPI2_CONFIG_PAGETYPE_MASK), config_request->ExtPageType,
      config_request->Header.PageNumber);
  desc = ioc->tmp_string;
  break;
 }
 case MPI2_FUNCTION_PORT_FACTS:
  desc = "port_facts";
  break;
 case MPI2_FUNCTION_PORT_ENABLE:
  desc = "port_enable";
  break;
 case MPI2_FUNCTION_EVENT_NOTIFICATION:
  desc = "event_notification";
  break;
 case MPI2_FUNCTION_FW_DOWNLOAD:
  desc = "fw_download";
  break;
 case MPI2_FUNCTION_FW_UPLOAD:
  desc = "fw_upload";
  break;
 case MPI2_FUNCTION_RAID_ACTION:
  desc = "raid_action";
  break;
 case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH:
 {
  Mpi2SCSIIORequest_t *scsi_request =
      (Mpi2SCSIIORequest_t *)mpi_request;

  snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
      "raid_pass, cmd(0x%02x), cdb_len(%d)",
      scsi_request->CDB.CDB32[0],
      le16_to_cpu(scsi_request->IoFlags) & 0xF);
  desc = ioc->tmp_string;
  break;
 }
 case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
  desc = "sas_iounit_cntl";
  break;
 case MPI2_FUNCTION_SATA_PASSTHROUGH:
  desc = "sata_pass";
  break;
 case MPI2_FUNCTION_DIAG_BUFFER_POST:
  desc = "diag_buffer_post";
  break;
 case MPI2_FUNCTION_DIAG_RELEASE:
  desc = "diag_release";
  break;
 case MPI2_FUNCTION_SMP_PASSTHROUGH:
  desc = "smp_passthrough";
  break;
 case MPI2_FUNCTION_TOOLBOX:
  desc = "toolbox";
  break;
 case MPI2_FUNCTION_NVME_ENCAPSULATED:
  desc = "nvme_encapsulated";
  break;
 case MPI2_FUNCTION_MCTP_PASSTHROUGH:
  desc = "mctp_passthrough";
  break;
 }

 if (!desc)
  return;

 ioc_info(ioc, "%s: %s, smid(%d)\n", calling_function_name, desc, smid);

 if (!mpi_reply)
  return;

 if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo)
  ioc_info(ioc, "\tiocstatus(0x%04x), loginfo(0x%08x)\n",
    le16_to_cpu(mpi_reply->IOCStatus),
    le32_to_cpu(mpi_reply->IOCLogInfo));

 if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
     mpi_request->Function ==
     MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
  Mpi2SCSIIOReply_t *scsi_reply =
      (Mpi2SCSIIOReply_t *)mpi_reply;
  struct _sas_device *sas_device = NULL;
  struct _pcie_device *pcie_device = NULL;

  sas_device = mpt3sas_get_sdev_by_handle(ioc,
      le16_to_cpu(scsi_reply->DevHandle));
  if (sas_device) {
   ioc_warn(ioc, "\tsas_address(0x%016llx), phy(%d)\n",
     (u64)sas_device->sas_address,
     sas_device->phy);
   ioc_warn(ioc, "\tenclosure_logical_id(0x%016llx), slot(%d)\n",
     (u64)sas_device->enclosure_logical_id,
     sas_device->slot);
   sas_device_put(sas_device);
  }
  if (!sas_device) {
   pcie_device = mpt3sas_get_pdev_by_handle(ioc,
    le16_to_cpu(scsi_reply->DevHandle));
   if (pcie_device) {
    ioc_warn(ioc, "\tWWID(0x%016llx), port(%d)\n",
      (unsigned long long)pcie_device->wwid,
      pcie_device->port_num);
    if (pcie_device->enclosure_handle != 0)
     ioc_warn(ioc, "\tenclosure_logical_id(0x%016llx), slot(%d)\n",
       (u64)pcie_device->enclosure_logical_id,
       pcie_device->slot);
    pcie_device_put(pcie_device);
   }
  }
  if (scsi_reply->SCSIState || scsi_reply->SCSIStatus)
   ioc_info(ioc, "\tscsi_state(0x%02x), scsi_status(0x%02x)\n",
     scsi_reply->SCSIState,
     scsi_reply->SCSIStatus);
 }
}

/**
 * mpt3sas_ctl_done - ctl module completion routine
 * @ioc: per adapter object
 * @smid: system request message index
 * @msix_index: MSIX table index supplied by the OS
 * @reply: reply message frame(lower 32bit addr)
 * Context: none.
 *
 * The callback handler when using ioc->ctl_cb_idx.
 *
 * Return: 1 meaning mf should be freed from _base_interrupt
 *         0 means the mf is freed from this function.
 */

u8
mpt3sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
 u32 reply)
{
 MPI2DefaultReply_t *mpi_reply;
 Mpi2SCSIIOReply_t *scsiio_reply;
 Mpi26NVMeEncapsulatedErrorReply_t *nvme_error_reply;
 const void *sense_data;
 u32 sz;

 if (ioc->ctl_cmds.status == MPT3_CMD_NOT_USED)
  return 1;
 if (ioc->ctl_cmds.smid != smid)
  return 1;
 ioc->ctl_cmds.status |= MPT3_CMD_COMPLETE;
 mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
 if (mpi_reply) {
  memcpy(ioc->ctl_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
  ioc->ctl_cmds.status |= MPT3_CMD_REPLY_VALID;
  /* get sense data */
  if (mpi_reply->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
      mpi_reply->Function ==
      MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
   scsiio_reply = (Mpi2SCSIIOReply_t *)mpi_reply;
   if (scsiio_reply->SCSIState &
       MPI2_SCSI_STATE_AUTOSENSE_VALID) {
    sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
        le32_to_cpu(scsiio_reply->SenseCount));
    sense_data = mpt3sas_base_get_sense_buffer(ioc,
        smid);
    memcpy(ioc->ctl_cmds.sense, sense_data, sz);
   }
  }
  /*
 * Get Error Response data for NVMe device. The ctl_cmds.sense
 * buffer is used to store the Error Response data.
 */

  if (mpi_reply->Function == MPI2_FUNCTION_NVME_ENCAPSULATED) {
   nvme_error_reply =
       (Mpi26NVMeEncapsulatedErrorReply_t *)mpi_reply;
   sz = min_t(u32, NVME_ERROR_RESPONSE_SIZE,
       le16_to_cpu(nvme_error_reply->ErrorResponseCount));
   sense_data = mpt3sas_base_get_sense_buffer(ioc, smid);
   memcpy(ioc->ctl_cmds.sense, sense_data, sz);
  }
 }

 _ctl_display_some_debug(ioc, smid, "ctl_done", mpi_reply);
 ioc->ctl_cmds.status &= ~MPT3_CMD_PENDING;
 complete(&ioc->ctl_cmds.done);
 return 1;
}

/**
 * _ctl_check_event_type - determines when an event needs logging
 * @ioc: per adapter object
 * @event: firmware event
 *
 * The bitmask in ioc->event_type[] indicates which events should be
 * be saved in the driver event_log.  This bitmask is set by application.
 *
 * Return: 1 when event should be captured, or zero means no match.
 */

static int
_ctl_check_event_type(struct MPT3SAS_ADAPTER *ioc, u16 event)
{
 u16 i;
 u32 desired_event;

 if (event >= 128 || !event || !ioc->event_log)
  return 0;

 desired_event = (1 << (event % 32));
 if (!desired_event)
  desired_event = 1;
 i = event / 32;
 return desired_event & ioc->event_type[i];
}

/**
 * mpt3sas_ctl_add_to_event_log - add event
 * @ioc: per adapter object
 * @mpi_reply: reply message frame
 */

void
mpt3sas_ctl_add_to_event_log(struct MPT3SAS_ADAPTER *ioc,
 Mpi2EventNotificationReply_t *mpi_reply)
{
 struct MPT3_IOCTL_EVENTS *event_log;
 u16 event;
 int i;
 u32 sz, event_data_sz;
 u8 send_aen = 0;

 if (!ioc->event_log)
  return;

 event = le16_to_cpu(mpi_reply->Event);

 if (_ctl_check_event_type(ioc, event)) {

  /* insert entry into circular event_log */
  i = ioc->event_context % MPT3SAS_CTL_EVENT_LOG_SIZE;
  event_log = ioc->event_log;
  event_log[i].event = event;
  event_log[i].context = ioc->event_context++;

  event_data_sz = le16_to_cpu(mpi_reply->EventDataLength)*4;
  sz = min_t(u32, event_data_sz, MPT3_EVENT_DATA_SIZE);
  memset(event_log[i].data, 0, MPT3_EVENT_DATA_SIZE);
  memcpy(event_log[i].data, mpi_reply->EventData, sz);
  send_aen = 1;
 }

 /* This aen_event_read_flag flag is set until the
 * application has read the event log.
 * For MPI2_EVENT_LOG_ENTRY_ADDED, we always notify.
 */

 if (event == MPI2_EVENT_LOG_ENTRY_ADDED ||
     (send_aen && !ioc->aen_event_read_flag)) {
  ioc->aen_event_read_flag = 1;
  wake_up_interruptible(&ctl_poll_wait);
  if (async_queue)
   kill_fasync(&async_queue, SIGIO, POLL_IN);
 }
}

/**
 * mpt3sas_ctl_event_callback - firmware event handler (called at ISR time)
 * @ioc: per adapter object
 * @msix_index: MSIX table index supplied by the OS
 * @reply: reply message frame(lower 32bit addr)
 * Context: interrupt.
 *
 * This function merely adds a new work task into ioc->firmware_event_thread.
 * The tasks are worked from _firmware_event_work in user context.
 *
 * Return: 1 meaning mf should be freed from _base_interrupt
 *         0 means the mf is freed from this function.
 */

u8
mpt3sas_ctl_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
 u32 reply)
{
 Mpi2EventNotificationReply_t *mpi_reply;

 mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
 if (mpi_reply)
  mpt3sas_ctl_add_to_event_log(ioc, mpi_reply);
 return 1;
}

/**
 * _ctl_verify_adapter - validates ioc_number passed from application
 * @ioc_number: ?
 * @iocpp: The ioc pointer is returned in this.
 * @mpi_version: will be MPI2_VERSION for mpt2ctl ioctl device &
 * MPI25_VERSION | MPI26_VERSION for mpt3ctl ioctl device.
 *
 * Return: (-1) means error, else ioc_number.
 */

static int
_ctl_verify_adapter(int ioc_number, struct MPT3SAS_ADAPTER **iocpp,
       int mpi_version)
{
 struct MPT3SAS_ADAPTER *ioc;
 int version = 0;
 /* global ioc lock to protect controller on list operations */
 spin_lock(&gioc_lock);
 list_for_each_entry(ioc, &mpt3sas_ioc_list, list) {
  if (ioc->id != ioc_number)
   continue;
  /* Check whether this ioctl command is from right
 * ioctl device or not, if not continue the search.
 */

  version = ioc->hba_mpi_version_belonged;
  /* MPI25_VERSION and MPI26_VERSION uses same ioctl
 * device.
 */

  if (mpi_version == (MPI25_VERSION | MPI26_VERSION)) {
   if ((version == MPI25_VERSION) ||
    (version == MPI26_VERSION))
    goto out;
   else
    continue;
  } else {
   if (version != mpi_version)
    continue;
  }
out:
  spin_unlock(&gioc_lock);
  *iocpp = ioc;
  return ioc_number;
 }
 spin_unlock(&gioc_lock);
 *iocpp = NULL;
 return -1;
}

/**
 * mpt3sas_ctl_pre_reset_handler - reset callback handler (for ctl)
 * @ioc: per adapter object
 *
 * The handler for doing any required cleanup or initialization.
 */

void mpt3sas_ctl_pre_reset_handler(struct MPT3SAS_ADAPTER *ioc)
{
 int i;
 u8 issue_reset;

 dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_PRE_RESET\n", __func__));
 for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
  if (!(ioc->diag_buffer_status[i] &
        MPT3_DIAG_BUFFER_IS_REGISTERED))
   continue;
  if ((ioc->diag_buffer_status[i] &
       MPT3_DIAG_BUFFER_IS_RELEASED))
   continue;

  /*
 * add a log message to indicate the release
 */

  ioc_info(ioc,
      "%s: Releasing the trace buffer due to adapter reset.",
      __func__);
  ioc->htb_rel.buffer_rel_condition =
      MPT3_DIAG_BUFFER_REL_TRIGGER;
  mpt3sas_send_diag_release(ioc, i, &issue_reset);
 }
}

/**
 * mpt3sas_ctl_clear_outstanding_ioctls - clears outstanding ioctl cmd.
 * @ioc: per adapter object
 *
 * The handler for doing any required cleanup or initialization.
 */

void mpt3sas_ctl_clear_outstanding_ioctls(struct MPT3SAS_ADAPTER *ioc)
{
 dtmprintk(ioc,
     ioc_info(ioc, "%s: clear outstanding ioctl cmd\n", __func__));
 if (ioc->ctl_cmds.status & MPT3_CMD_PENDING) {
  ioc->ctl_cmds.status |= MPT3_CMD_RESET;
  mpt3sas_base_free_smid(ioc, ioc->ctl_cmds.smid);
  complete(&ioc->ctl_cmds.done);
 }
}

/**
 * mpt3sas_ctl_reset_done_handler - reset callback handler (for ctl)
 * @ioc: per adapter object
 *
 * The handler for doing any required cleanup or initialization.
 */

void mpt3sas_ctl_reset_done_handler(struct MPT3SAS_ADAPTER *ioc)
{
 int i;

 dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_DONE_RESET\n", __func__));

 for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
  if (!(ioc->diag_buffer_status[i] &
        MPT3_DIAG_BUFFER_IS_REGISTERED))
   continue;
  if ((ioc->diag_buffer_status[i] &
       MPT3_DIAG_BUFFER_IS_RELEASED))
   continue;
  ioc->diag_buffer_status[i] |=
   MPT3_DIAG_BUFFER_IS_DIAG_RESET;
 }
}

/**
 * _ctl_fasync -
 * @fd: ?
 * @filep: ?
 * @mode: ?
 *
 * Called when application request fasyn callback handler.
 */

static int
_ctl_fasync(int fd, struct file *filep, int mode)
{
 return fasync_helper(fd, filep, mode, &async_queue);
}

/**
 * _ctl_poll -
 * @filep: ?
 * @wait: ?
 *
 */

static __poll_t
_ctl_poll(struct file *filep, poll_table *wait)
{
 struct MPT3SAS_ADAPTER *ioc;

 poll_wait(filep, &ctl_poll_wait, wait);

 /* global ioc lock to protect controller on list operations */
 spin_lock(&gioc_lock);
 list_for_each_entry(ioc, &mpt3sas_ioc_list, list) {
  if (ioc->aen_event_read_flag) {
   spin_unlock(&gioc_lock);
   return EPOLLIN | EPOLLRDNORM;
  }
 }
 spin_unlock(&gioc_lock);
 return 0;
}

/**
 * _ctl_set_task_mid - assign an active smid to tm request
 * @ioc: per adapter object
 * @karg: (struct mpt3_ioctl_command)
 * @tm_request: pointer to mf from user space
 *
 * Return: 0 when an smid if found, else fail.
 * during failure, the reply frame is filled.
 */

static int
_ctl_set_task_mid(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command *karg,
 Mpi2SCSITaskManagementRequest_t *tm_request)
{
 bool found = false;
 u16 smid;
 u16 handle;
 struct scsi_cmnd *scmd;
 struct MPT3SAS_DEVICE *priv_data;
 Mpi2SCSITaskManagementReply_t *tm_reply;
 u32 sz;
 u32 lun;
 char *desc = NULL;

 if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
  desc = "abort_task";
 else if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK)
  desc = "query_task";
 else
  return 0;

 lun = scsilun_to_int((struct scsi_lun *)tm_request->LUN);

 handle = le16_to_cpu(tm_request->DevHandle);
 for (smid = ioc->scsiio_depth; smid && !found; smid--) {
  struct scsiio_tracker *st;
  __le16 task_mid;

  scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
  if (!scmd)
   continue;
  if (lun != scmd->device->lun)
   continue;
  priv_data = scmd->device->hostdata;
  if (priv_data->sas_target == NULL)
   continue;
  if (priv_data->sas_target->handle != handle)
   continue;
  st = scsi_cmd_priv(scmd);

  /*
 * If the given TaskMID from the user space is zero, then the
 * first outstanding smid will be picked up.  Otherwise,
 * targeted smid will be the one.
 */

  task_mid = cpu_to_le16(st->smid);
  if (!tm_request->TaskMID)
   tm_request->TaskMID = task_mid;
  found = tm_request->TaskMID == task_mid;
 }

 if (!found) {
  dctlprintk(ioc,
      ioc_info(ioc, "%s: handle(0x%04x), lun(%d), no active mid!!\n",
        desc, le16_to_cpu(tm_request->DevHandle),
        lun));
  tm_reply = ioc->ctl_cmds.reply;
  tm_reply->DevHandle = tm_request->DevHandle;
  tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
  tm_reply->TaskType = tm_request->TaskType;
  tm_reply->MsgLength = sizeof(Mpi2SCSITaskManagementReply_t)/4;
  tm_reply->VP_ID = tm_request->VP_ID;
  tm_reply->VF_ID = tm_request->VF_ID;
  sz = min_t(u32, karg->max_reply_bytes, ioc->reply_sz);
  if (copy_to_user(karg->reply_frame_buf_ptr, ioc->ctl_cmds.reply,
      sz))
   pr_err("failure at %s:%d/%s()!\n", __FILE__,
       __LINE__, __func__);
  return 1;
 }

 dctlprintk(ioc,
     ioc_info(ioc, "%s: handle(0x%04x), lun(%d), task_mid(%d)\n",
       desc, le16_to_cpu(tm_request->DevHandle), lun,
       le16_to_cpu(tm_request->TaskMID)));
 return 0;
}

/**
 * _ctl_send_mctp_passthru_req - Send an MCTP passthru request
 * @ioc: per adapter object
 * @mctp_passthru_req: MPI mctp passhthru request from caller
 * @psge: pointer to the H2DSGL
 * @data_out_dma: DMA buffer for H2D SGL
 * @data_out_sz: H2D length
 * @data_in_dma: DMA buffer for D2H SGL
 * @data_in_sz: D2H length
 * @smid: SMID to submit the request
 *
 */

static void
_ctl_send_mctp_passthru_req(
 struct MPT3SAS_ADAPTER *ioc,
 Mpi26MctpPassthroughRequest_t *mctp_passthru_req, void *psge,
 dma_addr_t data_out_dma, int data_out_sz,
 dma_addr_t data_in_dma, int data_in_sz,
 u16 smid)
{
 mctp_passthru_req->H2DLength = data_out_sz;
 mctp_passthru_req->D2HLength = data_in_sz;

 /* Build the H2D SGL from the data out buffer */
 ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, 0, 0);

 psge += ioc->sge_size_ieee;

 /* Build the D2H SGL for the data in buffer */
 ioc->build_sg(ioc, psge, 0, 0, data_in_dma, data_in_sz);

 ioc->put_smid_default(ioc, smid);
}

/**
 * _ctl_do_mpt_command - main handler for MPT3COMMAND opcode
 * @ioc: per adapter object
 * @karg: (struct mpt3_ioctl_command)
 * @mf: pointer to mf in user space
 */

static long
_ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
 void __user *mf)
{
 MPI2RequestHeader_t *mpi_request = NULL, *request;
 MPI2DefaultReply_t *mpi_reply;
 Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request = NULL;
 struct _pcie_device *pcie_device = NULL;
 u16 smid;
 unsigned long timeout;
 u8 issue_reset;
 u32 sz, sz_arg;
 void *psge;
 void *data_out = NULL;
 dma_addr_t data_out_dma = 0;
 size_t data_out_sz = 0;
 void *data_in = NULL;
 dma_addr_t data_in_dma = 0;
 size_t data_in_sz = 0;
 long ret;
 u16 device_handle = MPT3SAS_INVALID_DEVICE_HANDLE;
 int tm_ret;

 issue_reset = 0;

 if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
  ioc_err(ioc, "%s: ctl_cmd in use\n", __func__);
  ret = -EAGAIN;
  goto out;
 }

 ret = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
 if (ret)
  goto out;

 mpi_request = kzalloc(ioc->request_sz, GFP_KERNEL);
 if (!mpi_request) {
  ioc_err(ioc, "%s: failed obtaining a memory for mpi_request\n",
   __func__);
  ret = -ENOMEM;
  goto out;
 }

 /* Check for overflow and wraparound */
 if (karg.data_sge_offset * 4 > ioc->request_sz ||
     karg.data_sge_offset > (UINT_MAX / 4)) {
  ret = -EINVAL;
  goto out;
 }

 /* copy in request message frame from user */
 if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) {
  pr_err("failure at %s:%d/%s()!\n", __FILE__, __LINE__,
      __func__);
  ret = -EFAULT;
  goto out;
 }

 if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) {
  smid = mpt3sas_base_get_smid_hpr(ioc, ioc->ctl_cb_idx);
  if (!smid) {
   ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
   ret = -EAGAIN;
   goto out;
  }
 } else {
  /* Use first reserved smid for passthrough ioctls */
  smid = ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT + 1;
 }

 ret = 0;
 ioc->ctl_cmds.status = MPT3_CMD_PENDING;
 memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
 request = mpt3sas_base_get_msg_frame(ioc, smid);
 memset(request, 0, ioc->request_sz);
 memcpy(request, mpi_request, karg.data_sge_offset*4);
 ioc->ctl_cmds.smid = smid;
 data_out_sz = karg.data_out_size;
 data_in_sz = karg.data_in_size;

 if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
     mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH ||
     mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT ||
     mpi_request->Function == MPI2_FUNCTION_SATA_PASSTHROUGH ||
     mpi_request->Function == MPI2_FUNCTION_NVME_ENCAPSULATED) {

  device_handle = le16_to_cpu(mpi_request->FunctionDependent1);
  if (!device_handle || (device_handle >
      ioc->facts.MaxDevHandle)) {
   ret = -EINVAL;
   mpt3sas_base_free_smid(ioc, smid);
   goto out;
  }
 }

 /* obtain dma-able memory for data transfer */
 if (data_out_sz) /* WRITE */ {
  data_out = dma_alloc_coherent(&ioc->pdev->dev, data_out_sz,
    &data_out_dma, GFP_KERNEL);
  if (!data_out) {
   pr_err("failure at %s:%d/%s()!\n", __FILE__,
       __LINE__, __func__);
   ret = -ENOMEM;
   mpt3sas_base_free_smid(ioc, smid);
   goto out;
  }
  if (copy_from_user(data_out, karg.data_out_buf_ptr,
   data_out_sz)) {
   pr_err("failure at %s:%d/%s()!\n", __FILE__,
       __LINE__, __func__);
   ret =  -EFAULT;
   mpt3sas_base_free_smid(ioc, smid);
   goto out;
  }
 }

 if (data_in_sz) /* READ */ {
  data_in = dma_alloc_coherent(&ioc->pdev->dev, data_in_sz,
    &data_in_dma, GFP_KERNEL);
  if (!data_in) {
   pr_err("failure at %s:%d/%s()!\n", __FILE__,
       __LINE__, __func__);
   ret = -ENOMEM;
   mpt3sas_base_free_smid(ioc, smid);
   goto out;
  }
 }

 psge = (void *)request + (karg.data_sge_offset*4);

 /* send command to firmware */
 _ctl_display_some_debug(ioc, smid, "ctl_request", NULL);

 init_completion(&ioc->ctl_cmds.done);
 switch (mpi_request->Function) {
 case MPI2_FUNCTION_MCTP_PASSTHROUGH:
 {
  Mpi26MctpPassthroughRequest_t *mctp_passthru_req =
      (Mpi26MctpPassthroughRequest_t *)request;

  if (!(ioc->facts.IOCCapabilities & MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU)) {
   ioc_err(ioc, "%s: MCTP Passthrough request not supported\n",
    __func__);
   mpt3sas_base_free_smid(ioc, smid);
   ret = -EINVAL;
   goto out;
  }

  _ctl_send_mctp_passthru_req(ioc, mctp_passthru_req, psge, data_out_dma,
     data_out_sz, data_in_dma, data_in_sz, smid);
  break;
 }
 case MPI2_FUNCTION_NVME_ENCAPSULATED:
 {
  nvme_encap_request = (Mpi26NVMeEncapsulatedRequest_t *)request;
  if (!ioc->pcie_sg_lookup) {
   dtmprintk(ioc, ioc_info(ioc,
       "HBA doesn't support NVMe. Rejecting NVMe Encapsulated request.\n"
       ));

   if (ioc->logging_level & MPT_DEBUG_TM)
    _debug_dump_mf(nvme_encap_request,
        ioc->request_sz/4);
   mpt3sas_base_free_smid(ioc, smid);
   ret = -EINVAL;
   goto out;
  }
  /*
 * Get the Physical Address of the sense buffer.
 * Use Error Response buffer address field to hold the sense
 * buffer address.
 * Clear the internal sense buffer, which will potentially hold
 * the Completion Queue Entry on return, or 0 if no Entry.
 * Build the PRPs and set direction bits.
 * Send the request.
 */

  nvme_encap_request->ErrorResponseBaseAddress =
      cpu_to_le64(ioc->sense_dma & 0xFFFFFFFF00000000UL);
  nvme_encap_request->ErrorResponseBaseAddress |=
     cpu_to_le64(le32_to_cpu(
     mpt3sas_base_get_sense_buffer_dma(ioc, smid)));
  nvme_encap_request->ErrorResponseAllocationLength =
     cpu_to_le16(NVME_ERROR_RESPONSE_SIZE);
  memset(ioc->ctl_cmds.sense, 0, NVME_ERROR_RESPONSE_SIZE);
  ioc->build_nvme_prp(ioc, smid, nvme_encap_request,
      data_out_dma, data_out_sz, data_in_dma, data_in_sz);
  if (test_bit(device_handle, ioc->device_remove_in_progress)) {
   dtmprintk(ioc,
      ioc_info(ioc, "handle(0x%04x): ioctl failed due to device removal in progress\n",
        device_handle));
   mpt3sas_base_free_smid(ioc, smid);
   ret = -EINVAL;
   goto out;
  }
  mpt3sas_base_put_smid_nvme_encap(ioc, smid);
  break;
 }
 case MPI2_FUNCTION_SCSI_IO_REQUEST:
 case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH:
 {
  Mpi2SCSIIORequest_t *scsiio_request =
      (Mpi2SCSIIORequest_t *)request;
  scsiio_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
  scsiio_request->SenseBufferLowAddress =
      mpt3sas_base_get_sense_buffer_dma(ioc, smid);
  memset(ioc->ctl_cmds.sense, 0, SCSI_SENSE_BUFFERSIZE);
  if (test_bit(device_handle, ioc->device_remove_in_progress)) {
   dtmprintk(ioc,
      ioc_info(ioc, "handle(0x%04x) :ioctl failed due to device removal in progress\n",
        device_handle));
   mpt3sas_base_free_smid(ioc, smid);
   ret = -EINVAL;
   goto out;
  }
  ioc->build_sg(ioc, psge, data_out_dma, data_out_sz,
      data_in_dma, data_in_sz);
  if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)
   ioc->put_smid_scsi_io(ioc, smid, device_handle);
  else
   ioc->put_smid_default(ioc, smid);
  break;
 }
 case MPI2_FUNCTION_SCSI_TASK_MGMT:
 {
  Mpi2SCSITaskManagementRequest_t *tm_request =
      (Mpi2SCSITaskManagementRequest_t *)request;

  dtmprintk(ioc,
     ioc_info(ioc, "TASK_MGMT: handle(0x%04x), task_type(0x%02x)\n",
       le16_to_cpu(tm_request->DevHandle),
       tm_request->TaskType));
  ioc->got_task_abort_from_ioctl = 1;
  if (tm_request->TaskType ==
      MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK ||
      tm_request->TaskType ==
      MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) {
   if (_ctl_set_task_mid(ioc, &karg, tm_request)) {
    mpt3sas_base_free_smid(ioc, smid);
    ioc->got_task_abort_from_ioctl = 0;
    goto out;
   }
  }
  ioc->got_task_abort_from_ioctl = 0;

  if (test_bit(device_handle, ioc->device_remove_in_progress)) {
   dtmprintk(ioc,
      ioc_info(ioc, "handle(0x%04x) :ioctl failed due to device removal in progress\n",
        device_handle));
   mpt3sas_base_free_smid(ioc, smid);
   ret = -EINVAL;
   goto out;
  }
  mpt3sas_scsih_set_tm_flag(ioc, le16_to_cpu(
      tm_request->DevHandle));
  ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
      data_in_dma, data_in_sz);
  ioc->put_smid_hi_priority(ioc, smid, 0);
  break;
 }
 case MPI2_FUNCTION_SMP_PASSTHROUGH:
 {
  Mpi2SmpPassthroughRequest_t *smp_request =
      (Mpi2SmpPassthroughRequest_t *)mpi_request;
  u8 *data;

  if (!ioc->multipath_on_hba) {
   /* ioc determines which port to use */
   smp_request->PhysicalPort = 0xFF;
  }
  if (smp_request->PassthroughFlags &
      MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE)
   data = (u8 *)&smp_request->SGL;
  else {
   if (unlikely(data_out == NULL)) {
    pr_err("failure at %s:%d/%s()!\n",
        __FILE__, __LINE__, __func__);
    mpt3sas_base_free_smid(ioc, smid);
    ret = -EINVAL;
    goto out;
   }
   data = data_out;
  }

  if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) {
   ioc->ioc_link_reset_in_progress = 1;
   ioc->ignore_loginfos = 1;
  }
  ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
      data_in_sz);
  ioc->put_smid_default(ioc, smid);
  break;
 }
 case MPI2_FUNCTION_SATA_PASSTHROUGH:
 {
  if (test_bit(device_handle, ioc->device_remove_in_progress)) {
   dtmprintk(ioc,
      ioc_info(ioc, "handle(0x%04x) :ioctl failed due to device removal in progress\n",
        device_handle));
   mpt3sas_base_free_smid(ioc, smid);
   ret = -EINVAL;
   goto out;
  }
  ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
      data_in_sz);
  ioc->put_smid_default(ioc, smid);
  break;
 }
 case MPI2_FUNCTION_FW_DOWNLOAD:
 {
  if (ioc->pdev->vendor == MPI2_MFGPAGE_VENDORID_ATTO) {
   ioc_info(ioc, "Firmware download not supported for ATTO HBA.\n");
   ret = -EPERM;
   break;
  }
  fallthrough;
 }
 case MPI2_FUNCTION_FW_UPLOAD:
 {
  ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
      data_in_sz);
  ioc->put_smid_default(ioc, smid);
  break;
 }
 case MPI2_FUNCTION_TOOLBOX:
 {
  Mpi2ToolboxCleanRequest_t *toolbox_request =
   (Mpi2ToolboxCleanRequest_t *)mpi_request;

  if ((toolbox_request->Tool == MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL)
      || (toolbox_request->Tool ==
      MPI26_TOOLBOX_BACKEND_PCIE_LANE_MARGIN))
   ioc->build_sg(ioc, psge, data_out_dma, data_out_sz,
    data_in_dma, data_in_sz);
  else if (toolbox_request->Tool ==
    MPI2_TOOLBOX_MEMORY_MOVE_TOOL) {
   Mpi2ToolboxMemMoveRequest_t *mem_move_request =
     (Mpi2ToolboxMemMoveRequest_t *)request;
   Mpi2SGESimple64_t tmp, *src = NULL, *dst = NULL;

   ioc->build_sg_mpi(ioc, psge, data_out_dma,
     data_out_sz, data_in_dma, data_in_sz);
   if (data_out_sz && !data_in_sz) {
    dst =
        (Mpi2SGESimple64_t *)&mem_move_request->SGL;
    src = (void *)dst + ioc->sge_size;

    memcpy(&tmp, src, ioc->sge_size);
    memcpy(src, dst, ioc->sge_size);
    memcpy(dst, &tmp, ioc->sge_size);
   }
   if (ioc->logging_level & MPT_DEBUG_TM) {
    ioc_info(ioc,
      "Mpi2ToolboxMemMoveRequest_t request msg\n");
    _debug_dump_mf(mem_move_request,
       ioc->request_sz/4);
   }
  } else
   ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
       data_in_dma, data_in_sz);
  ioc->put_smid_default(ioc, smid);
  break;
 }
 case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
 {
  Mpi2SasIoUnitControlRequest_t *sasiounit_request =
      (Mpi2SasIoUnitControlRequest_t *)mpi_request;

  if (sasiounit_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET
      || sasiounit_request->Operation ==
      MPI2_SAS_OP_PHY_LINK_RESET) {
   ioc->ioc_link_reset_in_progress = 1;
   ioc->ignore_loginfos = 1;
  }
  /* drop to default case for posting the request */
 }
  fallthrough;
 default:
  ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
      data_in_dma, data_in_sz);
  ioc->put_smid_default(ioc, smid);
  break;
 }

 if (karg.timeout < MPT3_IOCTL_DEFAULT_TIMEOUT)
  timeout = MPT3_IOCTL_DEFAULT_TIMEOUT;
 else
  timeout = karg.timeout;
 wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ);
 if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) {
  Mpi2SCSITaskManagementRequest_t *tm_request =
      (Mpi2SCSITaskManagementRequest_t *)mpi_request;
  mpt3sas_scsih_clear_tm_flag(ioc, le16_to_cpu(
      tm_request->DevHandle));
  mpt3sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT);
 } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH ||
     mpi_request->Function == MPI2_FUNCTION_SAS_IO_UNIT_CONTROL) &&
  ioc->ioc_link_reset_in_progress) {
  ioc->ioc_link_reset_in_progress = 0;
  ioc->ignore_loginfos = 0;
 }
 if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) {
  mpt3sas_check_cmd_timeout(ioc,
      ioc->ctl_cmds.status, mpi_request,
      karg.data_sge_offset, issue_reset);
  goto issue_host_reset;
 }

 mpi_reply = ioc->ctl_cmds.reply;

 if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT &&
     (ioc->logging_level & MPT_DEBUG_TM)) {
  Mpi2SCSITaskManagementReply_t *tm_reply =
      (Mpi2SCSITaskManagementReply_t *)mpi_reply;

  ioc_info(ioc, "TASK_MGMT: IOCStatus(0x%04x), IOCLogInfo(0x%08x), TerminationCount(0x%08x)\n",
    le16_to_cpu(tm_reply->IOCStatus),
    le32_to_cpu(tm_reply->IOCLogInfo),
    le32_to_cpu(tm_reply->TerminationCount));
 }

 /* copy out xdata to user */
 if (data_in_sz) {
  if (copy_to_user(karg.data_in_buf_ptr, data_in,
      data_in_sz)) {
   pr_err("failure at %s:%d/%s()!\n", __FILE__,
       __LINE__, __func__);
   ret = -ENODATA;
   goto out;
  }
 }

 /* copy out reply message frame to user */
 if (karg.max_reply_bytes) {
  sz = min_t(u32, karg.max_reply_bytes, ioc->reply_sz);
  if (copy_to_user(karg.reply_frame_buf_ptr, ioc->ctl_cmds.reply,
      sz)) {
   pr_err("failure at %s:%d/%s()!\n", __FILE__,
       __LINE__, __func__);
   ret = -ENODATA;
   goto out;
  }
 }

 /* copy out sense/NVMe Error Response to user */
 if (karg.max_sense_bytes && (mpi_request->Function ==
     MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function ==
     MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || mpi_request->Function ==
     MPI2_FUNCTION_NVME_ENCAPSULATED)) {
  if (karg.sense_data_ptr == NULL) {
   ioc_info(ioc, "Response buffer provided by application is NULL; Response data will not be returned\n");
   goto out;
  }
  sz_arg = (mpi_request->Function ==
  MPI2_FUNCTION_NVME_ENCAPSULATED) ? NVME_ERROR_RESPONSE_SIZE :
       SCSI_SENSE_BUFFERSIZE;
  sz = min_t(u32, karg.max_sense_bytes, sz_arg);
  if (copy_to_user(karg.sense_data_ptr, ioc->ctl_cmds.sense,
      sz)) {
   pr_err("failure at %s:%d/%s()!\n", __FILE__,
    __LINE__, __func__);
   ret = -ENODATA;
   goto out;
  }
 }

 issue_host_reset:
 if (issue_reset) {
  ret = -ENODATA;
  if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
      mpi_request->Function ==
      MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH ||
      mpi_request->Function == MPI2_FUNCTION_SATA_PASSTHROUGH)) {
   ioc_info(ioc, "issue target reset: handle = (0x%04x)\n",
     le16_to_cpu(mpi_request->FunctionDependent1));
   mpt3sas_halt_firmware(ioc);
   pcie_device = mpt3sas_get_pdev_by_handle(ioc,
    le16_to_cpu(mpi_request->FunctionDependent1));
   if (pcie_device && (!ioc->tm_custom_handling) &&
       (!(mpt3sas_scsih_is_pcie_scsi_device(
       pcie_device->device_info))))
    tm_ret = mpt3sas_scsih_issue_locked_tm(ioc,
      le16_to_cpu(mpi_request->FunctionDependent1),
      0, 0, 0,
      MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0,
      0, pcie_device->reset_timeout,
   MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE);
   else
    tm_ret = mpt3sas_scsih_issue_locked_tm(ioc,
      le16_to_cpu(mpi_request->FunctionDependent1),
      0, 0, 0,
      MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0,
      0, 30, MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET);

   if (tm_ret != SUCCESS) {
    ioc_info(ioc,
      "target reset failed, issue hard reset: handle (0x%04x)\n",
      le16_to_cpu(mpi_request->FunctionDependent1));
    mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
   }
  } else
   mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
 }

 out:
 if (pcie_device)
  pcie_device_put(pcie_device);

 /* free memory associated with sg buffers */
 if (data_in)
  dma_free_coherent(&ioc->pdev->dev, data_in_sz, data_in,
      data_in_dma);

 if (data_out)
  dma_free_coherent(&ioc->pdev->dev, data_out_sz, data_out,
      data_out_dma);

 kfree(mpi_request);
 ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
 return ret;
}

/**
 * _ctl_getiocinfo - main handler for MPT3IOCINFO opcode
 * @ioc: per adapter object
 * @arg: user space buffer containing ioctl content
 */

static long
_ctl_getiocinfo(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
{
 struct mpt3_ioctl_iocinfo karg;

 dctlprintk(ioc, ioc_info(ioc, "%s: enter\n",
     __func__));

 memset(&karg, 0 , sizeof(karg));
 if (ioc->pfacts)
  karg.port_number = ioc->pfacts[0].PortNumber;
 karg.hw_rev = ioc->pdev->revision;
 karg.pci_id = ioc->pdev->device;
 karg.subsystem_device = ioc->pdev->subsystem_device;
 karg.subsystem_vendor = ioc->pdev->subsystem_vendor;
 karg.pci_information.u.bits.bus = ioc->pdev->bus->number;
 karg.pci_information.u.bits.device = PCI_SLOT(ioc->pdev->devfn);
 karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn);
 karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus);
 karg.firmware_version = ioc->facts.FWVersion.Word;
 strcpy(karg.driver_version, ioc->driver_name);
 strcat(karg.driver_version, "-");
 switch  (ioc->hba_mpi_version_belonged) {
 case MPI2_VERSION:
  if (ioc->is_warpdrive)
   karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2_SSS6200;
  else
   karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2;
  strcat(karg.driver_version, MPT2SAS_DRIVER_VERSION);
  break;
 case MPI25_VERSION:
 case MPI26_VERSION:
  if (ioc->is_gen35_ioc)
   karg.adapter_type = MPT3_IOCTL_INTERFACE_SAS35;
  else
   karg.adapter_type = MPT3_IOCTL_INTERFACE_SAS3;
  strcat(karg.driver_version, MPT3SAS_DRIVER_VERSION);
  break;
 }
 karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion);

 karg.driver_capability |= MPT3_IOCTL_IOCINFO_DRIVER_CAP_MCTP_PASSTHRU;

 if (copy_to_user(arg, &karg, sizeof(karg))) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }
 return 0;
}

/**
 * _ctl_eventquery - main handler for MPT3EVENTQUERY opcode
 * @ioc: per adapter object
 * @arg: user space buffer containing ioctl content
 */

static long
_ctl_eventquery(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
{
 struct mpt3_ioctl_eventquery karg;

 if (copy_from_user(&karg, arg, sizeof(karg))) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }

 dctlprintk(ioc, ioc_info(ioc, "%s: enter\n",
     __func__));

 karg.event_entries = MPT3SAS_CTL_EVENT_LOG_SIZE;
 memcpy(karg.event_types, ioc->event_type,
     MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32));

 if (copy_to_user(arg, &karg, sizeof(karg))) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }
 return 0;
}

/**
 * _ctl_eventenable - main handler for MPT3EVENTENABLE opcode
 * @ioc: per adapter object
 * @arg: user space buffer containing ioctl content
 */

static long
_ctl_eventenable(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
{
 struct mpt3_ioctl_eventenable karg;

 if (copy_from_user(&karg, arg, sizeof(karg))) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }

 dctlprintk(ioc, ioc_info(ioc, "%s: enter\n",
     __func__));

 memcpy(ioc->event_type, karg.event_types,
     MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32));
 mpt3sas_base_validate_event_type(ioc, ioc->event_type);

 if (ioc->event_log)
  return 0;
 /* initialize event_log */
 ioc->event_context = 0;
 ioc->aen_event_read_flag = 0;
 ioc->event_log = kcalloc(MPT3SAS_CTL_EVENT_LOG_SIZE,
     sizeof(struct MPT3_IOCTL_EVENTS), GFP_KERNEL);
 if (!ioc->event_log) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -ENOMEM;
 }
 return 0;
}

/**
 * _ctl_eventreport - main handler for MPT3EVENTREPORT opcode
 * @ioc: per adapter object
 * @arg: user space buffer containing ioctl content
 */

static long
_ctl_eventreport(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
{
 struct mpt3_ioctl_eventreport karg;
 u32 number_bytes, max_events, max;
 struct mpt3_ioctl_eventreport __user *uarg = arg;

 if (copy_from_user(&karg, arg, sizeof(karg))) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }

 dctlprintk(ioc, ioc_info(ioc, "%s: enter\n",
     __func__));

 number_bytes = karg.hdr.max_data_size -
     sizeof(struct mpt3_ioctl_header);
 max_events = number_bytes/sizeof(struct MPT3_IOCTL_EVENTS);
 max = min_t(u32, MPT3SAS_CTL_EVENT_LOG_SIZE, max_events);

 /* If fewer than 1 event is requested, there must have
 * been some type of error.
 */

 if (!max || !ioc->event_log)
  return -ENODATA;

 number_bytes = max * sizeof(struct MPT3_IOCTL_EVENTS);
 if (copy_to_user(uarg->event_data, ioc->event_log, number_bytes)) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }

 /* reset flag so SIGIO can restart */
 ioc->aen_event_read_flag = 0;
 return 0;
}

/**
 * _ctl_do_reset - main handler for MPT3HARDRESET opcode
 * @ioc: per adapter object
 * @arg: user space buffer containing ioctl content
 */

static long
_ctl_do_reset(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
{
 struct mpt3_ioctl_diag_reset karg;
 int retval;

 if (copy_from_user(&karg, arg, sizeof(karg))) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }

 if (ioc->shost_recovery || ioc->pci_error_recovery ||
     ioc->is_driver_loading)
  return -EAGAIN;

 dctlprintk(ioc, ioc_info(ioc, "%s: enter\n",
     __func__));

 ioc->reset_from_user = 1;
 retval = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
 ioc_info(ioc,
     "Ioctl: host reset: %s\n", ((!retval) ? "SUCCESS" : "FAILED"));
 return 0;
}

/**
 * _ctl_btdh_search_sas_device - searching for sas device
 * @ioc: per adapter object
 * @btdh: btdh ioctl payload
 */

static int
_ctl_btdh_search_sas_device(struct MPT3SAS_ADAPTER *ioc,
 struct mpt3_ioctl_btdh_mapping *btdh)
{
 struct _sas_device *sas_device;
 unsigned long flags;
 int rc = 0;

 if (list_empty(&ioc->sas_device_list))
  return rc;

 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
  if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
      btdh->handle == sas_device->handle) {
   btdh->bus = sas_device->channel;
   btdh->id = sas_device->id;
   rc = 1;
   goto out;
  } else if (btdh->bus == sas_device->channel && btdh->id ==
      sas_device->id && btdh->handle == 0xFFFF) {
   btdh->handle = sas_device->handle;
   rc = 1;
   goto out;
  }
 }
 out:
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 return rc;
}

/**
 * _ctl_btdh_search_pcie_device - searching for pcie device
 * @ioc: per adapter object
 * @btdh: btdh ioctl payload
 */

static int
_ctl_btdh_search_pcie_device(struct MPT3SAS_ADAPTER *ioc,
 struct mpt3_ioctl_btdh_mapping *btdh)
{
 struct _pcie_device *pcie_device;
 unsigned long flags;
 int rc = 0;

 if (list_empty(&ioc->pcie_device_list))
  return rc;

 spin_lock_irqsave(&ioc->pcie_device_lock, flags);
 list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) {
  if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
      btdh->handle == pcie_device->handle) {
   btdh->bus = pcie_device->channel;
   btdh->id = pcie_device->id;
   rc = 1;
   goto out;
  } else if (btdh->bus == pcie_device->channel && btdh->id ==
      pcie_device->id && btdh->handle == 0xFFFF) {
   btdh->handle = pcie_device->handle;
   rc = 1;
   goto out;
  }
 }
 out:
 spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
 return rc;
}

/**
 * _ctl_btdh_search_raid_device - searching for raid device
 * @ioc: per adapter object
 * @btdh: btdh ioctl payload
 */

static int
_ctl_btdh_search_raid_device(struct MPT3SAS_ADAPTER *ioc,
 struct mpt3_ioctl_btdh_mapping *btdh)
{
 struct _raid_device *raid_device;
 unsigned long flags;
 int rc = 0;

 if (list_empty(&ioc->raid_device_list))
  return rc;

 spin_lock_irqsave(&ioc->raid_device_lock, flags);
 list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
  if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
      btdh->handle == raid_device->handle) {
   btdh->bus = raid_device->channel;
   btdh->id = raid_device->id;
   rc = 1;
   goto out;
  } else if (btdh->bus == raid_device->channel && btdh->id ==
      raid_device->id && btdh->handle == 0xFFFF) {
   btdh->handle = raid_device->handle;
   rc = 1;
   goto out;
  }
 }
 out:
 spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
 return rc;
}

/**
 * _ctl_btdh_mapping - main handler for MPT3BTDHMAPPING opcode
 * @ioc: per adapter object
 * @arg: user space buffer containing ioctl content
 */

static long
_ctl_btdh_mapping(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
{
 struct mpt3_ioctl_btdh_mapping karg;
 int rc;

 if (copy_from_user(&karg, arg, sizeof(karg))) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }

 dctlprintk(ioc, ioc_info(ioc, "%s\n",
     __func__));

 rc = _ctl_btdh_search_sas_device(ioc, &karg);
 if (!rc)
  rc = _ctl_btdh_search_pcie_device(ioc, &karg);
 if (!rc)
  _ctl_btdh_search_raid_device(ioc, &karg);

 if (copy_to_user(arg, &karg, sizeof(karg))) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }
 return 0;
}

/**
 * _ctl_diag_capability - return diag buffer capability
 * @ioc: per adapter object
 * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED
 *
 * returns 1 when diag buffer support is enabled in firmware
 */

static u8
_ctl_diag_capability(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type)
{
 u8 rc = 0;

 switch (buffer_type) {
 case MPI2_DIAG_BUF_TYPE_TRACE:
  if (ioc->facts.IOCCapabilities &
      MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER)
   rc = 1;
  break;
 case MPI2_DIAG_BUF_TYPE_SNAPSHOT:
  if (ioc->facts.IOCCapabilities &
      MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER)
   rc = 1;
  break;
 case MPI2_DIAG_BUF_TYPE_EXTENDED:
  if (ioc->facts.IOCCapabilities &
      MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER)
   rc = 1;
 }

 return rc;
}

/**
 * _ctl_diag_get_bufftype - return diag buffer type
 *              either TRACE, SNAPSHOT, or EXTENDED
 * @ioc: per adapter object
 * @unique_id: specifies the unique_id for the buffer
 *
 * returns MPT3_DIAG_UID_NOT_FOUND if the id not found
 */

static u8
_ctl_diag_get_bufftype(struct MPT3SAS_ADAPTER *ioc, u32 unique_id)
{
 u8  index;

 for (index = 0; index < MPI2_DIAG_BUF_TYPE_COUNT; index++) {
  if (ioc->unique_id[index] == unique_id)
   return index;
 }

 return MPT3_DIAG_UID_NOT_FOUND;
}

/**
 * _ctl_diag_register_2 - wrapper for registering diag buffer support
 * @ioc: per adapter object
 * @diag_register: the diag_register struct passed in from user space
 *
 */

static long
_ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc,
 struct mpt3_diag_register *diag_register)
{
 int rc, i;
 void *request_data = NULL;
 dma_addr_t request_data_dma;
 u32 request_data_sz = 0;
 Mpi2DiagBufferPostRequest_t *mpi_request;
 Mpi2DiagBufferPostReply_t *mpi_reply;
 u8 buffer_type;
 u16 smid;
 u16 ioc_status;
 u32 ioc_state;
 u8 issue_reset = 0;

 dctlprintk(ioc, ioc_info(ioc, "%s\n",
     __func__));

 ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
 if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
  ioc_err(ioc, "%s: failed due to ioc not operational\n",
   __func__);
  rc = -EAGAIN;
  goto out;
 }

 if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
  ioc_err(ioc, "%s: ctl_cmd in use\n", __func__);
  rc = -EAGAIN;
  goto out;
 }

 buffer_type = diag_register->buffer_type;
 if (!_ctl_diag_capability(ioc, buffer_type)) {
  ioc_err(ioc, "%s: doesn't have capability for buffer_type(0x%02x)\n",
   __func__, buffer_type);
  return -EPERM;
 }

 if (diag_register->unique_id == 0) {
  ioc_err(ioc,
      "%s: Invalid UID(0x%08x), buffer_type(0x%02x)\n", __func__,
      diag_register->unique_id, buffer_type);
  return -EINVAL;
 }

 if ((ioc->diag_buffer_status[buffer_type] &
     MPT3_DIAG_BUFFER_IS_APP_OWNED) &&
     !(ioc->diag_buffer_status[buffer_type] &
     MPT3_DIAG_BUFFER_IS_RELEASED)) {
  ioc_err(ioc,
      "%s: buffer_type(0x%02x) is already registered by application with UID(0x%08x)\n",
      __func__, buffer_type, ioc->unique_id[buffer_type]);
  return -EINVAL;
 }

 if (ioc->diag_buffer_status[buffer_type] &
     MPT3_DIAG_BUFFER_IS_REGISTERED) {
  /*
 * If driver posts buffer initially, then an application wants
 * to Register that buffer (own it) without Releasing first,
 * the application Register command MUST have the same buffer
 * type and size in the Register command (obtained from the
 * Query command). Otherwise that Register command will be
 * failed. If the application has released the buffer but wants
 * to re-register it, it should be allowed as long as the
 * Unique-Id/Size match.
 */


  if (ioc->unique_id[buffer_type] == MPT3DIAGBUFFUNIQUEID &&
      ioc->diag_buffer_sz[buffer_type] ==
      diag_register->requested_buffer_size) {

   if (!(ioc->diag_buffer_status[buffer_type] &
        MPT3_DIAG_BUFFER_IS_RELEASED)) {
    dctlprintk(ioc, ioc_info(ioc,
        "%s: diag_buffer (%d) ownership changed. old-ID(0x%08x), new-ID(0x%08x)\n",
        __func__, buffer_type,
        ioc->unique_id[buffer_type],
        diag_register->unique_id));

    /*
 * Application wants to own the buffer with
 * the same size.
 */

    ioc->unique_id[buffer_type] =
        diag_register->unique_id;
    rc = 0; /* success */
    goto out;
   }
  } else if (ioc->unique_id[buffer_type] !=
      MPT3DIAGBUFFUNIQUEID) {
   if (ioc->unique_id[buffer_type] !=
       diag_register->unique_id ||
       ioc->diag_buffer_sz[buffer_type] !=
       diag_register->requested_buffer_size ||
       !(ioc->diag_buffer_status[buffer_type] &
       MPT3_DIAG_BUFFER_IS_RELEASED)) {
    ioc_err(ioc,
        "%s: already has a registered buffer for buffer_type(0x%02x)\n",
        __func__, buffer_type);
    return -EINVAL;
   }
  } else {
   ioc_err(ioc, "%s: already has a registered buffer for buffer_type(0x%02x)\n",
       __func__, buffer_type);
   return -EINVAL;
  }
 } else if (ioc->diag_buffer_status[buffer_type] &
     MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED) {

  if (ioc->unique_id[buffer_type] != MPT3DIAGBUFFUNIQUEID ||
      ioc->diag_buffer_sz[buffer_type] !=
      diag_register->requested_buffer_size) {

   ioc_err(ioc,
       "%s: already a buffer is allocated for buffer_type(0x%02x) of size %d bytes, so please try registering again with same size\n",
        __func__, buffer_type,
       ioc->diag_buffer_sz[buffer_type]);
   return -EINVAL;
  }
 }

 if (diag_register->requested_buffer_size % 4)  {
  ioc_err(ioc, "%s: the requested_buffer_size is not 4 byte aligned\n",
   __func__);
  return -EINVAL;
 }

 smid = mpt3sas_base_get_smid(ioc, ioc->ctl_cb_idx);
 if (!smid) {
  ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
  rc = -EAGAIN;
  goto out;
 }

 rc = 0;
 ioc->ctl_cmds.status = MPT3_CMD_PENDING;
 memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
 mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
 memset(mpi_request, 0, ioc->request_sz);
 ioc->ctl_cmds.smid = smid;

 request_data = ioc->diag_buffer[buffer_type];
 request_data_sz = diag_register->requested_buffer_size;
 ioc->unique_id[buffer_type] = diag_register->unique_id;
 /* Reset ioc variables used for additional query commands */
 ioc->reset_from_user = 0;
 memset(&ioc->htb_rel, 0, sizeof(struct htb_rel_query));
 ioc->diag_buffer_status[buffer_type] &=
     MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED;
 memcpy(ioc->product_specific[buffer_type],
     diag_register->product_specific, MPT3_PRODUCT_SPECIFIC_DWORDS);
 ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags;

 if (request_data) {
  request_data_dma = ioc->diag_buffer_dma[buffer_type];
  if (request_data_sz != ioc->diag_buffer_sz[buffer_type]) {
   dma_free_coherent(&ioc->pdev->dev,
     ioc->diag_buffer_sz[buffer_type],
     request_data, request_data_dma);
   request_data = NULL;
  }
 }

 if (request_data == NULL) {
  ioc->diag_buffer_sz[buffer_type] = 0;
  ioc->diag_buffer_dma[buffer_type] = 0;
  request_data = dma_alloc_coherent(&ioc->pdev->dev,
    request_data_sz, &request_data_dma, GFP_KERNEL);
  if (request_data == NULL) {
   ioc_err(ioc, "%s: failed allocating memory for diag buffers, requested size(%d)\n",
    __func__, request_data_sz);
   mpt3sas_base_free_smid(ioc, smid);
   rc = -ENOMEM;
   goto out;
  }
  ioc->diag_buffer[buffer_type] = request_data;
  ioc->diag_buffer_sz[buffer_type] = request_data_sz;
  ioc->diag_buffer_dma[buffer_type] = request_data_dma;
 }

 mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
 mpi_request->BufferType = diag_register->buffer_type;
 mpi_request->Flags = cpu_to_le32(diag_register->diagnostic_flags);
 mpi_request->BufferAddress = cpu_to_le64(request_data_dma);
 mpi_request->BufferLength = cpu_to_le32(request_data_sz);
 mpi_request->VF_ID = 0; /* TODO */
 mpi_request->VP_ID = 0;

 dctlprintk(ioc,
     ioc_info(ioc, "%s: diag_buffer(0x%p), dma(0x%llx), sz(%d)\n",
       __func__, request_data,
       (unsigned long long)request_data_dma,
       le32_to_cpu(mpi_request->BufferLength)));

 for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++)
  mpi_request->ProductSpecific[i] =
   cpu_to_le32(ioc->product_specific[buffer_type][i]);

 init_completion(&ioc->ctl_cmds.done);
 ioc->put_smid_default(ioc, smid);
 wait_for_completion_timeout(&ioc->ctl_cmds.done,
     MPT3_IOCTL_DEFAULT_TIMEOUT*HZ);

 if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) {
  mpt3sas_check_cmd_timeout(ioc,
      ioc->ctl_cmds.status, mpi_request,
      sizeof(Mpi2DiagBufferPostRequest_t)/4, issue_reset);
  goto issue_host_reset;
 }

 /* process the completed Reply Message Frame */
 if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) {
  ioc_err(ioc, "%s: no reply message\n", __func__);
  rc = -EFAULT;
  goto out;
 }

 mpi_reply = ioc->ctl_cmds.reply;
 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;

 if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
  ioc->diag_buffer_status[buffer_type] |=
   MPT3_DIAG_BUFFER_IS_REGISTERED;
  dctlprintk(ioc, ioc_info(ioc, "%s: success\n", __func__));
 } else {
  ioc_info(ioc, "%s: ioc_status(0x%04x) log_info(0x%08x)\n",
    __func__,
    ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
  rc = -EFAULT;
 }

 issue_host_reset:
 if (issue_reset)
  mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);

 out:

 if (rc && request_data) {
  dma_free_coherent(&ioc->pdev->dev, request_data_sz,
      request_data, request_data_dma);
  ioc->diag_buffer[buffer_type] = NULL;
  ioc->diag_buffer_status[buffer_type] &=
      ~MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED;
 }

 ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
 return rc;
}

/**
 * mpt3sas_enable_diag_buffer - enabling diag_buffers support driver load time
 * @ioc: per adapter object
 * @bits_to_register: bitwise field where trace is bit 0, and snapshot is bit 1
 *
 * This is called when command line option diag_buffer_enable is enabled
 * at driver load time.
 */

void
mpt3sas_enable_diag_buffer(struct MPT3SAS_ADAPTER *ioc, u8 bits_to_register)
{
 struct mpt3_diag_register diag_register;
 u32 ret_val;
 u32 trace_buff_size = ioc->manu_pg11.HostTraceBufferMaxSizeKB<<10;
 u32 min_trace_buff_size = 0;
 u32 decr_trace_buff_size = 0;

 memset(&diag_register, 0, sizeof(struct mpt3_diag_register));

 if (bits_to_register & 1) {
  ioc_info(ioc, "registering trace buffer support\n");
  ioc->diag_trigger_master.MasterData =
      (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET);
  diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE;
  diag_register.unique_id =
      (ioc->hba_mpi_version_belonged == MPI2_VERSION) ?
      (MPT2DIAGBUFFUNIQUEID):(MPT3DIAGBUFFUNIQUEID);

  if (trace_buff_size != 0) {
   diag_register.requested_buffer_size = trace_buff_size;
   min_trace_buff_size =
       ioc->manu_pg11.HostTraceBufferMinSizeKB<<10;
   decr_trace_buff_size =
       ioc->manu_pg11.HostTraceBufferDecrementSizeKB<<10;

   if (min_trace_buff_size > trace_buff_size) {
    /* The buff size is not set correctly */
    ioc_err(ioc,
        "Min Trace Buff size (%d KB) greater than Max Trace Buff size (%d KB)\n",
         min_trace_buff_size>>10,
         trace_buff_size>>10);
    ioc_err(ioc,
        "Using zero Min Trace Buff Size\n");
    min_trace_buff_size = 0;
   }

   if (decr_trace_buff_size == 0) {
    /*
 * retry the min size if decrement
 * is not available.
 */

    decr_trace_buff_size =
        trace_buff_size - min_trace_buff_size;
   }
  } else {
   /* register for 2MB buffers  */
   diag_register.requested_buffer_size = 2 * (1024 * 1024);
  }

  do {
   ret_val = _ctl_diag_register_2(ioc,  &diag_register);

   if (ret_val == -ENOMEM && min_trace_buff_size &&
       (trace_buff_size - decr_trace_buff_size) >=
       min_trace_buff_size) {
    /* adjust the buffer size */
    trace_buff_size -= decr_trace_buff_size;
    diag_register.requested_buffer_size =
        trace_buff_size;
   } else
    break;
  } while (true);

  if (ret_val == -ENOMEM)
   ioc_err(ioc,
       "Cannot allocate trace buffer memory. Last memory tried = %d KB\n",
       diag_register.requested_buffer_size>>10);
  else if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE]
      & MPT3_DIAG_BUFFER_IS_REGISTERED) {
   ioc_info(ioc, "Trace buffer memory %d KB allocated\n",
       diag_register.requested_buffer_size>>10);
   if (ioc->hba_mpi_version_belonged != MPI2_VERSION)
    ioc->diag_buffer_status[
        MPI2_DIAG_BUF_TYPE_TRACE] |=
        MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED;
  }
 }

 if (bits_to_register & 2) {
  ioc_info(ioc, "registering snapshot buffer support\n");
  diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_SNAPSHOT;
  /* register for 2MB buffers  */
  diag_register.requested_buffer_size = 2 * (1024 * 1024);
  diag_register.unique_id = 0x7075901;
  _ctl_diag_register_2(ioc,  &diag_register);
 }

 if (bits_to_register & 4) {
  ioc_info(ioc, "registering extended buffer support\n");
  diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_EXTENDED;
  /* register for 2MB buffers  */
  diag_register.requested_buffer_size = 2 * (1024 * 1024);
  diag_register.unique_id = 0x7075901;
  _ctl_diag_register_2(ioc,  &diag_register);
 }
}

/**
 * _ctl_diag_register - application register with driver
 * @ioc: per adapter object
 * @arg: user space buffer containing ioctl content
 *
 * This will allow the driver to setup any required buffers that will be
 * needed by firmware to communicate with the driver.
 */

static long
_ctl_diag_register(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
{
 struct mpt3_diag_register karg;
 long rc;

 if (copy_from_user(&karg, arg, sizeof(karg))) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }

 rc = _ctl_diag_register_2(ioc, &karg);

 if (!rc && (ioc->diag_buffer_status[karg.buffer_type] &
     MPT3_DIAG_BUFFER_IS_REGISTERED))
  ioc->diag_buffer_status[karg.buffer_type] |=
      MPT3_DIAG_BUFFER_IS_APP_OWNED;

 return rc;
}

/**
 * _ctl_diag_unregister - application unregister with driver
 * @ioc: per adapter object
 * @arg: user space buffer containing ioctl content
 *
 * This will allow the driver to cleanup any memory allocated for diag
 * messages and to free up any resources.
 */

static long
_ctl_diag_unregister(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
{
 struct mpt3_diag_unregister karg;
 void *request_data;
 dma_addr_t request_data_dma;
 u32 request_data_sz;
 u8 buffer_type;

 if (copy_from_user(&karg, arg, sizeof(karg))) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }

 dctlprintk(ioc, ioc_info(ioc, "%s\n",
     __func__));

 buffer_type = _ctl_diag_get_bufftype(ioc, karg.unique_id);
 if (buffer_type == MPT3_DIAG_UID_NOT_FOUND) {
  ioc_err(ioc, "%s: buffer with unique_id(0x%08x) not found\n",
      __func__, karg.unique_id);
  return -EINVAL;
 }

 if (!_ctl_diag_capability(ioc, buffer_type)) {
  ioc_err(ioc, "%s: doesn't have capability for buffer_type(0x%02x)\n",
   __func__, buffer_type);
  return -EPERM;
 }

 if ((ioc->diag_buffer_status[buffer_type] &
     MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
  ioc_err(ioc, "%s: buffer_type(0x%02x) is not registered\n",
   __func__, buffer_type);
  return -EINVAL;
 }
 if ((ioc->diag_buffer_status[buffer_type] &
     MPT3_DIAG_BUFFER_IS_RELEASED) == 0) {
  ioc_err(ioc, "%s: buffer_type(0x%02x) has not been released\n",
   __func__, buffer_type);
  return -EINVAL;
 }

 if (karg.unique_id != ioc->unique_id[buffer_type]) {
  ioc_err(ioc, "%s: unique_id(0x%08x) is not registered\n",
   __func__, karg.unique_id);
  return -EINVAL;
 }

 request_data = ioc->diag_buffer[buffer_type];
 if (!request_data) {
  ioc_err(ioc, "%s: doesn't have memory allocated for buffer_type(0x%02x)\n",
   __func__, buffer_type);
  return -ENOMEM;
 }

 if (ioc->diag_buffer_status[buffer_type] &
     MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED) {
  ioc->unique_id[buffer_type] = MPT3DIAGBUFFUNIQUEID;
  ioc->diag_buffer_status[buffer_type] &=
      ~MPT3_DIAG_BUFFER_IS_APP_OWNED;
  ioc->diag_buffer_status[buffer_type] &=
      ~MPT3_DIAG_BUFFER_IS_REGISTERED;
 } else {
  request_data_sz = ioc->diag_buffer_sz[buffer_type];
  request_data_dma = ioc->diag_buffer_dma[buffer_type];
  dma_free_coherent(&ioc->pdev->dev, request_data_sz,
    request_data, request_data_dma);
  ioc->diag_buffer[buffer_type] = NULL;
  ioc->diag_buffer_status[buffer_type] = 0;
 }
 return 0;
}

/**
 * _ctl_diag_query - query relevant info associated with diag buffers
 * @ioc: per adapter object
 * @arg: user space buffer containing ioctl content
 *
 * The application will send only buffer_type and unique_id.  Driver will
 * inspect unique_id first, if valid, fill in all the info.  If unique_id is
 * 0x00, the driver will return info specified by Buffer Type.
 */

static long
_ctl_diag_query(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
{
 struct mpt3_diag_query karg;
 void *request_data;
 int i;
 u8 buffer_type;

 if (copy_from_user(&karg, arg, sizeof(karg))) {
  pr_err("failure at %s:%d/%s()!\n",
      __FILE__, __LINE__, __func__);
  return -EFAULT;
 }

 dctlprintk(ioc, ioc_info(ioc, "%s\n",
     __func__));

 karg.application_flags = 0;
 buffer_type = karg.buffer_type;

 if (!_ctl_diag_capability(ioc, buffer_type)) {
  ioc_err(ioc, "%s: doesn't have capability for buffer_type(0x%02x)\n",
   __func__, buffer_type);
  return -EPERM;
 }

 if (!(ioc->diag_buffer_status[buffer_type] &
     MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED)) {
  if ((ioc->diag_buffer_status[buffer_type] &
      MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
   ioc_err(ioc, "%s: buffer_type(0x%02x) is not registered\n",
    __func__, buffer_type);
   return -EINVAL;
  }
 }

 if (karg.unique_id) {
  if (karg.unique_id != ioc->unique_id[buffer_type]) {
   ioc_err(ioc, "%s: unique_id(0x%08x) is not registered\n",
    __func__, karg.unique_id);
   return -EINVAL;
  }
 }

 request_data = ioc->diag_buffer[buffer_type];
 if (!request_data) {
  ioc_err(ioc, "%s: doesn't have buffer for buffer_type(0x%02x)\n",
   __func__, buffer_type);
  return -ENOMEM;
 }

 if ((ioc->diag_buffer_status[buffer_type] &
     MPT3_DIAG_BUFFER_IS_REGISTERED))
  karg.application_flags |= MPT3_APP_FLAGS_BUFFER_VALID;

 if (!(ioc->diag_buffer_status[buffer_type] &
      MPT3_DIAG_BUFFER_IS_RELEASED))
  karg.application_flags |= MPT3_APP_FLAGS_FW_BUFFER_ACCESS;

 if (!(ioc->diag_buffer_status[buffer_type] &
     MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED))
  karg.application_flags |= MPT3_APP_FLAGS_DYNAMIC_BUFFER_ALLOC;

 if ((ioc->diag_buffer_status[buffer_type] &
     MPT3_DIAG_BUFFER_IS_APP_OWNED))
  karg.application_flags |= MPT3_APP_FLAGS_APP_OWNED;

 for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++)
  karg.product_specific[i] =
      ioc->product_specific[buffer_type][i];

 karg.total_buffer_size = ioc->diag_buffer_sz[buffer_type];
 karg.driver_added_buffer_size = 0;
 karg.unique_id = ioc->unique_id[buffer_type];
 karg.diagnostic_flags = ioc->diagnostic_flags[buffer_type];

 if (copy_to_user(arg, &karg, sizeof(struct mpt3_diag_query))) {
  ioc_err(ioc, "%s: unable to write mpt3_diag_query data @ %p\n",
   __func__, arg);
  return -EFAULT;
 }
 return 0;
}

/**
 * mpt3sas_send_diag_release - Diag Release Message
 * @ioc: per adapter object
 * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED
 * @issue_reset: specifies whether host reset is required.
 *
 */

int
mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type,
 u8 *issue_reset)
{
 Mpi2DiagReleaseRequest_t *mpi_request;
 Mpi2DiagReleaseReply_t *mpi_reply;
 u16 smid;
 u16 ioc_status;
 u32 ioc_state;
 int rc;
 u8 reset_needed = 0;

 dctlprintk(ioc, ioc_info(ioc, "%s\n",
     __func__));

 rc = 0;
 *issue_reset = 0;


 ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
 if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
  if (ioc->diag_buffer_status[buffer_type] &
      MPT3_DIAG_BUFFER_IS_REGISTERED)
   ioc->diag_buffer_status[buffer_type] |=
       MPT3_DIAG_BUFFER_IS_RELEASED;
  dctlprintk(ioc,
      ioc_info(ioc, "%s: skipping due to FAULT state\n",
        __func__));
  rc = -EAGAIN;
  goto out;
 }

 if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
  ioc_err(ioc, "%s: ctl_cmd in use\n", __func__);
  rc = -EAGAIN;
  goto out;
 }

 smid = mpt3sas_base_get_smid(ioc, ioc->ctl_cb_idx);
 if (!smid) {
  ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
  rc = -EAGAIN;
  goto out;
 }

 ioc->ctl_cmds.status = MPT3_CMD_PENDING;
 memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
 mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
 memset(mpi_request, 0, ioc->request_sz);
 ioc->ctl_cmds.smid = smid;

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=93 G=94

¤ Dauer der Verarbeitung: 0.18 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.