| /* |
| * driver for Microsemi PQI-based storage controllers |
| * Copyright (c) 2016-2017 Microsemi Corporation |
| * Copyright (c) 2016 PMC-Sierra, Inc. |
| * |
| * 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; version 2 of the License. |
| * |
| * 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, GOOD TITLE or |
| * NON INFRINGEMENT. See the GNU General Public License for more details. |
| * |
| * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com |
| * |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/pci.h> |
| #include <linux/delay.h> |
| #include <linux/interrupt.h> |
| #include <linux/sched.h> |
| #include <linux/rtc.h> |
| #include <linux/bcd.h> |
| #include <linux/reboot.h> |
| #include <linux/cciss_ioctl.h> |
| #include <linux/blk-mq-pci.h> |
| #include <scsi/scsi_host.h> |
| #include <scsi/scsi_cmnd.h> |
| #include <scsi/scsi_device.h> |
| #include <scsi/scsi_eh.h> |
| #include <scsi/scsi_transport_sas.h> |
| #include <asm/unaligned.h> |
| #include "smartpqi.h" |
| #include "smartpqi_sis.h" |
| |
| #if !defined(BUILD_TIMESTAMP) |
| #define BUILD_TIMESTAMP |
| #endif |
| |
| #define DRIVER_VERSION "1.1.4-130" |
| #define DRIVER_MAJOR 1 |
| #define DRIVER_MINOR 1 |
| #define DRIVER_RELEASE 4 |
| #define DRIVER_REVISION 130 |
| |
| #define DRIVER_NAME "Microsemi PQI Driver (v" \ |
| DRIVER_VERSION BUILD_TIMESTAMP ")" |
| #define DRIVER_NAME_SHORT "smartpqi" |
| |
| #define PQI_EXTRA_SGL_MEMORY (12 * sizeof(struct pqi_sg_descriptor)) |
| |
| MODULE_AUTHOR("Microsemi"); |
| MODULE_DESCRIPTION("Driver for Microsemi Smart Family Controller version " |
| DRIVER_VERSION); |
| MODULE_SUPPORTED_DEVICE("Microsemi Smart Family Controllers"); |
| MODULE_VERSION(DRIVER_VERSION); |
| MODULE_LICENSE("GPL"); |
| |
| static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info); |
| static void pqi_ctrl_offline_worker(struct work_struct *work); |
| static void pqi_retry_raid_bypass_requests(struct pqi_ctrl_info *ctrl_info); |
| static int pqi_scan_scsi_devices(struct pqi_ctrl_info *ctrl_info); |
| static void pqi_scan_start(struct Scsi_Host *shost); |
| static void pqi_start_io(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_queue_group *queue_group, enum pqi_io_path path, |
| struct pqi_io_request *io_request); |
| static int pqi_submit_raid_request_synchronous(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_iu_header *request, unsigned int flags, |
| struct pqi_raid_error_info *error_info, unsigned long timeout_msecs); |
| static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, |
| struct scsi_cmnd *scmd, u32 aio_handle, u8 *cdb, |
| unsigned int cdb_length, struct pqi_queue_group *queue_group, |
| struct pqi_encryption_info *encryption_info, bool raid_bypass); |
| |
| /* for flags argument to pqi_submit_raid_request_synchronous() */ |
| #define PQI_SYNC_FLAGS_INTERRUPTABLE 0x1 |
| |
| static struct scsi_transport_template *pqi_sas_transport_template; |
| |
| static atomic_t pqi_controller_count = ATOMIC_INIT(0); |
| |
| enum pqi_lockup_action { |
| NONE, |
| REBOOT, |
| PANIC |
| }; |
| |
| static enum pqi_lockup_action pqi_lockup_action = NONE; |
| |
| static struct { |
| enum pqi_lockup_action action; |
| char *name; |
| } pqi_lockup_actions[] = { |
| { |
| .action = NONE, |
| .name = "none", |
| }, |
| { |
| .action = REBOOT, |
| .name = "reboot", |
| }, |
| { |
| .action = PANIC, |
| .name = "panic", |
| }, |
| }; |
| |
| static unsigned int pqi_supported_event_types[] = { |
| PQI_EVENT_TYPE_HOTPLUG, |
| PQI_EVENT_TYPE_HARDWARE, |
| PQI_EVENT_TYPE_PHYSICAL_DEVICE, |
| PQI_EVENT_TYPE_LOGICAL_DEVICE, |
| PQI_EVENT_TYPE_AIO_STATE_CHANGE, |
| PQI_EVENT_TYPE_AIO_CONFIG_CHANGE, |
| }; |
| |
| static int pqi_disable_device_id_wildcards; |
| module_param_named(disable_device_id_wildcards, |
| pqi_disable_device_id_wildcards, int, 0644); |
| MODULE_PARM_DESC(disable_device_id_wildcards, |
| "Disable device ID wildcards."); |
| |
| static int pqi_disable_heartbeat; |
| module_param_named(disable_heartbeat, |
| pqi_disable_heartbeat, int, 0644); |
| MODULE_PARM_DESC(disable_heartbeat, |
| "Disable heartbeat."); |
| |
| static int pqi_disable_ctrl_shutdown; |
| module_param_named(disable_ctrl_shutdown, |
| pqi_disable_ctrl_shutdown, int, 0644); |
| MODULE_PARM_DESC(disable_ctrl_shutdown, |
| "Disable controller shutdown when controller locked up."); |
| |
| static char *pqi_lockup_action_param; |
| module_param_named(lockup_action, |
| pqi_lockup_action_param, charp, 0644); |
| MODULE_PARM_DESC(lockup_action, "Action to take when controller locked up.\n" |
| "\t\tSupported: none, reboot, panic\n" |
| "\t\tDefault: none"); |
| |
| static char *raid_levels[] = { |
| "RAID-0", |
| "RAID-4", |
| "RAID-1(1+0)", |
| "RAID-5", |
| "RAID-5+1", |
| "RAID-ADG", |
| "RAID-1(ADM)", |
| }; |
| |
| static char *pqi_raid_level_to_string(u8 raid_level) |
| { |
| if (raid_level < ARRAY_SIZE(raid_levels)) |
| return raid_levels[raid_level]; |
| |
| return "RAID UNKNOWN"; |
| } |
| |
| #define SA_RAID_0 0 |
| #define SA_RAID_4 1 |
| #define SA_RAID_1 2 /* also used for RAID 10 */ |
| #define SA_RAID_5 3 /* also used for RAID 50 */ |
| #define SA_RAID_51 4 |
| #define SA_RAID_6 5 /* also used for RAID 60 */ |
| #define SA_RAID_ADM 6 /* also used for RAID 1+0 ADM */ |
| #define SA_RAID_MAX SA_RAID_ADM |
| #define SA_RAID_UNKNOWN 0xff |
| |
| static inline void pqi_scsi_done(struct scsi_cmnd *scmd) |
| { |
| pqi_prep_for_scsi_done(scmd); |
| scmd->scsi_done(scmd); |
| } |
| |
| static inline bool pqi_scsi3addr_equal(u8 *scsi3addr1, u8 *scsi3addr2) |
| { |
| return memcmp(scsi3addr1, scsi3addr2, 8) == 0; |
| } |
| |
| static inline struct pqi_ctrl_info *shost_to_hba(struct Scsi_Host *shost) |
| { |
| void *hostdata = shost_priv(shost); |
| |
| return *((struct pqi_ctrl_info **)hostdata); |
| } |
| |
| static inline bool pqi_is_logical_device(struct pqi_scsi_dev *device) |
| { |
| return !device->is_physical_device; |
| } |
| |
| static inline bool pqi_is_external_raid_addr(u8 *scsi3addr) |
| { |
| return scsi3addr[2] != 0; |
| } |
| |
| static inline bool pqi_ctrl_offline(struct pqi_ctrl_info *ctrl_info) |
| { |
| return !ctrl_info->controller_online; |
| } |
| |
| static inline void pqi_check_ctrl_health(struct pqi_ctrl_info *ctrl_info) |
| { |
| if (ctrl_info->controller_online) |
| if (!sis_is_firmware_running(ctrl_info)) |
| pqi_take_ctrl_offline(ctrl_info); |
| } |
| |
| static inline bool pqi_is_hba_lunid(u8 *scsi3addr) |
| { |
| return pqi_scsi3addr_equal(scsi3addr, RAID_CTLR_LUNID); |
| } |
| |
| static inline enum pqi_ctrl_mode pqi_get_ctrl_mode( |
| struct pqi_ctrl_info *ctrl_info) |
| { |
| return sis_read_driver_scratch(ctrl_info); |
| } |
| |
| static inline void pqi_save_ctrl_mode(struct pqi_ctrl_info *ctrl_info, |
| enum pqi_ctrl_mode mode) |
| { |
| sis_write_driver_scratch(ctrl_info, mode); |
| } |
| |
| static inline void pqi_ctrl_block_requests(struct pqi_ctrl_info *ctrl_info) |
| { |
| ctrl_info->block_requests = true; |
| scsi_block_requests(ctrl_info->scsi_host); |
| } |
| |
| static inline void pqi_ctrl_unblock_requests(struct pqi_ctrl_info *ctrl_info) |
| { |
| ctrl_info->block_requests = false; |
| wake_up_all(&ctrl_info->block_requests_wait); |
| pqi_retry_raid_bypass_requests(ctrl_info); |
| scsi_unblock_requests(ctrl_info->scsi_host); |
| } |
| |
| static inline bool pqi_ctrl_blocked(struct pqi_ctrl_info *ctrl_info) |
| { |
| return ctrl_info->block_requests; |
| } |
| |
| static unsigned long pqi_wait_if_ctrl_blocked(struct pqi_ctrl_info *ctrl_info, |
| unsigned long timeout_msecs) |
| { |
| unsigned long remaining_msecs; |
| |
| if (!pqi_ctrl_blocked(ctrl_info)) |
| return timeout_msecs; |
| |
| atomic_inc(&ctrl_info->num_blocked_threads); |
| |
| if (timeout_msecs == NO_TIMEOUT) { |
| wait_event(ctrl_info->block_requests_wait, |
| !pqi_ctrl_blocked(ctrl_info)); |
| remaining_msecs = timeout_msecs; |
| } else { |
| unsigned long remaining_jiffies; |
| |
| remaining_jiffies = |
| wait_event_timeout(ctrl_info->block_requests_wait, |
| !pqi_ctrl_blocked(ctrl_info), |
| msecs_to_jiffies(timeout_msecs)); |
| remaining_msecs = jiffies_to_msecs(remaining_jiffies); |
| } |
| |
| atomic_dec(&ctrl_info->num_blocked_threads); |
| |
| return remaining_msecs; |
| } |
| |
| static inline void pqi_ctrl_busy(struct pqi_ctrl_info *ctrl_info) |
| { |
| atomic_inc(&ctrl_info->num_busy_threads); |
| } |
| |
| static inline void pqi_ctrl_unbusy(struct pqi_ctrl_info *ctrl_info) |
| { |
| atomic_dec(&ctrl_info->num_busy_threads); |
| } |
| |
| static inline void pqi_ctrl_wait_until_quiesced(struct pqi_ctrl_info *ctrl_info) |
| { |
| while (atomic_read(&ctrl_info->num_busy_threads) > |
| atomic_read(&ctrl_info->num_blocked_threads)) |
| usleep_range(1000, 2000); |
| } |
| |
| static inline bool pqi_device_offline(struct pqi_scsi_dev *device) |
| { |
| return device->device_offline; |
| } |
| |
| static inline void pqi_device_reset_start(struct pqi_scsi_dev *device) |
| { |
| device->in_reset = true; |
| } |
| |
| static inline void pqi_device_reset_done(struct pqi_scsi_dev *device) |
| { |
| device->in_reset = false; |
| } |
| |
| static inline bool pqi_device_in_reset(struct pqi_scsi_dev *device) |
| { |
| return device->in_reset; |
| } |
| |
| static inline void pqi_schedule_rescan_worker_with_delay( |
| struct pqi_ctrl_info *ctrl_info, unsigned long delay) |
| { |
| if (pqi_ctrl_offline(ctrl_info)) |
| return; |
| |
| schedule_delayed_work(&ctrl_info->rescan_work, delay); |
| } |
| |
| static inline void pqi_schedule_rescan_worker(struct pqi_ctrl_info *ctrl_info) |
| { |
| pqi_schedule_rescan_worker_with_delay(ctrl_info, 0); |
| } |
| |
| #define PQI_RESCAN_WORK_DELAY (10 * HZ) |
| |
| static inline void pqi_schedule_rescan_worker_delayed( |
| struct pqi_ctrl_info *ctrl_info) |
| { |
| pqi_schedule_rescan_worker_with_delay(ctrl_info, PQI_RESCAN_WORK_DELAY); |
| } |
| |
| static inline void pqi_cancel_rescan_worker(struct pqi_ctrl_info *ctrl_info) |
| { |
| cancel_delayed_work_sync(&ctrl_info->rescan_work); |
| } |
| |
| static inline u32 pqi_read_heartbeat_counter(struct pqi_ctrl_info *ctrl_info) |
| { |
| if (!ctrl_info->heartbeat_counter) |
| return 0; |
| |
| return readl(ctrl_info->heartbeat_counter); |
| } |
| |
| static int pqi_map_single(struct pci_dev *pci_dev, |
| struct pqi_sg_descriptor *sg_descriptor, void *buffer, |
| size_t buffer_length, int data_direction) |
| { |
| dma_addr_t bus_address; |
| |
| if (!buffer || buffer_length == 0 || data_direction == PCI_DMA_NONE) |
| return 0; |
| |
| bus_address = pci_map_single(pci_dev, buffer, buffer_length, |
| data_direction); |
| if (pci_dma_mapping_error(pci_dev, bus_address)) |
| return -ENOMEM; |
| |
| put_unaligned_le64((u64)bus_address, &sg_descriptor->address); |
| put_unaligned_le32(buffer_length, &sg_descriptor->length); |
| put_unaligned_le32(CISS_SG_LAST, &sg_descriptor->flags); |
| |
| return 0; |
| } |
| |
| static void pqi_pci_unmap(struct pci_dev *pci_dev, |
| struct pqi_sg_descriptor *descriptors, int num_descriptors, |
| int data_direction) |
| { |
| int i; |
| |
| if (data_direction == PCI_DMA_NONE) |
| return; |
| |
| for (i = 0; i < num_descriptors; i++) |
| pci_unmap_single(pci_dev, |
| (dma_addr_t)get_unaligned_le64(&descriptors[i].address), |
| get_unaligned_le32(&descriptors[i].length), |
| data_direction); |
| } |
| |
| static int pqi_build_raid_path_request(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_raid_path_request *request, u8 cmd, |
| u8 *scsi3addr, void *buffer, size_t buffer_length, |
| u16 vpd_page, int *pci_direction) |
| { |
| u8 *cdb; |
| int pci_dir; |
| |
| memset(request, 0, sizeof(*request)); |
| |
| request->header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO; |
| put_unaligned_le16(offsetof(struct pqi_raid_path_request, |
| sg_descriptors[1]) - PQI_REQUEST_HEADER_LENGTH, |
| &request->header.iu_length); |
| put_unaligned_le32(buffer_length, &request->buffer_length); |
| memcpy(request->lun_number, scsi3addr, sizeof(request->lun_number)); |
| request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; |
| request->additional_cdb_bytes_usage = SOP_ADDITIONAL_CDB_BYTES_0; |
| |
| cdb = request->cdb; |
| |
| switch (cmd) { |
| case INQUIRY: |
| request->data_direction = SOP_READ_FLAG; |
| cdb[0] = INQUIRY; |
| if (vpd_page & VPD_PAGE) { |
| cdb[1] = 0x1; |
| cdb[2] = (u8)vpd_page; |
| } |
| cdb[4] = (u8)buffer_length; |
| break; |
| case CISS_REPORT_LOG: |
| case CISS_REPORT_PHYS: |
| request->data_direction = SOP_READ_FLAG; |
| cdb[0] = cmd; |
| if (cmd == CISS_REPORT_PHYS) |
| cdb[1] = CISS_REPORT_PHYS_EXTENDED; |
| else |
| cdb[1] = CISS_REPORT_LOG_EXTENDED; |
| put_unaligned_be32(buffer_length, &cdb[6]); |
| break; |
| case CISS_GET_RAID_MAP: |
| request->data_direction = SOP_READ_FLAG; |
| cdb[0] = CISS_READ; |
| cdb[1] = CISS_GET_RAID_MAP; |
| put_unaligned_be32(buffer_length, &cdb[6]); |
| break; |
| case SA_FLUSH_CACHE: |
| request->data_direction = SOP_WRITE_FLAG; |
| cdb[0] = BMIC_WRITE; |
| cdb[6] = BMIC_FLUSH_CACHE; |
| put_unaligned_be16(buffer_length, &cdb[7]); |
| break; |
| case BMIC_IDENTIFY_CONTROLLER: |
| case BMIC_IDENTIFY_PHYSICAL_DEVICE: |
| request->data_direction = SOP_READ_FLAG; |
| cdb[0] = BMIC_READ; |
| cdb[6] = cmd; |
| put_unaligned_be16(buffer_length, &cdb[7]); |
| break; |
| case BMIC_WRITE_HOST_WELLNESS: |
| request->data_direction = SOP_WRITE_FLAG; |
| cdb[0] = BMIC_WRITE; |
| cdb[6] = cmd; |
| put_unaligned_be16(buffer_length, &cdb[7]); |
| break; |
| default: |
| dev_err(&ctrl_info->pci_dev->dev, "unknown command 0x%c\n", |
| cmd); |
| break; |
| } |
| |
| switch (request->data_direction) { |
| case SOP_READ_FLAG: |
| pci_dir = PCI_DMA_FROMDEVICE; |
| break; |
| case SOP_WRITE_FLAG: |
| pci_dir = PCI_DMA_TODEVICE; |
| break; |
| case SOP_NO_DIRECTION_FLAG: |
| pci_dir = PCI_DMA_NONE; |
| break; |
| default: |
| pci_dir = PCI_DMA_BIDIRECTIONAL; |
| break; |
| } |
| |
| *pci_direction = pci_dir; |
| |
| return pqi_map_single(ctrl_info->pci_dev, &request->sg_descriptors[0], |
| buffer, buffer_length, pci_dir); |
| } |
| |
| static inline void pqi_reinit_io_request(struct pqi_io_request *io_request) |
| { |
| io_request->scmd = NULL; |
| io_request->status = 0; |
| io_request->error_info = NULL; |
| io_request->raid_bypass = false; |
| } |
| |
| static struct pqi_io_request *pqi_alloc_io_request( |
| struct pqi_ctrl_info *ctrl_info) |
| { |
| struct pqi_io_request *io_request; |
| u16 i = ctrl_info->next_io_request_slot; /* benignly racy */ |
| |
| while (1) { |
| io_request = &ctrl_info->io_request_pool[i]; |
| if (atomic_inc_return(&io_request->refcount) == 1) |
| break; |
| atomic_dec(&io_request->refcount); |
| i = (i + 1) % ctrl_info->max_io_slots; |
| } |
| |
| /* benignly racy */ |
| ctrl_info->next_io_request_slot = (i + 1) % ctrl_info->max_io_slots; |
| |
| pqi_reinit_io_request(io_request); |
| |
| return io_request; |
| } |
| |
| static void pqi_free_io_request(struct pqi_io_request *io_request) |
| { |
| atomic_dec(&io_request->refcount); |
| } |
| |
| static int pqi_identify_controller(struct pqi_ctrl_info *ctrl_info, |
| struct bmic_identify_controller *buffer) |
| { |
| int rc; |
| int pci_direction; |
| struct pqi_raid_path_request request; |
| |
| rc = pqi_build_raid_path_request(ctrl_info, &request, |
| BMIC_IDENTIFY_CONTROLLER, RAID_CTLR_LUNID, buffer, |
| sizeof(*buffer), 0, &pci_direction); |
| if (rc) |
| return rc; |
| |
| rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, |
| NULL, NO_TIMEOUT); |
| |
| pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, |
| pci_direction); |
| |
| return rc; |
| } |
| |
| static int pqi_scsi_inquiry(struct pqi_ctrl_info *ctrl_info, |
| u8 *scsi3addr, u16 vpd_page, void *buffer, size_t buffer_length) |
| { |
| int rc; |
| int pci_direction; |
| struct pqi_raid_path_request request; |
| |
| rc = pqi_build_raid_path_request(ctrl_info, &request, |
| INQUIRY, scsi3addr, buffer, buffer_length, vpd_page, |
| &pci_direction); |
| if (rc) |
| return rc; |
| |
| rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, |
| NULL, NO_TIMEOUT); |
| |
| pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, |
| pci_direction); |
| |
| return rc; |
| } |
| |
| static int pqi_identify_physical_device(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device, |
| struct bmic_identify_physical_device *buffer, |
| size_t buffer_length) |
| { |
| int rc; |
| int pci_direction; |
| u16 bmic_device_index; |
| struct pqi_raid_path_request request; |
| |
| rc = pqi_build_raid_path_request(ctrl_info, &request, |
| BMIC_IDENTIFY_PHYSICAL_DEVICE, RAID_CTLR_LUNID, buffer, |
| buffer_length, 0, &pci_direction); |
| if (rc) |
| return rc; |
| |
| bmic_device_index = CISS_GET_DRIVE_NUMBER(device->scsi3addr); |
| request.cdb[2] = (u8)bmic_device_index; |
| request.cdb[9] = (u8)(bmic_device_index >> 8); |
| |
| rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, |
| 0, NULL, NO_TIMEOUT); |
| |
| pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, |
| pci_direction); |
| |
| return rc; |
| } |
| |
| static int pqi_flush_cache(struct pqi_ctrl_info *ctrl_info, |
| enum bmic_flush_cache_shutdown_event shutdown_event) |
| { |
| int rc; |
| struct pqi_raid_path_request request; |
| int pci_direction; |
| struct bmic_flush_cache *flush_cache; |
| |
| /* |
| * Don't bother trying to flush the cache if the controller is |
| * locked up. |
| */ |
| if (pqi_ctrl_offline(ctrl_info)) |
| return -ENXIO; |
| |
| flush_cache = kzalloc(sizeof(*flush_cache), GFP_KERNEL); |
| if (!flush_cache) |
| return -ENOMEM; |
| |
| flush_cache->shutdown_event = shutdown_event; |
| |
| rc = pqi_build_raid_path_request(ctrl_info, &request, |
| SA_FLUSH_CACHE, RAID_CTLR_LUNID, flush_cache, |
| sizeof(*flush_cache), 0, &pci_direction); |
| if (rc) |
| goto out; |
| |
| rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, |
| 0, NULL, NO_TIMEOUT); |
| |
| pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, |
| pci_direction); |
| |
| out: |
| kfree(flush_cache); |
| |
| return rc; |
| } |
| |
| static int pqi_write_host_wellness(struct pqi_ctrl_info *ctrl_info, |
| void *buffer, size_t buffer_length) |
| { |
| int rc; |
| struct pqi_raid_path_request request; |
| int pci_direction; |
| |
| rc = pqi_build_raid_path_request(ctrl_info, &request, |
| BMIC_WRITE_HOST_WELLNESS, RAID_CTLR_LUNID, buffer, |
| buffer_length, 0, &pci_direction); |
| if (rc) |
| return rc; |
| |
| rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, |
| 0, NULL, NO_TIMEOUT); |
| |
| pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, |
| pci_direction); |
| |
| return rc; |
| } |
| |
| #pragma pack(1) |
| |
| struct bmic_host_wellness_driver_version { |
| u8 start_tag[4]; |
| u8 driver_version_tag[2]; |
| __le16 driver_version_length; |
| char driver_version[32]; |
| u8 dont_write_tag[2]; |
| u8 end_tag[2]; |
| }; |
| |
| #pragma pack() |
| |
| static int pqi_write_driver_version_to_host_wellness( |
| struct pqi_ctrl_info *ctrl_info) |
| { |
| int rc; |
| struct bmic_host_wellness_driver_version *buffer; |
| size_t buffer_length; |
| |
| buffer_length = sizeof(*buffer); |
| |
| buffer = kmalloc(buffer_length, GFP_KERNEL); |
| if (!buffer) |
| return -ENOMEM; |
| |
| buffer->start_tag[0] = '<'; |
| buffer->start_tag[1] = 'H'; |
| buffer->start_tag[2] = 'W'; |
| buffer->start_tag[3] = '>'; |
| buffer->driver_version_tag[0] = 'D'; |
| buffer->driver_version_tag[1] = 'V'; |
| put_unaligned_le16(sizeof(buffer->driver_version), |
| &buffer->driver_version_length); |
| strncpy(buffer->driver_version, "Linux " DRIVER_VERSION, |
| sizeof(buffer->driver_version) - 1); |
| buffer->driver_version[sizeof(buffer->driver_version) - 1] = '\0'; |
| buffer->dont_write_tag[0] = 'D'; |
| buffer->dont_write_tag[1] = 'W'; |
| buffer->end_tag[0] = 'Z'; |
| buffer->end_tag[1] = 'Z'; |
| |
| rc = pqi_write_host_wellness(ctrl_info, buffer, buffer_length); |
| |
| kfree(buffer); |
| |
| return rc; |
| } |
| |
| #pragma pack(1) |
| |
| struct bmic_host_wellness_time { |
| u8 start_tag[4]; |
| u8 time_tag[2]; |
| __le16 time_length; |
| u8 time[8]; |
| u8 dont_write_tag[2]; |
| u8 end_tag[2]; |
| }; |
| |
| #pragma pack() |
| |
| static int pqi_write_current_time_to_host_wellness( |
| struct pqi_ctrl_info *ctrl_info) |
| { |
| int rc; |
| struct bmic_host_wellness_time *buffer; |
| size_t buffer_length; |
| time64_t local_time; |
| unsigned int year; |
| struct tm tm; |
| |
| buffer_length = sizeof(*buffer); |
| |
| buffer = kmalloc(buffer_length, GFP_KERNEL); |
| if (!buffer) |
| return -ENOMEM; |
| |
| buffer->start_tag[0] = '<'; |
| buffer->start_tag[1] = 'H'; |
| buffer->start_tag[2] = 'W'; |
| buffer->start_tag[3] = '>'; |
| buffer->time_tag[0] = 'T'; |
| buffer->time_tag[1] = 'D'; |
| put_unaligned_le16(sizeof(buffer->time), |
| &buffer->time_length); |
| |
| local_time = ktime_get_real_seconds(); |
| time64_to_tm(local_time, -sys_tz.tz_minuteswest * 60, &tm); |
| year = tm.tm_year + 1900; |
| |
| buffer->time[0] = bin2bcd(tm.tm_hour); |
| buffer->time[1] = bin2bcd(tm.tm_min); |
| buffer->time[2] = bin2bcd(tm.tm_sec); |
| buffer->time[3] = 0; |
| buffer->time[4] = bin2bcd(tm.tm_mon + 1); |
| buffer->time[5] = bin2bcd(tm.tm_mday); |
| buffer->time[6] = bin2bcd(year / 100); |
| buffer->time[7] = bin2bcd(year % 100); |
| |
| buffer->dont_write_tag[0] = 'D'; |
| buffer->dont_write_tag[1] = 'W'; |
| buffer->end_tag[0] = 'Z'; |
| buffer->end_tag[1] = 'Z'; |
| |
| rc = pqi_write_host_wellness(ctrl_info, buffer, buffer_length); |
| |
| kfree(buffer); |
| |
| return rc; |
| } |
| |
| #define PQI_UPDATE_TIME_WORK_INTERVAL (24UL * 60 * 60 * HZ) |
| |
| static void pqi_update_time_worker(struct work_struct *work) |
| { |
| int rc; |
| struct pqi_ctrl_info *ctrl_info; |
| |
| ctrl_info = container_of(to_delayed_work(work), struct pqi_ctrl_info, |
| update_time_work); |
| |
| if (pqi_ctrl_offline(ctrl_info)) |
| return; |
| |
| rc = pqi_write_current_time_to_host_wellness(ctrl_info); |
| if (rc) |
| dev_warn(&ctrl_info->pci_dev->dev, |
| "error updating time on controller\n"); |
| |
| schedule_delayed_work(&ctrl_info->update_time_work, |
| PQI_UPDATE_TIME_WORK_INTERVAL); |
| } |
| |
| static inline void pqi_schedule_update_time_worker( |
| struct pqi_ctrl_info *ctrl_info) |
| { |
| schedule_delayed_work(&ctrl_info->update_time_work, 0); |
| } |
| |
| static inline void pqi_cancel_update_time_worker( |
| struct pqi_ctrl_info *ctrl_info) |
| { |
| cancel_delayed_work_sync(&ctrl_info->update_time_work); |
| } |
| |
| static int pqi_report_luns(struct pqi_ctrl_info *ctrl_info, u8 cmd, |
| void *buffer, size_t buffer_length) |
| { |
| int rc; |
| int pci_direction; |
| struct pqi_raid_path_request request; |
| |
| rc = pqi_build_raid_path_request(ctrl_info, &request, |
| cmd, RAID_CTLR_LUNID, buffer, buffer_length, 0, &pci_direction); |
| if (rc) |
| return rc; |
| |
| rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, |
| NULL, NO_TIMEOUT); |
| |
| pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, |
| pci_direction); |
| |
| return rc; |
| } |
| |
| static int pqi_report_phys_logical_luns(struct pqi_ctrl_info *ctrl_info, u8 cmd, |
| void **buffer) |
| { |
| int rc; |
| size_t lun_list_length; |
| size_t lun_data_length; |
| size_t new_lun_list_length; |
| void *lun_data = NULL; |
| struct report_lun_header *report_lun_header; |
| |
| report_lun_header = kmalloc(sizeof(*report_lun_header), GFP_KERNEL); |
| if (!report_lun_header) { |
| rc = -ENOMEM; |
| goto out; |
| } |
| |
| rc = pqi_report_luns(ctrl_info, cmd, report_lun_header, |
| sizeof(*report_lun_header)); |
| if (rc) |
| goto out; |
| |
| lun_list_length = get_unaligned_be32(&report_lun_header->list_length); |
| |
| again: |
| lun_data_length = sizeof(struct report_lun_header) + lun_list_length; |
| |
| lun_data = kmalloc(lun_data_length, GFP_KERNEL); |
| if (!lun_data) { |
| rc = -ENOMEM; |
| goto out; |
| } |
| |
| if (lun_list_length == 0) { |
| memcpy(lun_data, report_lun_header, sizeof(*report_lun_header)); |
| goto out; |
| } |
| |
| rc = pqi_report_luns(ctrl_info, cmd, lun_data, lun_data_length); |
| if (rc) |
| goto out; |
| |
| new_lun_list_length = get_unaligned_be32( |
| &((struct report_lun_header *)lun_data)->list_length); |
| |
| if (new_lun_list_length > lun_list_length) { |
| lun_list_length = new_lun_list_length; |
| kfree(lun_data); |
| goto again; |
| } |
| |
| out: |
| kfree(report_lun_header); |
| |
| if (rc) { |
| kfree(lun_data); |
| lun_data = NULL; |
| } |
| |
| *buffer = lun_data; |
| |
| return rc; |
| } |
| |
| static inline int pqi_report_phys_luns(struct pqi_ctrl_info *ctrl_info, |
| void **buffer) |
| { |
| return pqi_report_phys_logical_luns(ctrl_info, CISS_REPORT_PHYS, |
| buffer); |
| } |
| |
| static inline int pqi_report_logical_luns(struct pqi_ctrl_info *ctrl_info, |
| void **buffer) |
| { |
| return pqi_report_phys_logical_luns(ctrl_info, CISS_REPORT_LOG, buffer); |
| } |
| |
| static int pqi_get_device_lists(struct pqi_ctrl_info *ctrl_info, |
| struct report_phys_lun_extended **physdev_list, |
| struct report_log_lun_extended **logdev_list) |
| { |
| int rc; |
| size_t logdev_list_length; |
| size_t logdev_data_length; |
| struct report_log_lun_extended *internal_logdev_list; |
| struct report_log_lun_extended *logdev_data; |
| struct report_lun_header report_lun_header; |
| |
| rc = pqi_report_phys_luns(ctrl_info, (void **)physdev_list); |
| if (rc) |
| dev_err(&ctrl_info->pci_dev->dev, |
| "report physical LUNs failed\n"); |
| |
| rc = pqi_report_logical_luns(ctrl_info, (void **)logdev_list); |
| if (rc) |
| dev_err(&ctrl_info->pci_dev->dev, |
| "report logical LUNs failed\n"); |
| |
| /* |
| * Tack the controller itself onto the end of the logical device list. |
| */ |
| |
| logdev_data = *logdev_list; |
| |
| if (logdev_data) { |
| logdev_list_length = |
| get_unaligned_be32(&logdev_data->header.list_length); |
| } else { |
| memset(&report_lun_header, 0, sizeof(report_lun_header)); |
| logdev_data = |
| (struct report_log_lun_extended *)&report_lun_header; |
| logdev_list_length = 0; |
| } |
| |
| logdev_data_length = sizeof(struct report_lun_header) + |
| logdev_list_length; |
| |
| internal_logdev_list = kmalloc(logdev_data_length + |
| sizeof(struct report_log_lun_extended), GFP_KERNEL); |
| if (!internal_logdev_list) { |
| kfree(*logdev_list); |
| *logdev_list = NULL; |
| return -ENOMEM; |
| } |
| |
| memcpy(internal_logdev_list, logdev_data, logdev_data_length); |
| memset((u8 *)internal_logdev_list + logdev_data_length, 0, |
| sizeof(struct report_log_lun_extended_entry)); |
| put_unaligned_be32(logdev_list_length + |
| sizeof(struct report_log_lun_extended_entry), |
| &internal_logdev_list->header.list_length); |
| |
| kfree(*logdev_list); |
| *logdev_list = internal_logdev_list; |
| |
| return 0; |
| } |
| |
| static inline void pqi_set_bus_target_lun(struct pqi_scsi_dev *device, |
| int bus, int target, int lun) |
| { |
| device->bus = bus; |
| device->target = target; |
| device->lun = lun; |
| } |
| |
| static void pqi_assign_bus_target_lun(struct pqi_scsi_dev *device) |
| { |
| u8 *scsi3addr; |
| u32 lunid; |
| int bus; |
| int target; |
| int lun; |
| |
| scsi3addr = device->scsi3addr; |
| lunid = get_unaligned_le32(scsi3addr); |
| |
| if (pqi_is_hba_lunid(scsi3addr)) { |
| /* The specified device is the controller. */ |
| pqi_set_bus_target_lun(device, PQI_HBA_BUS, 0, lunid & 0x3fff); |
| device->target_lun_valid = true; |
| return; |
| } |
| |
| if (pqi_is_logical_device(device)) { |
| if (device->is_external_raid_device) { |
| bus = PQI_EXTERNAL_RAID_VOLUME_BUS; |
| target = (lunid >> 16) & 0x3fff; |
| lun = lunid & 0xff; |
| } else { |
| bus = PQI_RAID_VOLUME_BUS; |
| target = 0; |
| lun = lunid & 0x3fff; |
| } |
| pqi_set_bus_target_lun(device, bus, target, lun); |
| device->target_lun_valid = true; |
| return; |
| } |
| |
| /* |
| * Defer target and LUN assignment for non-controller physical devices |
| * because the SAS transport layer will make these assignments later. |
| */ |
| pqi_set_bus_target_lun(device, PQI_PHYSICAL_DEVICE_BUS, 0, 0); |
| } |
| |
| static void pqi_get_raid_level(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device) |
| { |
| int rc; |
| u8 raid_level; |
| u8 *buffer; |
| |
| raid_level = SA_RAID_UNKNOWN; |
| |
| buffer = kmalloc(64, GFP_KERNEL); |
| if (buffer) { |
| rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, |
| VPD_PAGE | CISS_VPD_LV_DEVICE_GEOMETRY, buffer, 64); |
| if (rc == 0) { |
| raid_level = buffer[8]; |
| if (raid_level > SA_RAID_MAX) |
| raid_level = SA_RAID_UNKNOWN; |
| } |
| kfree(buffer); |
| } |
| |
| device->raid_level = raid_level; |
| } |
| |
| static int pqi_validate_raid_map(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device, struct raid_map *raid_map) |
| { |
| char *err_msg; |
| u32 raid_map_size; |
| u32 r5or6_blocks_per_row; |
| unsigned int num_phys_disks; |
| unsigned int num_raid_map_entries; |
| |
| raid_map_size = get_unaligned_le32(&raid_map->structure_size); |
| |
| if (raid_map_size < offsetof(struct raid_map, disk_data)) { |
| err_msg = "RAID map too small"; |
| goto bad_raid_map; |
| } |
| |
| if (raid_map_size > sizeof(*raid_map)) { |
| err_msg = "RAID map too large"; |
| goto bad_raid_map; |
| } |
| |
| num_phys_disks = get_unaligned_le16(&raid_map->layout_map_count) * |
| (get_unaligned_le16(&raid_map->data_disks_per_row) + |
| get_unaligned_le16(&raid_map->metadata_disks_per_row)); |
| num_raid_map_entries = num_phys_disks * |
| get_unaligned_le16(&raid_map->row_cnt); |
| |
| if (num_raid_map_entries > RAID_MAP_MAX_ENTRIES) { |
| err_msg = "invalid number of map entries in RAID map"; |
| goto bad_raid_map; |
| } |
| |
| if (device->raid_level == SA_RAID_1) { |
| if (get_unaligned_le16(&raid_map->layout_map_count) != 2) { |
| err_msg = "invalid RAID-1 map"; |
| goto bad_raid_map; |
| } |
| } else if (device->raid_level == SA_RAID_ADM) { |
| if (get_unaligned_le16(&raid_map->layout_map_count) != 3) { |
| err_msg = "invalid RAID-1(ADM) map"; |
| goto bad_raid_map; |
| } |
| } else if ((device->raid_level == SA_RAID_5 || |
| device->raid_level == SA_RAID_6) && |
| get_unaligned_le16(&raid_map->layout_map_count) > 1) { |
| /* RAID 50/60 */ |
| r5or6_blocks_per_row = |
| get_unaligned_le16(&raid_map->strip_size) * |
| get_unaligned_le16(&raid_map->data_disks_per_row); |
| if (r5or6_blocks_per_row == 0) { |
| err_msg = "invalid RAID-5 or RAID-6 map"; |
| goto bad_raid_map; |
| } |
| } |
| |
| return 0; |
| |
| bad_raid_map: |
| dev_warn(&ctrl_info->pci_dev->dev, |
| "logical device %08x%08x %s\n", |
| *((u32 *)&device->scsi3addr), |
| *((u32 *)&device->scsi3addr[4]), err_msg); |
| |
| return -EINVAL; |
| } |
| |
| static int pqi_get_raid_map(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device) |
| { |
| int rc; |
| int pci_direction; |
| struct pqi_raid_path_request request; |
| struct raid_map *raid_map; |
| |
| raid_map = kmalloc(sizeof(*raid_map), GFP_KERNEL); |
| if (!raid_map) |
| return -ENOMEM; |
| |
| rc = pqi_build_raid_path_request(ctrl_info, &request, |
| CISS_GET_RAID_MAP, device->scsi3addr, raid_map, |
| sizeof(*raid_map), 0, &pci_direction); |
| if (rc) |
| goto error; |
| |
| rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, |
| NULL, NO_TIMEOUT); |
| |
| pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, |
| pci_direction); |
| |
| if (rc) |
| goto error; |
| |
| rc = pqi_validate_raid_map(ctrl_info, device, raid_map); |
| if (rc) |
| goto error; |
| |
| device->raid_map = raid_map; |
| |
| return 0; |
| |
| error: |
| kfree(raid_map); |
| |
| return rc; |
| } |
| |
| static void pqi_get_raid_bypass_status(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device) |
| { |
| int rc; |
| u8 *buffer; |
| u8 bypass_status; |
| |
| buffer = kmalloc(64, GFP_KERNEL); |
| if (!buffer) |
| return; |
| |
| rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, |
| VPD_PAGE | CISS_VPD_LV_BYPASS_STATUS, buffer, 64); |
| if (rc) |
| goto out; |
| |
| #define RAID_BYPASS_STATUS 4 |
| #define RAID_BYPASS_CONFIGURED 0x1 |
| #define RAID_BYPASS_ENABLED 0x2 |
| |
| bypass_status = buffer[RAID_BYPASS_STATUS]; |
| device->raid_bypass_configured = |
| (bypass_status & RAID_BYPASS_CONFIGURED) != 0; |
| if (device->raid_bypass_configured && |
| (bypass_status & RAID_BYPASS_ENABLED) && |
| pqi_get_raid_map(ctrl_info, device) == 0) |
| device->raid_bypass_enabled = true; |
| |
| out: |
| kfree(buffer); |
| } |
| |
| /* |
| * Use vendor-specific VPD to determine online/offline status of a volume. |
| */ |
| |
| static void pqi_get_volume_status(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device) |
| { |
| int rc; |
| size_t page_length; |
| u8 volume_status = CISS_LV_STATUS_UNAVAILABLE; |
| bool volume_offline = true; |
| u32 volume_flags; |
| struct ciss_vpd_logical_volume_status *vpd; |
| |
| vpd = kmalloc(sizeof(*vpd), GFP_KERNEL); |
| if (!vpd) |
| goto no_buffer; |
| |
| rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, |
| VPD_PAGE | CISS_VPD_LV_STATUS, vpd, sizeof(*vpd)); |
| if (rc) |
| goto out; |
| |
| if (vpd->page_code != CISS_VPD_LV_STATUS) |
| goto out; |
| |
| page_length = offsetof(struct ciss_vpd_logical_volume_status, |
| volume_status) + vpd->page_length; |
| if (page_length < sizeof(*vpd)) |
| goto out; |
| |
| volume_status = vpd->volume_status; |
| volume_flags = get_unaligned_be32(&vpd->flags); |
| volume_offline = (volume_flags & CISS_LV_FLAGS_NO_HOST_IO) != 0; |
| |
| out: |
| kfree(vpd); |
| no_buffer: |
| device->volume_status = volume_status; |
| device->volume_offline = volume_offline; |
| } |
| |
| #define PQI_INQUIRY_PAGE0_RETRIES 3 |
| |
| static int pqi_get_device_info(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device) |
| { |
| int rc; |
| u8 *buffer; |
| unsigned int retries; |
| |
| buffer = kmalloc(64, GFP_KERNEL); |
| if (!buffer) |
| return -ENOMEM; |
| |
| /* Send an inquiry to the device to see what it is. */ |
| for (retries = 0;;) { |
| rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, 0, |
| buffer, 64); |
| if (rc == 0) |
| break; |
| if (pqi_is_logical_device(device) || |
| rc != PQI_CMD_STATUS_ABORTED || |
| ++retries > PQI_INQUIRY_PAGE0_RETRIES) |
| goto out; |
| } |
| |
| scsi_sanitize_inquiry_string(&buffer[8], 8); |
| scsi_sanitize_inquiry_string(&buffer[16], 16); |
| |
| device->devtype = buffer[0] & 0x1f; |
| memcpy(device->vendor, &buffer[8], sizeof(device->vendor)); |
| memcpy(device->model, &buffer[16], sizeof(device->model)); |
| |
| if (pqi_is_logical_device(device) && device->devtype == TYPE_DISK) { |
| if (device->is_external_raid_device) { |
| device->raid_level = SA_RAID_UNKNOWN; |
| device->volume_status = CISS_LV_OK; |
| device->volume_offline = false; |
| } else { |
| pqi_get_raid_level(ctrl_info, device); |
| pqi_get_raid_bypass_status(ctrl_info, device); |
| pqi_get_volume_status(ctrl_info, device); |
| } |
| } |
| |
| out: |
| kfree(buffer); |
| |
| return rc; |
| } |
| |
| static void pqi_get_physical_disk_info(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device, |
| struct bmic_identify_physical_device *id_phys) |
| { |
| int rc; |
| |
| memset(id_phys, 0, sizeof(*id_phys)); |
| |
| rc = pqi_identify_physical_device(ctrl_info, device, |
| id_phys, sizeof(*id_phys)); |
| if (rc) { |
| device->queue_depth = PQI_PHYSICAL_DISK_DEFAULT_MAX_QUEUE_DEPTH; |
| return; |
| } |
| |
| device->queue_depth = |
| get_unaligned_le16(&id_phys->current_queue_depth_limit); |
| device->device_type = id_phys->device_type; |
| device->active_path_index = id_phys->active_path_number; |
| device->path_map = id_phys->redundant_path_present_map; |
| memcpy(&device->box, |
| &id_phys->alternate_paths_phys_box_on_port, |
| sizeof(device->box)); |
| memcpy(&device->phys_connector, |
| &id_phys->alternate_paths_phys_connector, |
| sizeof(device->phys_connector)); |
| device->bay = id_phys->phys_bay_in_box; |
| } |
| |
| static void pqi_show_volume_status(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device) |
| { |
| char *status; |
| static const char unknown_state_str[] = |
| "Volume is in an unknown state (%u)"; |
| char unknown_state_buffer[sizeof(unknown_state_str) + 10]; |
| |
| switch (device->volume_status) { |
| case CISS_LV_OK: |
| status = "Volume online"; |
| break; |
| case CISS_LV_FAILED: |
| status = "Volume failed"; |
| break; |
| case CISS_LV_NOT_CONFIGURED: |
| status = "Volume not configured"; |
| break; |
| case CISS_LV_DEGRADED: |
| status = "Volume degraded"; |
| break; |
| case CISS_LV_READY_FOR_RECOVERY: |
| status = "Volume ready for recovery operation"; |
| break; |
| case CISS_LV_UNDERGOING_RECOVERY: |
| status = "Volume undergoing recovery"; |
| break; |
| case CISS_LV_WRONG_PHYSICAL_DRIVE_REPLACED: |
| status = "Wrong physical drive was replaced"; |
| break; |
| case CISS_LV_PHYSICAL_DRIVE_CONNECTION_PROBLEM: |
| status = "A physical drive not properly connected"; |
| break; |
| case CISS_LV_HARDWARE_OVERHEATING: |
| status = "Hardware is overheating"; |
| break; |
| case CISS_LV_HARDWARE_HAS_OVERHEATED: |
| status = "Hardware has overheated"; |
| break; |
| case CISS_LV_UNDERGOING_EXPANSION: |
| status = "Volume undergoing expansion"; |
| break; |
| case CISS_LV_NOT_AVAILABLE: |
| status = "Volume waiting for transforming volume"; |
| break; |
| case CISS_LV_QUEUED_FOR_EXPANSION: |
| status = "Volume queued for expansion"; |
| break; |
| case CISS_LV_DISABLED_SCSI_ID_CONFLICT: |
| status = "Volume disabled due to SCSI ID conflict"; |
| break; |
| case CISS_LV_EJECTED: |
| status = "Volume has been ejected"; |
| break; |
| case CISS_LV_UNDERGOING_ERASE: |
| status = "Volume undergoing background erase"; |
| break; |
| case CISS_LV_READY_FOR_PREDICTIVE_SPARE_REBUILD: |
| status = "Volume ready for predictive spare rebuild"; |
| break; |
| case CISS_LV_UNDERGOING_RPI: |
| status = "Volume undergoing rapid parity initialization"; |
| break; |
| case CISS_LV_PENDING_RPI: |
| status = "Volume queued for rapid parity initialization"; |
| break; |
| case CISS_LV_ENCRYPTED_NO_KEY: |
| status = "Encrypted volume inaccessible - key not present"; |
| break; |
| case CISS_LV_UNDERGOING_ENCRYPTION: |
| status = "Volume undergoing encryption process"; |
| break; |
| case CISS_LV_UNDERGOING_ENCRYPTION_REKEYING: |
| status = "Volume undergoing encryption re-keying process"; |
| break; |
| case CISS_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER: |
| status = "Volume encrypted but encryption is disabled"; |
| break; |
| case CISS_LV_PENDING_ENCRYPTION: |
| status = "Volume pending migration to encrypted state"; |
| break; |
| case CISS_LV_PENDING_ENCRYPTION_REKEYING: |
| status = "Volume pending encryption rekeying"; |
| break; |
| case CISS_LV_NOT_SUPPORTED: |
| status = "Volume not supported on this controller"; |
| break; |
| case CISS_LV_STATUS_UNAVAILABLE: |
| status = "Volume status not available"; |
| break; |
| default: |
| snprintf(unknown_state_buffer, sizeof(unknown_state_buffer), |
| unknown_state_str, device->volume_status); |
| status = unknown_state_buffer; |
| break; |
| } |
| |
| dev_info(&ctrl_info->pci_dev->dev, |
| "scsi %d:%d:%d:%d %s\n", |
| ctrl_info->scsi_host->host_no, |
| device->bus, device->target, device->lun, status); |
| } |
| |
| static void pqi_rescan_worker(struct work_struct *work) |
| { |
| struct pqi_ctrl_info *ctrl_info; |
| |
| ctrl_info = container_of(to_delayed_work(work), struct pqi_ctrl_info, |
| rescan_work); |
| |
| pqi_scan_scsi_devices(ctrl_info); |
| } |
| |
| static int pqi_add_device(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device) |
| { |
| int rc; |
| |
| if (pqi_is_logical_device(device)) |
| rc = scsi_add_device(ctrl_info->scsi_host, device->bus, |
| device->target, device->lun); |
| else |
| rc = pqi_add_sas_device(ctrl_info->sas_host, device); |
| |
| return rc; |
| } |
| |
| static inline void pqi_remove_device(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device) |
| { |
| if (pqi_is_logical_device(device)) |
| scsi_remove_device(device->sdev); |
| else |
| pqi_remove_sas_device(device); |
| } |
| |
| /* Assumes the SCSI device list lock is held. */ |
| |
| static struct pqi_scsi_dev *pqi_find_scsi_dev(struct pqi_ctrl_info *ctrl_info, |
| int bus, int target, int lun) |
| { |
| struct pqi_scsi_dev *device; |
| |
| list_for_each_entry(device, &ctrl_info->scsi_device_list, |
| scsi_device_list_entry) |
| if (device->bus == bus && device->target == target && |
| device->lun == lun) |
| return device; |
| |
| return NULL; |
| } |
| |
| static inline bool pqi_device_equal(struct pqi_scsi_dev *dev1, |
| struct pqi_scsi_dev *dev2) |
| { |
| if (dev1->is_physical_device != dev2->is_physical_device) |
| return false; |
| |
| if (dev1->is_physical_device) |
| return dev1->wwid == dev2->wwid; |
| |
| return memcmp(dev1->volume_id, dev2->volume_id, |
| sizeof(dev1->volume_id)) == 0; |
| } |
| |
| enum pqi_find_result { |
| DEVICE_NOT_FOUND, |
| DEVICE_CHANGED, |
| DEVICE_SAME, |
| }; |
| |
| static enum pqi_find_result pqi_scsi_find_entry(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device_to_find, |
| struct pqi_scsi_dev **matching_device) |
| { |
| struct pqi_scsi_dev *device; |
| |
| list_for_each_entry(device, &ctrl_info->scsi_device_list, |
| scsi_device_list_entry) { |
| if (pqi_scsi3addr_equal(device_to_find->scsi3addr, |
| device->scsi3addr)) { |
| *matching_device = device; |
| if (pqi_device_equal(device_to_find, device)) { |
| if (device_to_find->volume_offline) |
| return DEVICE_CHANGED; |
| return DEVICE_SAME; |
| } |
| return DEVICE_CHANGED; |
| } |
| } |
| |
| return DEVICE_NOT_FOUND; |
| } |
| |
| #define PQI_DEV_INFO_BUFFER_LENGTH 128 |
| |
| static void pqi_dev_info(struct pqi_ctrl_info *ctrl_info, |
| char *action, struct pqi_scsi_dev *device) |
| { |
| ssize_t count; |
| char buffer[PQI_DEV_INFO_BUFFER_LENGTH]; |
| |
| count = snprintf(buffer, PQI_DEV_INFO_BUFFER_LENGTH, |
| "%d:%d:", ctrl_info->scsi_host->host_no, device->bus); |
| |
| if (device->target_lun_valid) |
| count += snprintf(buffer + count, |
| PQI_DEV_INFO_BUFFER_LENGTH - count, |
| "%d:%d", |
| device->target, |
| device->lun); |
| else |
| count += snprintf(buffer + count, |
| PQI_DEV_INFO_BUFFER_LENGTH - count, |
| "-:-"); |
| |
| if (pqi_is_logical_device(device)) |
| count += snprintf(buffer + count, |
| PQI_DEV_INFO_BUFFER_LENGTH - count, |
| " %08x%08x", |
| *((u32 *)&device->scsi3addr), |
| *((u32 *)&device->scsi3addr[4])); |
| else |
| count += snprintf(buffer + count, |
| PQI_DEV_INFO_BUFFER_LENGTH - count, |
| " %016llx", device->sas_address); |
| |
| count += snprintf(buffer + count, PQI_DEV_INFO_BUFFER_LENGTH - count, |
| " %s %.8s %.16s ", |
| scsi_device_type(device->devtype), |
| device->vendor, |
| device->model); |
| |
| if (pqi_is_logical_device(device)) { |
| if (device->devtype == TYPE_DISK) |
| count += snprintf(buffer + count, |
| PQI_DEV_INFO_BUFFER_LENGTH - count, |
| "SSDSmartPathCap%c En%c %-12s", |
| device->raid_bypass_configured ? '+' : '-', |
| device->raid_bypass_enabled ? '+' : '-', |
| pqi_raid_level_to_string(device->raid_level)); |
| } else { |
| count += snprintf(buffer + count, |
| PQI_DEV_INFO_BUFFER_LENGTH - count, |
| "AIO%c", device->aio_enabled ? '+' : '-'); |
| if (device->devtype == TYPE_DISK || |
| device->devtype == TYPE_ZBC) |
| count += snprintf(buffer + count, |
| PQI_DEV_INFO_BUFFER_LENGTH - count, |
| " qd=%-6d", device->queue_depth); |
| } |
| |
| dev_info(&ctrl_info->pci_dev->dev, "%s %s\n", action, buffer); |
| } |
| |
| /* Assumes the SCSI device list lock is held. */ |
| |
| static void pqi_scsi_update_device(struct pqi_scsi_dev *existing_device, |
| struct pqi_scsi_dev *new_device) |
| { |
| existing_device->devtype = new_device->devtype; |
| existing_device->device_type = new_device->device_type; |
| existing_device->bus = new_device->bus; |
| if (new_device->target_lun_valid) { |
| existing_device->target = new_device->target; |
| existing_device->lun = new_device->lun; |
| existing_device->target_lun_valid = true; |
| } |
| |
| /* By definition, the scsi3addr and wwid fields are already the same. */ |
| |
| existing_device->is_physical_device = new_device->is_physical_device; |
| existing_device->is_external_raid_device = |
| new_device->is_external_raid_device; |
| existing_device->aio_enabled = new_device->aio_enabled; |
| memcpy(existing_device->vendor, new_device->vendor, |
| sizeof(existing_device->vendor)); |
| memcpy(existing_device->model, new_device->model, |
| sizeof(existing_device->model)); |
| existing_device->sas_address = new_device->sas_address; |
| existing_device->raid_level = new_device->raid_level; |
| existing_device->queue_depth = new_device->queue_depth; |
| existing_device->aio_handle = new_device->aio_handle; |
| existing_device->volume_status = new_device->volume_status; |
| existing_device->active_path_index = new_device->active_path_index; |
| existing_device->path_map = new_device->path_map; |
| existing_device->bay = new_device->bay; |
| memcpy(existing_device->box, new_device->box, |
| sizeof(existing_device->box)); |
| memcpy(existing_device->phys_connector, new_device->phys_connector, |
| sizeof(existing_device->phys_connector)); |
| existing_device->offload_to_mirror = 0; |
| kfree(existing_device->raid_map); |
| existing_device->raid_map = new_device->raid_map; |
| existing_device->raid_bypass_configured = |
| new_device->raid_bypass_configured; |
| existing_device->raid_bypass_enabled = |
| new_device->raid_bypass_enabled; |
| |
| /* To prevent this from being freed later. */ |
| new_device->raid_map = NULL; |
| } |
| |
| static inline void pqi_free_device(struct pqi_scsi_dev *device) |
| { |
| if (device) { |
| kfree(device->raid_map); |
| kfree(device); |
| } |
| } |
| |
| /* |
| * Called when exposing a new device to the OS fails in order to re-adjust |
| * our internal SCSI device list to match the SCSI ML's view. |
| */ |
| |
| static inline void pqi_fixup_botched_add(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); |
| list_del(&device->scsi_device_list_entry); |
| spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); |
| |
| /* Allow the device structure to be freed later. */ |
| device->keep_device = false; |
| } |
| |
| static void pqi_update_device_list(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *new_device_list[], unsigned int num_new_devices) |
| { |
| int rc; |
| unsigned int i; |
| unsigned long flags; |
| enum pqi_find_result find_result; |
| struct pqi_scsi_dev *device; |
| struct pqi_scsi_dev *next; |
| struct pqi_scsi_dev *matching_device; |
| LIST_HEAD(add_list); |
| LIST_HEAD(delete_list); |
| |
| /* |
| * The idea here is to do as little work as possible while holding the |
| * spinlock. That's why we go to great pains to defer anything other |
| * than updating the internal device list until after we release the |
| * spinlock. |
| */ |
| |
| spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); |
| |
| /* Assume that all devices in the existing list have gone away. */ |
| list_for_each_entry(device, &ctrl_info->scsi_device_list, |
| scsi_device_list_entry) |
| device->device_gone = true; |
| |
| for (i = 0; i < num_new_devices; i++) { |
| device = new_device_list[i]; |
| |
| find_result = pqi_scsi_find_entry(ctrl_info, device, |
| &matching_device); |
| |
| switch (find_result) { |
| case DEVICE_SAME: |
| /* |
| * The newly found device is already in the existing |
| * device list. |
| */ |
| device->new_device = false; |
| matching_device->device_gone = false; |
| pqi_scsi_update_device(matching_device, device); |
| break; |
| case DEVICE_NOT_FOUND: |
| /* |
| * The newly found device is NOT in the existing device |
| * list. |
| */ |
| device->new_device = true; |
| break; |
| case DEVICE_CHANGED: |
| /* |
| * The original device has gone away and we need to add |
| * the new device. |
| */ |
| device->new_device = true; |
| break; |
| } |
| } |
| |
| /* Process all devices that have gone away. */ |
| list_for_each_entry_safe(device, next, &ctrl_info->scsi_device_list, |
| scsi_device_list_entry) { |
| if (device->device_gone) { |
| list_del(&device->scsi_device_list_entry); |
| list_add_tail(&device->delete_list_entry, &delete_list); |
| } |
| } |
| |
| /* Process all new devices. */ |
| for (i = 0; i < num_new_devices; i++) { |
| device = new_device_list[i]; |
| if (!device->new_device) |
| continue; |
| if (device->volume_offline) |
| continue; |
| list_add_tail(&device->scsi_device_list_entry, |
| &ctrl_info->scsi_device_list); |
| list_add_tail(&device->add_list_entry, &add_list); |
| /* To prevent this device structure from being freed later. */ |
| device->keep_device = true; |
| } |
| |
| spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); |
| |
| /* Remove all devices that have gone away. */ |
| list_for_each_entry_safe(device, next, &delete_list, |
| delete_list_entry) { |
| if (device->volume_offline) { |
| pqi_dev_info(ctrl_info, "offline", device); |
| pqi_show_volume_status(ctrl_info, device); |
| } else { |
| pqi_dev_info(ctrl_info, "removed", device); |
| } |
| if (device->sdev) |
| pqi_remove_device(ctrl_info, device); |
| list_del(&device->delete_list_entry); |
| pqi_free_device(device); |
| } |
| |
| /* |
| * Notify the SCSI ML if the queue depth of any existing device has |
| * changed. |
| */ |
| list_for_each_entry(device, &ctrl_info->scsi_device_list, |
| scsi_device_list_entry) { |
| if (device->sdev && device->queue_depth != |
| device->advertised_queue_depth) { |
| device->advertised_queue_depth = device->queue_depth; |
| scsi_change_queue_depth(device->sdev, |
| device->advertised_queue_depth); |
| } |
| } |
| |
| /* Expose any new devices. */ |
| list_for_each_entry_safe(device, next, &add_list, add_list_entry) { |
| if (!device->sdev) { |
| pqi_dev_info(ctrl_info, "added", device); |
| rc = pqi_add_device(ctrl_info, device); |
| if (rc) { |
| dev_warn(&ctrl_info->pci_dev->dev, |
| "scsi %d:%d:%d:%d addition failed, device not added\n", |
| ctrl_info->scsi_host->host_no, |
| device->bus, device->target, |
| device->lun); |
| pqi_fixup_botched_add(ctrl_info, device); |
| } |
| } |
| } |
| } |
| |
| static bool pqi_is_supported_device(struct pqi_scsi_dev *device) |
| { |
| bool is_supported = false; |
| |
| switch (device->devtype) { |
| case TYPE_DISK: |
| case TYPE_ZBC: |
| case TYPE_TAPE: |
| case TYPE_MEDIUM_CHANGER: |
| case TYPE_ENCLOSURE: |
| is_supported = true; |
| break; |
| case TYPE_RAID: |
| /* |
| * Only support the HBA controller itself as a RAID |
| * controller. If it's a RAID controller other than |
| * the HBA itself (an external RAID controller, for |
| * example), we don't support it. |
| */ |
| if (pqi_is_hba_lunid(device->scsi3addr)) |
| is_supported = true; |
| break; |
| } |
| |
| return is_supported; |
| } |
| |
| static inline bool pqi_skip_device(u8 *scsi3addr) |
| { |
| /* Ignore all masked devices. */ |
| if (MASKED_DEVICE(scsi3addr)) |
| return true; |
| |
| return false; |
| } |
| |
| static int pqi_update_scsi_devices(struct pqi_ctrl_info *ctrl_info) |
| { |
| int i; |
| int rc; |
| LIST_HEAD(new_device_list_head); |
| struct report_phys_lun_extended *physdev_list = NULL; |
| struct report_log_lun_extended *logdev_list = NULL; |
| struct report_phys_lun_extended_entry *phys_lun_ext_entry; |
| struct report_log_lun_extended_entry *log_lun_ext_entry; |
| struct bmic_identify_physical_device *id_phys = NULL; |
| u32 num_physicals; |
| u32 num_logicals; |
| struct pqi_scsi_dev **new_device_list = NULL; |
| struct pqi_scsi_dev *device; |
| struct pqi_scsi_dev *next; |
| unsigned int num_new_devices; |
| unsigned int num_valid_devices; |
| bool is_physical_device; |
| u8 *scsi3addr; |
| static char *out_of_memory_msg = |
| "failed to allocate memory, device discovery stopped"; |
| |
| rc = pqi_get_device_lists(ctrl_info, &physdev_list, &logdev_list); |
| if (rc) |
| goto out; |
| |
| if (physdev_list) |
| num_physicals = |
| get_unaligned_be32(&physdev_list->header.list_length) |
| / sizeof(physdev_list->lun_entries[0]); |
| else |
| num_physicals = 0; |
| |
| if (logdev_list) |
| num_logicals = |
| get_unaligned_be32(&logdev_list->header.list_length) |
| / sizeof(logdev_list->lun_entries[0]); |
| else |
| num_logicals = 0; |
| |
| if (num_physicals) { |
| /* |
| * We need this buffer for calls to pqi_get_physical_disk_info() |
| * below. We allocate it here instead of inside |
| * pqi_get_physical_disk_info() because it's a fairly large |
| * buffer. |
| */ |
| id_phys = kmalloc(sizeof(*id_phys), GFP_KERNEL); |
| if (!id_phys) { |
| dev_warn(&ctrl_info->pci_dev->dev, "%s\n", |
| out_of_memory_msg); |
| rc = -ENOMEM; |
| goto out; |
| } |
| } |
| |
| num_new_devices = num_physicals + num_logicals; |
| |
| new_device_list = kmalloc_array(num_new_devices, |
| sizeof(*new_device_list), |
| GFP_KERNEL); |
| if (!new_device_list) { |
| dev_warn(&ctrl_info->pci_dev->dev, "%s\n", out_of_memory_msg); |
| rc = -ENOMEM; |
| goto out; |
| } |
| |
| for (i = 0; i < num_new_devices; i++) { |
| device = kzalloc(sizeof(*device), GFP_KERNEL); |
| if (!device) { |
| dev_warn(&ctrl_info->pci_dev->dev, "%s\n", |
| out_of_memory_msg); |
| rc = -ENOMEM; |
| goto out; |
| } |
| list_add_tail(&device->new_device_list_entry, |
| &new_device_list_head); |
| } |
| |
| device = NULL; |
| num_valid_devices = 0; |
| |
| for (i = 0; i < num_new_devices; i++) { |
| |
| if (i < num_physicals) { |
| is_physical_device = true; |
| phys_lun_ext_entry = &physdev_list->lun_entries[i]; |
| log_lun_ext_entry = NULL; |
| scsi3addr = phys_lun_ext_entry->lunid; |
| } else { |
| is_physical_device = false; |
| phys_lun_ext_entry = NULL; |
| log_lun_ext_entry = |
| &logdev_list->lun_entries[i - num_physicals]; |
| scsi3addr = log_lun_ext_entry->lunid; |
| } |
| |
| if (is_physical_device && pqi_skip_device(scsi3addr)) |
| continue; |
| |
| if (device) |
| device = list_next_entry(device, new_device_list_entry); |
| else |
| device = list_first_entry(&new_device_list_head, |
| struct pqi_scsi_dev, new_device_list_entry); |
| |
| memcpy(device->scsi3addr, scsi3addr, sizeof(device->scsi3addr)); |
| device->is_physical_device = is_physical_device; |
| if (!is_physical_device) |
| device->is_external_raid_device = |
| pqi_is_external_raid_addr(scsi3addr); |
| |
| /* Gather information about the device. */ |
| rc = pqi_get_device_info(ctrl_info, device); |
| if (rc == -ENOMEM) { |
| dev_warn(&ctrl_info->pci_dev->dev, "%s\n", |
| out_of_memory_msg); |
| goto out; |
| } |
| if (rc) { |
| if (device->is_physical_device) |
| dev_warn(&ctrl_info->pci_dev->dev, |
| "obtaining device info failed, skipping physical device %016llx\n", |
| get_unaligned_be64( |
| &phys_lun_ext_entry->wwid)); |
| else |
| dev_warn(&ctrl_info->pci_dev->dev, |
| "obtaining device info failed, skipping logical device %08x%08x\n", |
| *((u32 *)&device->scsi3addr), |
| *((u32 *)&device->scsi3addr[4])); |
| rc = 0; |
| continue; |
| } |
| |
| if (!pqi_is_supported_device(device)) |
| continue; |
| |
| pqi_assign_bus_target_lun(device); |
| |
| if (device->is_physical_device) { |
| device->wwid = phys_lun_ext_entry->wwid; |
| if ((phys_lun_ext_entry->device_flags & |
| REPORT_PHYS_LUN_DEV_FLAG_AIO_ENABLED) && |
| phys_lun_ext_entry->aio_handle) |
| device->aio_enabled = true; |
| } else { |
| memcpy(device->volume_id, log_lun_ext_entry->volume_id, |
| sizeof(device->volume_id)); |
| } |
| |
| switch (device->devtype) { |
| case TYPE_DISK: |
| case TYPE_ZBC: |
| case TYPE_ENCLOSURE: |
| if (device->is_physical_device) { |
| device->sas_address = |
| get_unaligned_be64(&device->wwid); |
| if (device->devtype == TYPE_DISK || |
| device->devtype == TYPE_ZBC) { |
| device->aio_handle = |
| phys_lun_ext_entry->aio_handle; |
| pqi_get_physical_disk_info(ctrl_info, |
| device, id_phys); |
| } |
| } |
| break; |
| } |
| |
| new_device_list[num_valid_devices++] = device; |
| } |
| |
| pqi_update_device_list(ctrl_info, new_device_list, num_valid_devices); |
| |
| out: |
| list_for_each_entry_safe(device, next, &new_device_list_head, |
| new_device_list_entry) { |
| if (device->keep_device) |
| continue; |
| list_del(&device->new_device_list_entry); |
| pqi_free_device(device); |
| } |
| |
| kfree(new_device_list); |
| kfree(physdev_list); |
| kfree(logdev_list); |
| kfree(id_phys); |
| |
| return rc; |
| } |
| |
| static void pqi_remove_all_scsi_devices(struct pqi_ctrl_info *ctrl_info) |
| { |
| unsigned long flags; |
| struct pqi_scsi_dev *device; |
| |
| while (1) { |
| spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); |
| |
| device = list_first_entry_or_null(&ctrl_info->scsi_device_list, |
| struct pqi_scsi_dev, scsi_device_list_entry); |
| if (device) |
| list_del(&device->scsi_device_list_entry); |
| |
| spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, |
| flags); |
| |
| if (!device) |
| break; |
| |
| if (device->sdev) |
| pqi_remove_device(ctrl_info, device); |
| pqi_free_device(device); |
| } |
| } |
| |
| static int pqi_scan_scsi_devices(struct pqi_ctrl_info *ctrl_info) |
| { |
| int rc; |
| |
| if (pqi_ctrl_offline(ctrl_info)) |
| return -ENXIO; |
| |
| mutex_lock(&ctrl_info->scan_mutex); |
| |
| rc = pqi_update_scsi_devices(ctrl_info); |
| if (rc) |
| pqi_schedule_rescan_worker_delayed(ctrl_info); |
| |
| mutex_unlock(&ctrl_info->scan_mutex); |
| |
| return rc; |
| } |
| |
| static void pqi_scan_start(struct Scsi_Host *shost) |
| { |
| pqi_scan_scsi_devices(shost_to_hba(shost)); |
| } |
| |
| /* Returns TRUE if scan is finished. */ |
| |
| static int pqi_scan_finished(struct Scsi_Host *shost, |
| unsigned long elapsed_time) |
| { |
| struct pqi_ctrl_info *ctrl_info; |
| |
| ctrl_info = shost_priv(shost); |
| |
| return !mutex_is_locked(&ctrl_info->scan_mutex); |
| } |
| |
| static void pqi_wait_until_scan_finished(struct pqi_ctrl_info *ctrl_info) |
| { |
| mutex_lock(&ctrl_info->scan_mutex); |
| mutex_unlock(&ctrl_info->scan_mutex); |
| } |
| |
| static void pqi_wait_until_lun_reset_finished(struct pqi_ctrl_info *ctrl_info) |
| { |
| mutex_lock(&ctrl_info->lun_reset_mutex); |
| mutex_unlock(&ctrl_info->lun_reset_mutex); |
| } |
| |
| static inline void pqi_set_encryption_info( |
| struct pqi_encryption_info *encryption_info, struct raid_map *raid_map, |
| u64 first_block) |
| { |
| u32 volume_blk_size; |
| |
| /* |
| * Set the encryption tweak values based on logical block address. |
| * If the block size is 512, the tweak value is equal to the LBA. |
| * For other block sizes, tweak value is (LBA * block size) / 512. |
| */ |
| volume_blk_size = get_unaligned_le32(&raid_map->volume_blk_size); |
| if (volume_blk_size != 512) |
| first_block = (first_block * volume_blk_size) / 512; |
| |
| encryption_info->data_encryption_key_index = |
| get_unaligned_le16(&raid_map->data_encryption_key_index); |
| encryption_info->encrypt_tweak_lower = lower_32_bits(first_block); |
| encryption_info->encrypt_tweak_upper = upper_32_bits(first_block); |
| } |
| |
| /* |
| * Attempt to perform RAID bypass mapping for a logical volume I/O. |
| */ |
| |
| #define PQI_RAID_BYPASS_INELIGIBLE 1 |
| |
| static int pqi_raid_bypass_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_scsi_dev *device, struct scsi_cmnd *scmd, |
| struct pqi_queue_group *queue_group) |
| { |
| struct raid_map *raid_map; |
| bool is_write = false; |
| u32 map_index; |
| u64 first_block; |
| u64 last_block; |
| u32 block_cnt; |
| u32 blocks_per_row; |
| u64 first_row; |
| u64 last_row; |
| u32 first_row_offset; |
| u32 last_row_offset; |
| u32 first_column; |
| u32 last_column; |
| u64 r0_first_row; |
| u64 r0_last_row; |
| u32 r5or6_blocks_per_row; |
| u64 r5or6_first_row; |
| u64 r5or6_last_row; |
| u32 r5or6_first_row_offset; |
| u32 r5or6_last_row_offset; |
| u32 r5or6_first_column; |
| u32 r5or6_last_column; |
| u16 data_disks_per_row; |
| u32 total_disks_per_row; |
| u16 layout_map_count; |
| u32 stripesize; |
| u16 strip_size; |
| u32 first_group; |
| u32 last_group; |
| u32 current_group; |
| u32 map_row; |
| u32 aio_handle; |
| u64 disk_block; |
| u32 disk_block_cnt; |
| u8 cdb[16]; |
| u8 cdb_length; |
| int offload_to_mirror; |
| struct pqi_encryption_info *encryption_info_ptr; |
| struct pqi_encryption_info encryption_info; |
| #if BITS_PER_LONG == 32 |
| u64 tmpdiv; |
| #endif |
| |
| /* Check for valid opcode, get LBA and block count. */ |
| switch (scmd->cmnd[0]) { |
| case WRITE_6: |
| is_write = true; |
| /* fall through */ |
| case READ_6: |
| first_block = (u64)(((scmd->cmnd[1] & 0x1f) << 16) | |
| (scmd->cmnd[2] << 8) | scmd->cmnd[3]); |
| block_cnt = (u32)scmd->cmnd[4]; |
| if (block_cnt == 0) |
| block_cnt = 256; |
| break; |
| case WRITE_10: |
| is_write = true; |
| /* fall through */ |
| case READ_10: |
| first_block = (u64)get_unaligned_be32(&scmd->cmnd[2]); |
| block_cnt = (u32)get_unaligned_be16(&scmd->cmnd[7]); |
| break; |
| case WRITE_12: |
| is_write = true; |
| /* fall through */ |
| case READ_12: |
| first_block = (u64)get_unaligned_be32(&scmd->cmnd[2]); |
| block_cnt = get_unaligned_be32(&scmd->cmnd[6]); |
| break; |
| case WRITE_16: |
| is_write = true; |
| /* fall through */ |
| case READ_16: |
| first_block = get_unaligned_be64(&scmd->cmnd[2]); |
| block_cnt = get_unaligned_be32(&scmd->cmnd[10]); |
| break; |
| default: |
| /* Process via normal I/O path. */ |
| return PQI_RAID_BYPASS_INELIGIBLE; |
| } |
| |
| /* Check for write to non-RAID-0. */ |
| if (is_write && device->raid_level != SA_RAID_0) |
| return PQI_RAID_BYPASS_INELIGIBLE; |
| |
| if (unlikely(block_cnt == 0)) |
| return PQI_RAID_BYPASS_INELIGIBLE; |
| |
| last_block = first_block + block_cnt - 1; |
| raid_map = device->raid_map; |
| |
| /* Check for invalid block or wraparound. */ |
| if (last_block >= get_unaligned_le64(&raid_map->volume_blk_cnt) || |
| last_block < first_block) |
| return PQI_RAID_BYPASS_INELIGIBLE; |
| |
| data_disks_per_row = get_unaligned_le16(&raid_map->data_disks_per_row); |
| strip_size = get_unaligned_le16(&raid_map->strip_size); |
| layout_map_count = get_unaligned_le16(&raid_map->layout_map_count); |
| |
| /* Calculate stripe information for the request. */ |
| blocks_per_row = data_disks_per_row * strip_size; |
| #if BITS_PER_LONG == 32 |
| tmpdiv = first_block; |
| do_div(tmpdiv, blocks_per_row); |
| first_row = tmpdiv; |
| tmpdiv = last_block; |
| do_div(tmpdiv, blocks_per_row); |
| last_row = tmpdiv; |
| first_row_offset = (u32)(first_block - (first_row * blocks_per_row)); |
| last_row_offset = (u32)(last_block - (last_row * blocks_per_row)); |
| tmpdiv = first_row_offset; |
| do_div(tmpdiv, strip_size); |
| first_column = tmpdiv; |
| tmpdiv = last_row_offset; |
| do_div(tmpdiv, strip_size); |
| last_column = tmpdiv; |
| #else |
| first_row = first_block / blocks_per_row; |
| last_row = last_block / blocks_per_row; |
| first_row_offset = (u32)(first_block - (first_row * blocks_per_row)); |
| last_row_offset = (u32)(last_block - (last_row * blocks_per_row)); |
| first_column = first_row_offset / strip_size; |
| last_column = last_row_offset / strip_size; |
| #endif |
| |
| /* If this isn't a single row/column then give to the controller. */ |
| if (first_row != last_row || first_column != last_column) |
| return PQI_RAID_BYPASS_INELIGIBLE; |
| |
| /* Proceeding with driver mapping. */ |
| total_disks_per_row = data_disks_per_row + |
| get_unaligned_le16(&raid_map->metadata_disks_per_row); |
| map_row = ((u32)(first_row >> raid_map->parity_rotation_shift)) % |
| get_unaligned_le16(&raid_map->row_cnt); |
| map_index = (map_row * total_disks_per_row) + first_column; |
| |
| /* RAID 1 */ |
| if (device->raid_level == SA_RAID_1) { |
| if (device->offload_to_mirror) |
| map_index += data_disks_per_row; |
| device->offload_to_mirror = !device->offload_to_mirror; |
| } else if (device->raid_level == SA_RAID_ADM) { |
| /* RAID ADM */ |
| /* |
| * Handles N-way mirrors (R1-ADM) and R10 with # of drives |
| * divisible by 3. |
| */ |
| offload_to_mirror = device->offload_to_mirror; |
| if (offload_to_mirror == 0) { |
| /* use physical disk in the first mirrored group. */ |
| map_index %= data_disks_per_row; |
| } else { |
| do { |
| /* |
| * Determine mirror group that map_index |
| * indicates. |
| */ |
| current_group = map_index / data_disks_per_row; |
| |
| if (offload_to_mirror != current_group) { |
| if (current_group < |
| layout_map_count - 1) { |
| /* |
| * Select raid index from |
| * next group. |
| */ |
| map_index += data_disks_per_row; |
| current_group++; |
| } else { |
| /* |
| * Select raid index from first |
| * group. |
| */ |
| map_index %= data_disks_per_row; |
| current_group = 0; |
| } |
| } |
| } while (offload_to_mirror != current_group); |
| } |
| |
| /* Set mirror group to use next time. */ |
| offload_to_mirror = |
| (offload_to_mirror >= layout_map_count - 1) ? |
| 0 : offload_to_mirror + 1; |
| WARN_ON(offload_to_mirror >= layout_map_count); |
| device->offload_to_mirror = offload_to_mirror; |
| /* |
| * Avoid direct use of device->offload_to_mirror within this |
| * function since multiple threads might simultaneously |
| * increment it beyond the range of device->layout_map_count -1. |
| */ |
| } else if ((device->raid_level == SA_RAID_5 || |
| device->raid_level == SA_RAID_6) && layout_map_count > 1) { |
| /* RAID 50/60 */ |
| /* Verify first and last block are in same RAID group */ |
| r5or6_blocks_per_row = strip_size * data_disks_per_row; |
| stripesize = r5or6_blocks_per_row * layout_map_count; |
| #if BITS_PER_LONG == 32 |
| tmpdiv = first_block; |
| first_group = do_div(tmpdiv, stripesize); |
| tmpdiv = first_group; |
| do_div(tmpdiv, r5or6_blocks_per_row); |
| first_group = tmpdiv; |
| tmpdiv = last_block; |
| last_group = do_div(tmpdiv, stripesize); |
| tmpdiv = last_group; |
| do_div(tmpdiv, r5or6_blocks_per_row); |
| last_group = tmpdiv; |
| #else |
| first_group = (first_block % stripesize) / r5or6_blocks_per_row; |
| last_group = (last_block % stripesize) / r5or6_blocks_per_row; |
| #endif |
| if (first_group != last_group) |
| return PQI_RAID_BYPASS_INELIGIBLE; |
| |
| /* Verify request is in a single row of RAID 5/6 */ |
| #if BITS_PER_LONG == 32 |
| tmpdiv = first_block; |
| do_div(tmpdiv, stripesize); |
| first_row = r5or6_first_row = r0_first_row = tmpdiv; |
| tmpdiv = last_block; |
| do_div(tmpdiv, stripesize); |
| r5or6_last_row = r0_last_row = tmpdiv; |
| #else |
| first_row = r5or6_first_row = r0_first_row = |
| first_block / stripesize; |
| r5or6_last_row = r0_last_row = last_block / stripesize; |
| #endif |
| if (r5or6_first_row != r5or6_last_row) |
| return PQI_RAID_BYPASS_INELIGIBLE; |
| |
| /* Verify request is in a single column */ |
| #if BITS_PER_LONG == 32 |
| tmpdiv = first_block; |
| first_row_offset = do_div(tmpdiv, stripesize); |
| tmpdiv = first_row_offset; |
| first_row_offset = (u32)do_div(tmpdiv, r5or6_blocks_per_row); |
| r5or6_first_row_offset = first_row_offset; |
| tmpdiv = last_block; |
| r5or6_last_row_offset = do_div(tmpdiv, stripesize); |
| tmpdiv = r5or6_last_row_offset; |
| r5or6_last_row_offset = do_div(tmpdiv, r5or6_blocks_per_row); |
| tmpdiv = r5or6_first_row_offset; |
| do_div(tmpdiv, strip_size); |
| first_column = r5or6_first_column = tmpdiv; |
| tmpdiv = r5or6_last_row_offset; |
| do_div(tmpdiv, strip_size); |
| r5or6_last_column = tmpdiv; |
| #else |
| first_row_offset = r5or6_first_row_offset = |
| (u32)((first_block % stripesize) % |
| r5or6_blocks_per_row); |
| |
| r5or6_last_row_offset = |
| (u32)((last_block % stripesize) % |
| r5or6_blocks_per_row); |
| |
| first_column = r5or6_first_row_offset / strip_size; |
| r5or6_first_column = first_column; |
| r5or6_last_column = r5or6_last_row_offset / strip_size; |
| #endif |
| if (r5or6_first_column != r5or6_last_column) |
| return PQI_RAID_BYPASS_INELIGIBLE; |
| |
| /* Request is eligible */ |
| map_row = |
| ((u32)(first_row >> raid_map->parity_rotation_shift)) % |
| get_unaligned_le16(&raid_map->row_cnt); |
| |
| map_index = (first_group * |
| (get_unaligned_le16(&raid_map->row_cnt) * |
| total_disks_per_row)) + |
| (map_row * total_disks_per_row) + first_column; |
| } |
| |
| if (unlikely(map_index >= RAID_MAP_MAX_ENTRIES)) |
| return PQI_RAID_BYPASS_INELIGIBLE; |
| |
| aio_handle = raid_map->disk_data[map_index].aio_handle; |
| disk_block = get_unaligned_le64(&raid_map->disk_starting_blk) + |
| first_row * strip_size + |
| (first_row_offset - first_column * strip_size); |
| disk_block_cnt = block_cnt; |
| |
| /* Handle differing logical/physical block sizes. */ |
| if (raid_map->phys_blk_shift) { |
| disk_block <<= raid_map->phys_blk_shift; |
| disk_block_cnt <<= raid_map->phys_blk_shift; |
| } |
| |
| if (unlikely(disk_block_cnt > 0xffff)) |
| return PQI_RAID_BYPASS_INELIGIBLE; |
| |
| /* Build the new CDB for the physical disk I/O. */ |
| if (disk_block > 0xffffffff) { |
| cdb[0] = is_write ? WRITE_16 : READ_16; |
| cdb[1] = 0; |
| put_unaligned_be64(disk_block, &cdb[2]); |
| put_unaligned_be32(disk_block_cnt, &cdb[10]); |
| cdb[14] = 0; |
| cdb[15] = 0; |
| cdb_length = 16; |
| } else { |
| cdb[0] = is_write ? WRITE_10 : READ_10; |
| cdb[1] = 0; |
| put_unaligned_be32((u32)disk_block, &cdb[2]); |
| cdb[6] = 0; |
| put_unaligned_be16((u16)disk_block_cnt, &cdb[7]); |
| cdb[9] = 0; |
| cdb_length = 10; |
| } |
| |
| if (get_unaligned_le16(&raid_map->flags) & |
| RAID_MAP_ENCRYPTION_ENABLED) { |
| pqi_set_encryption_info(&encryption_info, raid_map, |
| first_block); |
| encryption_info_ptr = &encryption_info; |
| } else { |
| encryption_info_ptr = NULL; |
| } |
| |
| return pqi_aio_submit_io(ctrl_info, scmd, aio_handle, |
| cdb, cdb_length, queue_group, encryption_info_ptr, true); |
| } |
| |
| #define PQI_STATUS_IDLE 0x0 |
| |
| #define PQI_CREATE_ADMIN_QUEUE_PAIR 1 |
| #define PQI_DELETE_ADMIN_QUEUE_PAIR 2 |
| |
| #define PQI_DEVICE_STATE_POWER_ON_AND_RESET 0x0 |
| #define PQI_DEVICE_STATE_STATUS_AVAILABLE 0x1 |
| #define PQI_DEVICE_STATE_ALL_REGISTERS_READY 0x2 |
| #define PQI_DEVICE_STATE_ADMIN_QUEUE_PAIR_READY 0x3 |
| #define PQI_DEVICE_STATE_ERROR 0x4 |
| |
| #define PQI_MODE_READY_TIMEOUT_SECS 30 |
| #define PQI_MODE_READY_POLL_INTERVAL_MSECS 1 |
| |
| static int pqi_wait_for_pqi_mode_ready(struct pqi_ctrl_info *ctrl_info) |
| { |
| struct pqi_device_registers __iomem *pqi_registers; |
| unsigned long timeout; |
| u64 signature; |
| u8 status; |
| |
| pqi_registers = ctrl_info->pqi_registers; |
| timeout = (PQI_MODE_READY_TIMEOUT_SECS * HZ) + jiffies; |
| |
| while (1) { |
| signature = readq(&pqi_registers->signature); |
| if (memcmp(&signature, PQI_DEVICE_SIGNATURE, |
| sizeof(signature)) == 0) |
| break; |
| if (time_after(jiffies, timeout)) { |
| dev_err(&ctrl_info->pci_dev->dev, |
| "timed out waiting for PQI signature\n"); |
| return -ETIMEDOUT; |
| } |
| msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS); |
| } |
| |
| while (1) { |
| status = readb(&pqi_registers->function_and_status_code); |
| if (status == PQI_STATUS_IDLE) |
| break; |
| if (time_after(jiffies, timeout)) { |
| dev_err(&ctrl_info->pci_dev->dev, |
| "timed out waiting for PQI IDLE\n"); |
| return -ETIMEDOUT; |
| } |
| msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS); |
| } |
| |
| while (1) { |
| if (readl(&pqi_registers->device_status) == |
| PQI_DEVICE_STATE_ALL_REGISTERS_READY) |
| break; |
| if (time_after(jiffies, timeout)) { |
| dev_err(&ctrl_info->pci_dev->dev, |
| "timed out waiting for PQI all registers ready\n"); |
| return -ETIMEDOUT; |
| } |
| msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS); |
| } |
| |
| return 0; |
| } |
| |
| static inline void pqi_aio_path_disabled(struct pqi_io_request *io_request) |
| { |
| struct pqi_scsi_dev *device; |
| |
| device = io_request->scmd->device->hostdata; |
| device->raid_bypass_enabled = false; |
| device->aio_enabled = false; |
| } |
| |
| static inline void pqi_take_device_offline(struct scsi_device *sdev, char *path) |
| { |
| struct pqi_ctrl_info *ctrl_info; |
| struct pqi_scsi_dev *device; |
| |
| device = sdev->hostdata; |
| if (device->device_offline) |
| return; |
| |
| device->device_offline = true; |
| scsi_device_set_state(sdev, SDEV_OFFLINE); |
| ctrl_info = shost_to_hba(sdev->host); |
| pqi_schedule_rescan_worker(ctrl_info); |
| dev_err(&ctrl_info->pci_dev->dev, "offlined %s scsi %d:%d:%d:%d\n", |
| path, ctrl_info->scsi_host->host_no, device->bus, |
| device->target, device->lun); |
| } |
| |
| static void pqi_process_raid_io_error(struct pqi_io_request *io_request) |
| { |
| u8 scsi_status; |
| u8 host_byte; |
| struct scsi_cmnd *scmd; |
| struct pqi_raid_error_info *error_info; |
| size_t sense_data_length; |
| int residual_count; |
| int xfer_count; |
| struct scsi_sense_hdr sshdr; |
| |
| scmd = io_request->scmd; |
| if (!scmd) |
| return; |
| |
| error_info = io_request->error_info; |
| scsi_status = error_info->status; |
| host_byte = DID_OK; |
| |
| switch (error_info->data_out_result) { |
| case PQI_DATA_IN_OUT_GOOD: |
| break; |
| case PQI_DATA_IN_OUT_UNDERFLOW: |
| xfer_count = |
| get_unaligned_le32(&error_info->data_out_transferred); |
| residual_count = scsi_bufflen(scmd) - xfer_count; |
| scsi_set_resid(scmd, residual_count); |
| if (xfer_count < scmd->underflow) |
| host_byte = DID_SOFT_ERROR; |
| break; |
| case PQI_DATA_IN_OUT_UNSOLICITED_ABORT: |
| case PQI_DATA_IN_OUT_ABORTED: |
| host_byte = DID_ABORT; |
| break; |
| case PQI_DATA_IN_OUT_TIMEOUT: |
| host_byte = DID_TIME_OUT; |
| break; |
| case PQI_DATA_IN_OUT_BUFFER_OVERFLOW: |
| case PQI_DATA_IN_OUT_PROTOCOL_ERROR: |
| case PQI_DATA_IN_OUT_BUFFER_ERROR: |
| case PQI_DATA_IN_OUT_BUFFER_OVERFLOW_DESCRIPTOR_AREA: |
| case PQI_DATA_IN_OUT_BUFFER_OVERFLOW_BRIDGE: |
| case PQI_DATA_IN_OUT_ERROR: |
| case PQI_DATA_IN_OUT_HARDWARE_ERROR: |
| case PQI_DATA_IN_OUT_PCIE_FABRIC_ERROR: |
| case PQI_DATA_IN_OUT_PCIE_COMPLETION_TIMEOUT: |
| case PQI_DATA_IN_OUT_PCIE_COMPLETER_ABORT_RECEIVED: |
| case PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST_RECEIVED: |
| case PQI_DATA_IN_OUT_PCIE_ECRC_CHECK_FAILED: |
| case PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST: |
| case PQI_DATA_IN_OUT_PCIE_ACS_VIOLATION: |
| case PQI_DATA_IN_OUT_PCIE_TLP_PREFIX_BLOCKED: |
| case PQI_DATA_IN_OUT_PCIE_POISONED_MEMORY_READ: |
| default: |
| host_byte = DID_ERROR; |
| break; |
| } |
| |
| sense_data_length = get_unaligned_le16(&error_info->sense_data_length); |
| if (sense_data_length == 0) |
| sense_data_length = |
| get_unaligned_le16(&error_info->response_data_length); |
| if (sense_data_length) { |
| if (sense_data_length > sizeof(error_info->data)) |
| sense_data_length = sizeof(error_info->data); |
| |
| if (scsi_status == SAM_STAT_CHECK_CONDITION && |
| scsi_normalize_sense(error_info->data, |
| sense_data_length, &sshdr) && |
| sshdr.sense_key == HARDWARE_ERROR && |
| sshdr.asc == 0x3e && |
| sshdr.ascq == 0x1) { |
| pqi_take_device_offline(scmd->device, "RAID"); |
| host_byte = DID_NO_CONNECT; |
| } |
| |
| if (sense_data_length > SCSI_SENSE_BUFFERSIZE) |
| sense_data_length = SCSI_SENSE_BUFFERSIZE; |
| memcpy(scmd->sense_buffer, error_info->data, |
| sense_data_length); |
| } |
| |
| scmd->result = scsi_status; |
| set_host_byte(scmd, host_byte); |
| } |
| |
| static void pqi_process_aio_io_error(struct pqi_io_request *io_request) |
| { |
| u8 scsi_status; |
| u8 host_byte; |
| struct scsi_cmnd *scmd; |
| struct pqi_aio_error_info *error_info; |
| size_t sense_data_length; |
| int residual_count; |
| int xfer_count; |
| bool device_offline; |
| |
| scmd = io_request->scmd; |
| error_info = io_request->error_info; |
| host_byte = DID_OK; |
| sense_data_length = 0; |
| device_offline = false; |
| |
| switch (error_info->service_response) { |
| case PQI_AIO_SERV_RESPONSE_COMPLETE: |
| scsi_status = error_info->status; |
| break; |
| case PQI_AIO_SERV_RESPONSE_FAILURE: |
| switch (error_info->status) { |
| case PQI_AIO_STATUS_IO_ABORTED: |
| scsi_status = SAM_STAT_TASK_ABORTED; |
| break; |
| case PQI_AIO_STATUS_UNDERRUN: |
| scsi_status = SAM_STAT_GOOD; |
| residual_count = get_unaligned_le32( |
| &error_info->residual_count); |
| scsi_set_resid(scmd, residual_count); |
| xfer_count = scsi_bufflen(scmd) - residual_count; |
| if (xfer_count < scmd->underflow) |
| host_byte = DID_SOFT_ERROR; |
| break; |
| case PQI_AIO_STATUS_OVERRUN: |
| scsi_status = SAM_STAT_GOOD; |
| break; |
| case PQI_AIO_STATUS_AIO_PATH_DISABLED: |
| pqi_aio_path_disabled(io_request); |
| scsi_status = SAM_STAT_GOOD; |
| io_request->status = -EAGAIN; |
| break; |
| case PQI_AIO_STATUS_NO_PATH_TO_DEVICE: |
| case PQI_AIO_STATUS_INVALID_DEVICE: |
| if (!io_request->raid_bypass) { |
| device_offline = true; |
| pqi_take_device_offline(scmd->device, "AIO"); |
| host_byte = DID_NO_CONNECT; |
| } |
| scsi_status = SAM_STAT_CHECK_CONDITION; |
| break; |
| case PQI_AIO_STATUS_IO_ERROR: |
| default: |
| scsi_status = SAM_STAT_CHECK_CONDITION; |
| break; |
| } |
| break; |
| case PQI_AIO_SERV_RESPONSE_TMF_COMPLETE: |
| case PQI_AIO_SERV_RESPONSE_TMF_SUCCEEDED: |
| scsi_status = SAM_STAT_GOOD; |
| break; |
| case PQI_AIO_SERV_RESPONSE_TMF_REJECTED: |
| case PQI_AIO_SERV_RESPONSE_TMF_INCORRECT_LUN: |
| default: |
| scsi_status = SAM_STAT_CHECK_CONDITION; |
| break; |
| } |
| |
| if (error_info->data_present) { |
| sense_data_length = |
| get_unaligned_le16(&error_info->data_length); |
| if (sense_data_length) { |
| if (sense_data_length > sizeof(error_info->data)) |
| sense_data_length = sizeof(error_info->data); |
| if (sense_data_length > SCSI_SENSE_BUFFERSIZE) |
| sense_data_length = SCSI_SENSE_BUFFERSIZE; |
| memcpy(scmd->sense_buffer, error_info->data, |
| sense_data_length); |
| } |
| } |
| |
| if (device_offline && sense_data_length == 0) |
| scsi_build_sense_buffer(0, scmd->sense_buffer, HARDWARE_ERROR, |
| 0x3e, 0x1); |
| |
| scmd->result = scsi_status; |
| set_host_byte(scmd, host_byte); |
| } |
| |
| static void pqi_process_io_error(unsigned int iu_type, |
| struct pqi_io_request *io_request) |
| { |
| switch (iu_type) { |
| case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR: |
| pqi_process_raid_io_error(io_request); |
| break; |
| case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR: |
| pqi_process_aio_io_error(io_request); |
| break; |
| } |
| } |
| |
| static int pqi_interpret_task_management_response( |
| struct pqi_task_management_response *response) |
| { |
| int rc; |
| |
| switch (response->response_code) { |
| case SOP_TMF_COMPLETE: |
| case SOP_TMF_FUNCTION_SUCCEEDED: |
| rc = 0; |
| break; |
| default: |
| rc = -EIO; |
| break; |
| } |
| |
| return rc; |
| } |
| |
| static unsigned int pqi_process_io_intr(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_queue_group *queue_group) |
| { |
| unsigned int num_responses; |
| pqi_index_t oq_pi; |
| pqi_index_t oq_ci; |
| struct pqi_io_request *io_request; |
| struct pqi_io_response *response; |
| u16 request_id; |
| |
| num_responses = 0; |
| oq_ci = queue_group->oq_ci_copy; |
| |
| while (1) { |
| oq_pi = readl(queue_group->oq_pi); |
| if (oq_pi == oq_ci) |
| break; |
| |
| num_responses++; |
| response = queue_group->oq_element_array + |
| (oq_ci * PQI_OPERATIONAL_OQ_ELEMENT_LENGTH); |
| |
| request_id = get_unaligned_le16(&response->request_id); |
| WARN_ON(request_id >= ctrl_info->max_io_slots); |
| |
| io_request = &ctrl_info->io_request_pool[request_id]; |
| WARN_ON(atomic_read(&io_request->refcount) == 0); |
| |
| switch (response->header.iu_type) { |
| case PQI_RESPONSE_IU_RAID_PATH_IO_SUCCESS: |
| case PQI_RESPONSE_IU_AIO_PATH_IO_SUCCESS: |
| if (io_request->scmd) |
| io_request->scmd->result = 0; |
| /* fall through */ |
| case PQI_RESPONSE_IU_GENERAL_MANAGEMENT: |
| break; |
| case PQI_RESPONSE_IU_TASK_MANAGEMENT: |
| io_request->status = |
| pqi_interpret_task_management_response( |
| (void *)response); |
| break; |
| case PQI_RESPONSE_IU_AIO_PATH_DISABLED: |
| pqi_aio_path_disabled(io_request); |
| io_request->status = -EAGAIN; |
| break; |
| case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR: |
| case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR: |
| io_request->error_info = ctrl_info->error_buffer + |
| (get_unaligned_le16(&response->error_index) * |
| PQI_ERROR_BUFFER_ELEMENT_LENGTH); |
| pqi_process_io_error(response->header.iu_type, |
| io_request); |
| break; |
| default: |
| dev_err(&ctrl_info->pci_dev->dev, |
| "unexpected IU type: 0x%x\n", |
| response->header.iu_type); |
| break; |
| } |
| |
| io_request->io_complete_callback(io_request, |
| io_request->context); |
| |
| /* |
| * Note that the I/O request structure CANNOT BE TOUCHED after |
| * returning from the I/O completion callback! |
| */ |
| |
| oq_ci = (oq_ci + 1) % ctrl_info->num_elements_per_oq; |
| } |
| |
| if (num_responses) { |
| queue_group->oq_ci_copy = oq_ci; |
| writel(oq_ci, queue_group->oq_ci); |
| } |
| |
| return num_responses; |
| } |
| |
| static inline unsigned int pqi_num_elements_free(unsigned int pi, |
| unsigned int ci, unsigned int elements_in_queue) |
| { |
| unsigned int num_elements_used; |
| |
| if (pi >= ci) |
| num_elements_used = pi - ci; |
| else |
| num_elements_used = elements_in_queue - ci + pi; |
| |
| return elements_in_queue - num_elements_used - 1; |
| } |
| |
| static void pqi_send_event_ack(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_event_acknowledge_request *iu, size_t iu_length) |
| { |
| pqi_index_t iq_pi; |
| pqi_index_t iq_ci; |
| unsigned long flags; |
| void *next_element; |
| struct pqi_queue_group *queue_group; |
| |
| queue_group = &ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP]; |
| put_unaligned_le16(queue_group->oq_id, &iu->header.response_queue_id); |
| |
| while (1) { |
| spin_lock_irqsave(&queue_group->submit_lock[RAID_PATH], flags); |
| |
| iq_pi = queue_group->iq_pi_copy[RAID_PATH]; |
| iq_ci = readl(queue_group->iq_ci[RAID_PATH]); |
| |
| if (pqi_num_elements_free(iq_pi, iq_ci, |
| ctrl_info->num_elements_per_iq)) |
| break; |
| |
| spin_unlock_irqrestore( |
| &queue_group->submit_lock[RAID_PATH], flags); |
| |
| if (pqi_ctrl_offline(ctrl_info)) |
| return; |
| } |
| |
| next_element = queue_group->iq_element_array[RAID_PATH] + |
| (iq_pi * PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); |
| |
| memcpy(next_element, iu, iu_length); |
| |
| iq_pi = (iq_pi + 1) % ctrl_info->num_elements_per_iq; |
| queue_group->iq_pi_copy[RAID_PATH] = iq_pi; |
| |
| /* |
| * This write notifies the controller that an IU is available to be |
| * processed. |
| */ |
| writel(iq_pi, queue_group->iq_pi[RAID_PATH]); |
| |
| spin_unlock_irqrestore(&queue_group->submit_lock[RAID_PATH], flags); |
| } |
| |
| static void pqi_acknowledge_event(struct pqi_ctrl_info *ctrl_info, |
| struct pqi_event *event) |
| { |
| struct pqi_event_acknowledge_request request; |
| |
| memset(&request, 0, sizeof(request)); |
| |
| request.header.iu_type = PQI_REQUEST_IU_ACKNOWLEDGE_VENDOR_EVENT; |
| put_unaligned_le16(sizeof(request) - PQI_REQUEST_HEADER_LENGTH, |
| &request.header.iu_length); |
| request.event_type = event->event_type; |
| request.event_id = event->event_id; |
| request.additional_event_id = event->additional_event_id; |
| |
| pqi_send_event_ack(ctrl_info, &request, sizeof(request)); |
| } |
| |
| static void pqi_event_worker(struct work_struct *work) |
| { |
| unsigned int i; |
| struct pqi_ctrl_info *ctrl_info; |
| struct pqi_event *event; |
| |
| ctrl_info = container_of(work, struct pqi_ctrl_info, event_work); |
| |
| pqi_ctrl_busy(ctrl_info); |
| pqi_wait_if_ctrl_blocked(ctrl_info, NO_TIMEOUT); |
| if (pqi_ctrl_offline(ctrl_info)) |
| goto out; |
| |
| pqi_schedule_rescan_worker_delayed(ctrl_info); |
| |
| event = ctrl_info->events; |
| for (i = 0; i < PQI_NUM_SUPPORTED_EVENTS; i++) { |
| if (event->pending) { |
| event->pending = false; |
| pqi_acknowledge_event(ctrl_info, event); |
| } |
| event++; |
| } |
| |
| out: |
| pqi_ctrl_unbusy(ctrl_info); |
| } |
| |
| #define PQI_HEARTBEAT_TIMER_INTERVAL (10 * HZ) |
| |
| static void pqi_heartbeat_timer_handler(struct timer_list *t) |
| { |
| int num_interrupts; |
| u32 heartbeat_count; |
| struct pqi_ctrl_info *ctrl_info = from_timer(ctrl_info, t, |
| heartbeat_timer); |
| |
| pqi_check_ctrl_health(ctrl_info); |
| if (pqi_ctrl_offline(ctrl_info)) |
| return; |
| |
| num_interrupts = atomic_read(&ctrl_info->num_interrupts); |
| heartbeat_count = pqi_read_heartbeat_counter(ctrl_info); |
| |
| if (num_interrupts == ctrl_info->previous_num_interrupts) { |
| if (heartbeat_count == ctrl_info->previous_heartbeat_count) { |
| dev_err(&ctrl_info->pci_dev->dev, |
| "no heartbeat detected - last heartbeat count: %u\n", |
| heartbeat_count); |
| pqi_take_ctrl_offline(ctrl_info); |
| return; |
| } |
| } else { |
| ctrl_info->previous_num_interrupts = num_interrupts; |
| } |
| |
| ctrl_info->previous_heartbeat_count = heartbeat_count; |
| mod_timer(&ctrl_info->heartbeat_timer, |
| jiffies + PQI_HEARTBEAT_TIMER_INTERVAL); |
| } |
| |
| static void pqi_start_heartbeat_timer(struct pqi_ctrl_info *ctrl_info) |
| { |
| if (!ctrl_info->heartbeat_counter) |
| return; |
| |
| ctrl_info->previous_num_interrupts = |
| atomic_read(&ctrl_info->num_interrupts); |
| ctrl_info->previous_heartbeat_count = |
| pqi_read_heartbeat_counter(ctrl_info); |
| |
| ctrl_info->heartbeat_timer.expires = |
| jiffies + PQI_HEARTBEAT_TIMER_INTERVAL; |
| add_timer(&ctrl_info->heartbeat_timer); |
| } |
| |
| static inline void pqi_stop_heartbeat_timer(struct pqi_ctrl_info *ctrl_info) |
| { |
| del_timer_sync(&ctrl_info->heartbeat_timer); |
| } |
| |
| static inline int pqi_event_type_to_event_index(unsigned int event_type) |
| { |
| int index; |
| |
| for (index = 0; index < ARRAY_SIZE(pqi_supported_event_types); index++) |
| if (event_type == pqi_supported_event_types[index]) |
| return index; |
| |
| return -1; |
| } |
| |
| static inline bool pqi_is_supported_event(unsigned int event_type) |
| { |
| return pqi_event_type_to_event_index(event_type) != -1; |
| } |
| |
| static unsigned int pqi_process_event_intr(struct pqi_ctrl_info *ctrl_info) |
| { |
| unsigned int num_events; |
| pqi_index_t oq_pi; |
| pqi_index_t oq_ci; |
| struct pqi_event_queue *event_queue; |
| struct pqi_event_response *response; |
| struct pqi_event *event; |
| int event_index; |
| |
| event_queue = &ctrl_info->event_queue; |
| num_events = 0; |
| oq_ci = event_queue->oq_ci_copy; |
| |
| while (1) { |
| oq_pi = readl(event_queue->oq_pi); |
| if (oq_pi == oq_ci) |
| break; |
| |
| num_events++; |
| response = event_queue->oq_element_array + |
| (oq_ci * PQI_EVENT_OQ_ELEMENT_LENGTH); |
| |
| event_index = |
| pqi_event_type_to_event_index(response->event_type); |
| |
| if (event_index >= 0) { |
| if (response->request_acknowlege) { |
| event = &ctrl_info->events[event_index]; |
| event->pending = true; |
| event->event_type = response->event_type; |
| event->event_id = response->event_id; |
| event->additional_event_id = |
| response->additional_event_id; |
| } |
| } |
| |
| oq_ci = (oq_ci + 1) % PQI_NUM_EVENT_QUEUE_ELEMENTS; |
| } |
| |
| if (num_events) { |
| event_queue->oq_ci_copy = oq_ci; |
| writel(oq_ci, event_queue->oq_ci); |
| schedule_work(&ctrl_info->event_work); |
| } |
| |
| return num_events; |
| } |
| |
| #define PQI_LEGACY_INTX_MASK 0x1 |
| |
| static inline void pqi_configure_legacy_intx(struct pqi_ctrl_info *ctrl_info, |
| bool enable_intx) |
| { |
| u32 intx_mask; |
| struct pqi_device_registers __iomem *pqi_registers; |
| volatile void __iomem *register_addr; |
| |
| pqi_registers = ctrl_info->pqi_registers; |
| |
| if (enable_intx) |
| register_addr = &pqi_registers->legacy_intx_mask_clear; |
| else |
| register_addr = &pqi_registers->legacy_intx_mask_set; |
| |
| intx_mask = readl(register_addr); |
| intx_mask |= PQI_LEGACY_INTX_MASK; |
| writel(intx_mask, register_addr); |
| } |
| |
| static void pqi_change_irq_mode(struct pqi_ctrl_info *ctrl_info, |
| enum pqi_irq_mode new_mode) |
| { |
| switch (ctrl_info->irq_mode) { |
| case IRQ_MODE_MSIX: |
| switch (new_mode) { |
| case IRQ_MODE_MSIX: |
| break; |
| case IRQ_MODE_INTX: |
| pqi_configure_legacy_intx(ctrl_info, true); |
| sis_enable_intx(ctrl_info); |
| break; |
| case IRQ_MODE_NONE: |
| break; |
| } |
| break; |
| case IRQ_MODE_INTX: |
| switch (new_mode) { |
| case IRQ_MODE_MSIX: |
| pqi_configure_legacy_intx(ctrl_info, false); |
| sis_enable_msix(ctrl_info); |
| break; |
| case IRQ_MODE_INTX: |
| break; |
| case IRQ_MODE_NONE: |
| pqi_configure_legacy_intx(ctrl_info, false); |
| break; |
| } |
| break; |
| case IRQ_MODE_NONE: |
| switch (new_mode) { |
| case IRQ_MODE_MSIX: |
| sis_enable_msix(ctrl_info); |
| break; |
| case IRQ_MODE_INTX: |
| pqi_configure_legacy_intx(ctrl_info, true); |
| sis_enable_intx(ctrl_info); |
| break; |
| case IRQ_MODE_NONE: |
| break; |
| } |
| break; |
| } |
| |
| ctrl_info->irq_mode = new_mode; |
| } |
| |
| #define PQI_LEGACY_INTX_PENDING 0x1 |
| |
| static inline bool pqi_is_valid_irq(struct pqi_ctrl_info *ctrl_info) |
| { |
| bool valid_irq; |
| u32 intx_status; |
| |
| switch (ctrl_info->irq_mode) { |
| case IRQ_MODE_MSIX: |
| valid_irq = true; |
| break; |
| case IRQ_MODE_INTX: |
| intx_status = |
| readl(&ctrl_info->pqi_registers->legacy_intx_status); |
| if (intx_status & PQI_LEGACY_INTX_PENDING) |
| valid_irq = true; |
| else |
| valid_irq = false; |
| break; |
| case IRQ_MODE_NONE: |
| default: |
| valid_irq = false; |
| break; |
| } |
| |
| return valid_irq; |
| } |
| |
| static irqreturn_t pqi_irq_handler(int irq, void *data) |
| { |
| struct pqi_ctrl_info *ctrl_info; |
| struct pqi_queue_group *queue_group; |
| unsigned int num_responses_handled; |
| |
| queue_group = data; |
| ctrl_info = queue_group->ctrl_info; |
| |
| if (!pqi_is_valid_irq(ctrl_info)) |
| return IRQ_NONE; |
| |
| num_responses_handled = pqi_process_io_intr(ctrl_info, queue_group); |
| |
| if (irq == ctrl_info->event_irq) |
| num_responses_handled += pqi_process_event_intr(ctrl_info); |
| |
| if (num_responses_handled) |
| atomic_inc(&ctrl_info->num_interrupts); |
| |
| pqi_start_io(ctrl_info, queue_group, RAID_PATH, NULL); |
| pqi_start_io(ctrl_info, queue_group, AIO_PATH, NULL); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int pqi_request_irqs(struct pqi_ctrl_info *ctrl_info) |
| { |
| struct pci_dev *pci_dev = ctrl_info->pci_dev; |
| int i; |
| int rc; |
| |
| ctrl_info->event_irq = pci_irq_vector(pci_dev, 0); |
| |
| for (i = 0; i < ctrl_info->num_msix_vectors_enabled; i++) { |
| rc = request_irq(pci_irq_vector(pci_dev, i), pqi_irq_handler, 0, |
| DRIVER_NAME_SHORT, &ctrl_info->queue_groups[i]); |
| if (rc) { |
| dev_err(&pci_dev->dev, |
| "irq %u init failed with error %d\n", |
| pci_irq_vector(pci_dev, i), rc); |
| return rc; |
| } |
| ctrl_info->num_msix_vectors_initialized++; |
| } |
| |
| return 0; |
| } |
| |
| static void pqi_free_irqs(struct pqi_ctrl_info *ctrl_info) |
| { |
| int i; |
| |
| for (i = 0; i < ctrl_info->num_msix_vectors_initialized; i++) |
| free_irq(pci_irq_vector(ctrl_info->pci_dev, i), |
| &ctrl_info->queue_groups[i]); |
| |
|