* The controller firmware sends separate events for NVMe devices and PCIe switches similar to existing SAS events. * NVMe device detection, addition and removal are reported by the firmware through PCIe Topology Change list events. * The PCIe device state change events are sent when the firmware detects any abnormal conditions with a NVMe device or switch. * The enumeration event are sent when the firmware starts PCIe device enumeration and stops. * This patch has the code change to handle the events and add/remove NVMe devices in driver's inventory. Signed-off-by: Chaitra P B <chaitra.basappa@xxxxxxxxxxxx> Signed-off-by: Suganath Prabu S <suganath-prabu.subramani@xxxxxxxxxxxx> Reviewed-by: Hannes Reinecke <hare@xxxxxxxx> --- drivers/scsi/mpt3sas/mpt3sas_base.c | 30 ++- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 471 ++++++++++++++++++++++++++++++++++- 2 files changed, 495 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 9653cfa..6e0a940 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -663,6 +663,26 @@ _base_display_event_data(struct MPT3SAS_ADAPTER *ioc, case MPI2_EVENT_ACTIVE_CABLE_EXCEPTION: desc = "Active cable exception"; break; + case MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE: + desc = "PCIE Device Status Change"; + break; + case MPI2_EVENT_PCIE_ENUMERATION: + { + Mpi26EventDataPCIeEnumeration_t *event_data = + (Mpi26EventDataPCIeEnumeration_t *)mpi_reply->EventData; + pr_info(MPT3SAS_FMT "PCIE Enumeration: (%s)", ioc->name, + (event_data->ReasonCode == + MPI26_EVENT_PCIE_ENUM_RC_STARTED) ? + "start" : "stop"); + if (event_data->EnumerationStatus) + pr_info("enumeration_status(0x%08x)", + le32_to_cpu(event_data->EnumerationStatus)); + pr_info("\n"); + return; + } + case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST: + desc = "PCIE Topology Change List"; + break; } if (!desc) @@ -6154,8 +6174,16 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS); _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED); _base_unmask_events(ioc, MPI2_EVENT_TEMP_THRESHOLD); - if (ioc->hba_mpi_version_belonged == MPI26_VERSION) + if (ioc->hba_mpi_version_belonged == MPI26_VERSION) { _base_unmask_events(ioc, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION); + if (ioc->is_gen35_ioc) { + _base_unmask_events(ioc, + MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE); + _base_unmask_events(ioc, MPI2_EVENT_PCIE_ENUMERATION); + _base_unmask_events(ioc, + MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST); + } + } r = _base_make_ioc_operational(ioc); if (r) diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 710ea63..344f946 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -75,6 +75,8 @@ static int _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, static int _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle); static void _scsih_pcie_device_remove_from_sml(struct MPT3SAS_ADAPTER *ioc, struct _pcie_device *pcie_device); +static void +_scsih_pcie_check_device(struct MPT3SAS_ADAPTER *ioc, u16 handle); static u8 _scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid); /* global parameters */ @@ -3458,8 +3460,6 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) struct _sas_device *sas_device; sas_device = mpt3sas_get_sdev_by_handle(ioc, handle); - if (!sas_device) - return; shost_for_each_device(sdev, ioc->shost) { sas_device_priv_data = sdev->hostdata; @@ -3469,7 +3469,7 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) continue; if (sas_device_priv_data->block) continue; - if (sas_device->pend_sas_rphy_add) + if (sas_device && sas_device->pend_sas_rphy_add) continue; if (sas_device_priv_data->ignore_delay_remove) { sdev_printk(KERN_INFO, sdev, @@ -3480,7 +3480,8 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) _scsih_internal_device_block(sdev, sas_device_priv_data); } - sas_device_put(sas_device); + if (sas_device) + sas_device_put(sas_device); } /** @@ -3564,6 +3565,33 @@ _scsih_block_io_to_children_attached_directly(struct MPT3SAS_ADAPTER *ioc, } /** + * _scsih_block_io_to_pcie_children_attached_directly + * @ioc: per adapter object + * @event_data: topology change event data + * + * This routine set sdev state to SDEV_BLOCK for all devices + * direct attached during device pull/reconnect. + */ +static void +_scsih_block_io_to_pcie_children_attached_directly(struct MPT3SAS_ADAPTER *ioc, + Mpi26EventDataPCIeTopologyChangeList_t *event_data) +{ + int i; + u16 handle; + u16 reason_code; + + for (i = 0; i < event_data->NumEntries; i++) { + handle = + le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle); + if (!handle) + continue; + reason_code = event_data->PortEntry[i].PortStatus; + if (reason_code == + MPI26_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING) + _scsih_block_io_device(ioc, handle); + } +} +/** * _scsih_tm_tr_send - send task management request * @ioc: per adapter object * @handle: device handle @@ -4182,6 +4210,81 @@ _scsih_check_topo_delete_events(struct MPT3SAS_ADAPTER *ioc, } /** + * _scsih_check_pcie_topo_remove_events - sanity check on topo + * events + * @ioc: per adapter object + * @event_data: the event data payload + * + * This handles the case where driver receives multiple switch + * or device add and delete events in a single shot. When there + * is a delete event the routine will void any pending add + * events waiting in the event queue. + * + * Return nothing. + */ +static void +_scsih_check_pcie_topo_remove_events(struct MPT3SAS_ADAPTER *ioc, + Mpi26EventDataPCIeTopologyChangeList_t *event_data) +{ + struct fw_event_work *fw_event; + Mpi26EventDataPCIeTopologyChangeList_t *local_event_data; + unsigned long flags; + int i, reason_code; + u16 handle, switch_handle; + + for (i = 0; i < event_data->NumEntries; i++) { + handle = + le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle); + if (!handle) + continue; + reason_code = event_data->PortEntry[i].PortStatus; + if (reason_code == MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING) + _scsih_tm_tr_send(ioc, handle); + } + + switch_handle = le16_to_cpu(event_data->SwitchDevHandle); + if (!switch_handle) { + _scsih_block_io_to_pcie_children_attached_directly( + ioc, event_data); + return; + } + /* TODO We are not supporting cascaded PCIe Switch removal yet*/ + if ((event_data->SwitchStatus + == MPI26_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING) || + (event_data->SwitchStatus == + MPI26_EVENT_PCIE_TOPO_SS_RESPONDING)) + _scsih_block_io_to_pcie_children_attached_directly( + ioc, event_data); + + if (event_data->SwitchStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) + return; + + /* mark ignore flag for pending events */ + spin_lock_irqsave(&ioc->fw_event_lock, flags); + list_for_each_entry(fw_event, &ioc->fw_event_list, list) { + if (fw_event->event != MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST || + fw_event->ignore) + continue; + local_event_data = + (Mpi26EventDataPCIeTopologyChangeList_t *) + fw_event->event_data; + if (local_event_data->SwitchStatus == + MPI2_EVENT_SAS_TOPO_ES_ADDED || + local_event_data->SwitchStatus == + MPI2_EVENT_SAS_TOPO_ES_RESPONDING) { + if (le16_to_cpu(local_event_data->SwitchDevHandle) == + switch_handle) { + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "setting ignoring flag for switch event\n", + ioc->name)); + fw_event->ignore = 1; + } + } + } + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); +} + +/** * _scsih_set_volume_delete_flag - setting volume delete flag * @ioc: per adapter object * @handle: device handle @@ -6481,9 +6584,9 @@ out: sas_device_put(sas_device); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - } + /** * _scsih_check_pcie_access_status - check access flags * @ioc: per adapter object @@ -6869,6 +6972,319 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) pcie_device_put(pcie_device); return 0; } + +/** + * _scsih_pcie_topology_change_event_debug - debug for topology + * event + * @ioc: per adapter object + * @event_data: event data payload + * Context: user. + */ +static void +_scsih_pcie_topology_change_event_debug(struct MPT3SAS_ADAPTER *ioc, + Mpi26EventDataPCIeTopologyChangeList_t *event_data) +{ + int i; + u16 handle; + u16 reason_code; + u8 port_number; + char *status_str = NULL; + u8 link_rate, prev_link_rate; + + switch (event_data->SwitchStatus) { + case MPI26_EVENT_PCIE_TOPO_SS_ADDED: + status_str = "add"; + break; + case MPI26_EVENT_PCIE_TOPO_SS_NOT_RESPONDING: + status_str = "remove"; + break; + case MPI26_EVENT_PCIE_TOPO_SS_RESPONDING: + case 0: + status_str = "responding"; + break; + case MPI26_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING: + status_str = "remove delay"; + break; + default: + status_str = "unknown status"; + break; + } + pr_info(MPT3SAS_FMT "pcie topology change: (%s)\n", + ioc->name, status_str); + pr_info("\tswitch_handle(0x%04x), enclosure_handle(0x%04x)" + "start_port(%02d), count(%d)\n", + le16_to_cpu(event_data->SwitchDevHandle), + le16_to_cpu(event_data->EnclosureHandle), + event_data->StartPortNum, event_data->NumEntries); + for (i = 0; i < event_data->NumEntries; i++) { + handle = + le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle); + if (!handle) + continue; + port_number = event_data->StartPortNum + i; + reason_code = event_data->PortEntry[i].PortStatus; + switch (reason_code) { + case MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED: + status_str = "target add"; + break; + case MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING: + status_str = "target remove"; + break; + case MPI26_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING: + status_str = "delay target remove"; + break; + case MPI26_EVENT_PCIE_TOPO_PS_PORT_CHANGED: + status_str = "link rate change"; + break; + case MPI26_EVENT_PCIE_TOPO_PS_NO_CHANGE: + status_str = "target responding"; + break; + default: + status_str = "unknown"; + break; + } + link_rate = event_data->PortEntry[i].CurrentPortInfo & + MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK; + prev_link_rate = event_data->PortEntry[i].PreviousPortInfo & + MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK; + pr_info("\tport(%02d), attached_handle(0x%04x): %s:" + " link rate: new(0x%02x), old(0x%02x)\n", port_number, + handle, status_str, link_rate, prev_link_rate); + } +} + +/** + * _scsih_pcie_topology_change_event - handle PCIe topology + * changes + * @ioc: per adapter object + * @fw_event: The fw_event_work object + * Context: user. + * + */ +static int +_scsih_pcie_topology_change_event(struct MPT3SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) +{ + int i; + u16 handle; + u16 reason_code; + u8 link_rate, prev_link_rate; + unsigned long flags; + int rc; + int requeue_event; + Mpi26EventDataPCIeTopologyChangeList_t *event_data = + (Mpi26EventDataPCIeTopologyChangeList_t *) fw_event->event_data; + struct _pcie_device *pcie_device; + + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + _scsih_pcie_topology_change_event_debug(ioc, event_data); + + if (ioc->shost_recovery || ioc->remove_host || + ioc->pci_error_recovery) + return 0; + + if (fw_event->ignore) { + dewtprintk(ioc, pr_info(MPT3SAS_FMT "ignoring switch event\n", + ioc->name)); + return 0; + } + + /* handle siblings events */ + for (i = 0; i < event_data->NumEntries; i++) { + if (fw_event->ignore) { + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "ignoring switch event\n", ioc->name)); + return 0; + } + if (ioc->remove_host || ioc->pci_error_recovery) + return 0; + reason_code = event_data->PortEntry[i].PortStatus; + handle = + le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle); + if (!handle) + continue; + + link_rate = event_data->PortEntry[i].CurrentPortInfo + & MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK; + prev_link_rate = event_data->PortEntry[i].PreviousPortInfo + & MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK; + + switch (reason_code) { + case MPI26_EVENT_PCIE_TOPO_PS_PORT_CHANGED: + if (ioc->shost_recovery) + break; + if (link_rate == prev_link_rate) + break; + if (link_rate < MPI26_EVENT_PCIE_TOPO_PI_RATE_2_5) + break; + + _scsih_pcie_check_device(ioc, handle); + + /* This code after this point handles the test case + * where a device has been added, however its returning + * BUSY for sometime. Then before the Device Missing + * Delay expires and the device becomes READY, the + * device is removed and added back. + */ + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + pcie_device = __mpt3sas_get_pdev_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + + if (pcie_device) { + pcie_device_put(pcie_device); + break; + } + + if (!test_bit(handle, ioc->pend_os_device_add)) + break; + + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "handle(0x%04x) device not found: convert " + "event to a device add\n", ioc->name, handle)); + event_data->PortEntry[i].PortStatus &= 0xF0; + event_data->PortEntry[i].PortStatus |= + MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED; + case MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED: + if (ioc->shost_recovery) + break; + if (link_rate < MPI26_EVENT_PCIE_TOPO_PI_RATE_2_5) + break; + + rc = _scsih_pcie_add_device(ioc, handle); + if (!rc) { + /* mark entry vacant */ + /* TODO This needs to be reviewed and fixed, + * we dont have an entry + * to make an event void like vacant + */ + event_data->PortEntry[i].PortStatus |= + MPI26_EVENT_PCIE_TOPO_PS_NO_CHANGE; + } + break; + case MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING: + _scsih_pcie_device_remove_by_handle(ioc, handle); + break; + } + } + return requeue_event; +} + +/** + * _scsih_pcie_device_status_change_event_debug - debug for + * device event + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_pcie_device_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc, + Mpi26EventDataPCIeDeviceStatusChange_t *event_data) +{ + char *reason_str = NULL; + + switch (event_data->ReasonCode) { + case MPI26_EVENT_PCIDEV_STAT_RC_SMART_DATA: + reason_str = "smart data"; + break; + case MPI26_EVENT_PCIDEV_STAT_RC_UNSUPPORTED: + reason_str = "unsupported device discovered"; + break; + case MPI26_EVENT_PCIDEV_STAT_RC_INTERNAL_DEVICE_RESET: + reason_str = "internal device reset"; + break; + case MPI26_EVENT_PCIDEV_STAT_RC_TASK_ABORT_INTERNAL: + reason_str = "internal task abort"; + break; + case MPI26_EVENT_PCIDEV_STAT_RC_ABORT_TASK_SET_INTERNAL: + reason_str = "internal task abort set"; + break; + case MPI26_EVENT_PCIDEV_STAT_RC_CLEAR_TASK_SET_INTERNAL: + reason_str = "internal clear task set"; + break; + case MPI26_EVENT_PCIDEV_STAT_RC_QUERY_TASK_INTERNAL: + reason_str = "internal query task"; + break; + case MPI26_EVENT_PCIDEV_STAT_RC_DEV_INIT_FAILURE: + reason_str = "device init failure"; + break; + case MPI26_EVENT_PCIDEV_STAT_RC_CMP_INTERNAL_DEV_RESET: + reason_str = "internal device reset complete"; + break; + case MPI26_EVENT_PCIDEV_STAT_RC_CMP_TASK_ABORT_INTERNAL: + reason_str = "internal task abort complete"; + break; + case MPI26_EVENT_PCIDEV_STAT_RC_ASYNC_NOTIFICATION: + reason_str = "internal async notification"; + break; + default: + reason_str = "unknown reason"; + break; + } + + pr_info(MPT3SAS_FMT "PCIE device status change: (%s)\n" + "\thandle(0x%04x), WWID(0x%016llx), tag(%d)", + ioc->name, reason_str, le16_to_cpu(event_data->DevHandle), + (unsigned long long)le64_to_cpu(event_data->WWID), + le16_to_cpu(event_data->TaskTag)); + if (event_data->ReasonCode == MPI26_EVENT_PCIDEV_STAT_RC_SMART_DATA) + pr_info(MPT3SAS_FMT ", ASC(0x%x), ASCQ(0x%x)\n", ioc->name, + event_data->ASC, event_data->ASCQ); + pr_info("\n"); +} + +/** + * _scsih_pcie_device_status_change_event - handle device status + * change + * @ioc: per adapter object + * @fw_event: The fw_event_work object + * Context: user. + * + * Return nothing. + */ +static void +_scsih_pcie_device_status_change_event(struct MPT3SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) +{ + struct MPT3SAS_TARGET *target_priv_data; + struct _pcie_device *pcie_device; + u64 wwid; + unsigned long flags; + Mpi26EventDataPCIeDeviceStatusChange_t *event_data = + (Mpi26EventDataPCIeDeviceStatusChange_t *)fw_event->event_data; + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + _scsih_pcie_device_status_change_event_debug(ioc, + event_data); + + if (event_data->ReasonCode != + MPI26_EVENT_PCIDEV_STAT_RC_INTERNAL_DEVICE_RESET && + event_data->ReasonCode != + MPI26_EVENT_PCIDEV_STAT_RC_CMP_INTERNAL_DEV_RESET) + return; + + spin_lock_irqsave(&ioc->pcie_device_lock, flags); + wwid = le64_to_cpu(event_data->WWID); + pcie_device = __mpt3sas_get_pdev_by_wwid(ioc, wwid); + + if (!pcie_device || !pcie_device->starget) + goto out; + + target_priv_data = pcie_device->starget->hostdata; + if (!target_priv_data) + goto out; + + if (event_data->ReasonCode == + MPI26_EVENT_PCIDEV_STAT_RC_INTERNAL_DEVICE_RESET) + target_priv_data->tm_busy = 1; + else + target_priv_data->tm_busy = 0; +out: + if (pcie_device) + pcie_device_put(pcie_device); + + spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); +} + /** * _scsih_sas_enclosure_dev_status_change_event_debug - debug for enclosure * event @@ -7120,6 +7536,34 @@ _scsih_sas_discovery_event(struct MPT3SAS_ADAPTER *ioc, } /** + * _scsih_pcie_enumeration_event - handle enumeration events + * @ioc: per adapter object + * @fw_event: The fw_event_work object + * Context: user. + * + * Return nothing. + */ +static void +_scsih_pcie_enumeration_event(struct MPT3SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) +{ + Mpi26EventDataPCIeEnumeration_t *event_data = + (Mpi26EventDataPCIeEnumeration_t *)fw_event->event_data; + + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) { + pr_info(MPT3SAS_FMT "pcie enumeration event: (%s) Flag 0x%02x", + ioc->name, + ((event_data->ReasonCode == + MPI26_EVENT_PCIE_ENUM_RC_STARTED) ? + "started" : "completed"), event_data->Flags); + if (event_data->EnumerationStatus) + pr_info("enumeration_status(0x%08x)", + le32_to_cpu(event_data->EnumerationStatus)); + pr_info("\n"); + } +} + +/** * _scsih_ir_fastpath - turn on fastpath for IR physdisk * @ioc: per adapter object * @handle: device handle for physical disk @@ -8751,6 +9195,16 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) case MPI2_EVENT_IR_OPERATION_STATUS: _scsih_sas_ir_operation_status_event(ioc, fw_event); break; + case MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE: + _scsih_pcie_device_status_change_event(ioc, fw_event); + break; + case MPI2_EVENT_PCIE_ENUMERATION: + _scsih_pcie_enumeration_event(ioc, fw_event); + break; + case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST: + _scsih_pcie_topology_change_event(ioc, fw_event); + return; + break; } out: fw_event_work_put(fw_event); @@ -8841,6 +9295,11 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, (Mpi2EventDataSasTopologyChangeList_t *) mpi_reply->EventData); break; + case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST: + _scsih_check_pcie_topo_remove_events(ioc, + (Mpi26EventDataPCIeTopologyChangeList_t *) + mpi_reply->EventData); + break; case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: _scsih_check_ir_config_unhide_events(ioc, (Mpi2EventDataIrConfigChangeList_t *) @@ -8903,6 +9362,8 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, case MPI2_EVENT_SAS_DISCOVERY: case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: case MPI2_EVENT_IR_PHYSICAL_DISK: + case MPI2_EVENT_PCIE_ENUMERATION: + case MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE: break; case MPI2_EVENT_TEMP_THRESHOLD: -- 2.5.5