diff options
Diffstat (limited to 'sys/dev/mps/mps_mapping.c')
-rw-r--r-- | sys/dev/mps/mps_mapping.c | 2268 |
1 files changed, 2268 insertions, 0 deletions
diff --git a/sys/dev/mps/mps_mapping.c b/sys/dev/mps/mps_mapping.c new file mode 100644 index 0000000..e897dd3 --- /dev/null +++ b/sys/dev/mps/mps_mapping.c @@ -0,0 +1,2268 @@ +/*- + * Copyright (c) 2011 LSI Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * LSI MPT-Fusion Host Adapter FreeBSD + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* TODO Move headers to mpsvar */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/kthread.h> +#include <sys/taskqueue.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/sysctl.h> +#include <sys/eventhandler.h> +#include <sys/uio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <dev/mps/mpi/mpi2_type.h> +#include <dev/mps/mpi/mpi2.h> +#include <dev/mps/mpi/mpi2_ioc.h> +#include <dev/mps/mpi/mpi2_sas.h> +#include <dev/mps/mpi/mpi2_cnfg.h> +#include <dev/mps/mpi/mpi2_init.h> +#include <dev/mps/mpi/mpi2_tool.h> +#include <dev/mps/mps_ioctl.h> +#include <dev/mps/mpsvar.h> +#include <dev/mps/mps_mapping.h> + +/** + * _mapping_clear_entry - Clear a particular mapping entry. + * @map_entry: map table entry + * + * Returns nothing. + */ +static inline void +_mapping_clear_map_entry(struct dev_mapping_table *map_entry) +{ + map_entry->physical_id = 0; + map_entry->device_info = 0; + map_entry->phy_bits = 0; + map_entry->dpm_entry_num = MPS_DPM_BAD_IDX; + map_entry->dev_handle = 0; + map_entry->channel = -1; + map_entry->id = -1; + map_entry->missing_count = 0; + map_entry->init_complete = 0; + map_entry->TLR_bits = (u8)MPI2_SCSIIO_CONTROL_NO_TLR; +} + +/** + * _mapping_clear_enc_entry - Clear a particular enclosure table entry. + * @enc_entry: enclosure table entry + * + * Returns nothing. + */ +static inline void +_mapping_clear_enc_entry(struct enc_mapping_table *enc_entry) +{ + enc_entry->enclosure_id = 0; + enc_entry->start_index = MPS_MAPTABLE_BAD_IDX; + enc_entry->phy_bits = 0; + enc_entry->dpm_entry_num = MPS_DPM_BAD_IDX; + enc_entry->enc_handle = 0; + enc_entry->num_slots = 0; + enc_entry->start_slot = 0; + enc_entry->missing_count = 0; + enc_entry->removal_flag = 0; + enc_entry->skip_search = 0; + enc_entry->init_complete = 0; +} + +/** + * _mapping_commit_enc_entry - write a particular enc entry in DPM page0. + * @sc: per adapter object + * @enc_entry: enclosure table entry + * + * Returns 0 for success, non-zero for failure. + */ +static int +_mapping_commit_enc_entry(struct mps_softc *sc, + struct enc_mapping_table *et_entry) +{ + Mpi2DriverMap0Entry_t *dpm_entry; + struct dev_mapping_table *mt_entry; + Mpi2ConfigReply_t mpi_reply; + Mpi2DriverMappingPage0_t config_page; + + if (!sc->is_dpm_enable) + return 0; + + memset(&config_page, 0, sizeof(Mpi2DriverMappingPage0_t)); + memcpy(&config_page.Header, (u8 *) sc->dpm_pg0, + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 + + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + dpm_entry += et_entry->dpm_entry_num; + dpm_entry->PhysicalIdentifier.Low = + ( 0xFFFFFFFF & et_entry->enclosure_id); + dpm_entry->PhysicalIdentifier.High = + ( et_entry->enclosure_id >> 32); + mt_entry = &sc->mapping_table[et_entry->start_index]; + dpm_entry->DeviceIndex = htole16(mt_entry->id); + dpm_entry->MappingInformation = et_entry->num_slots; + dpm_entry->MappingInformation <<= MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT; + dpm_entry->MappingInformation |= et_entry->missing_count; + dpm_entry->MappingInformation = htole16(dpm_entry->MappingInformation); + dpm_entry->PhysicalBitsMapping = htole32(et_entry->phy_bits); + dpm_entry->Reserved1 = 0; + + memcpy(&config_page.Entry, (u8 *)dpm_entry, + sizeof(Mpi2DriverMap0Entry_t)); + if (mps_config_set_dpm_pg0(sc, &mpi_reply, &config_page, + et_entry->dpm_entry_num)) { + printf("%s: write of dpm entry %d for enclosure failed\n", + __func__, et_entry->dpm_entry_num); + dpm_entry->MappingInformation = le16toh(dpm_entry-> + MappingInformation); + dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex); + dpm_entry->PhysicalBitsMapping = + le32toh(dpm_entry->PhysicalBitsMapping); + return -1; + } + dpm_entry->MappingInformation = le16toh(dpm_entry-> + MappingInformation); + dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex); + dpm_entry->PhysicalBitsMapping = + le32toh(dpm_entry->PhysicalBitsMapping); + return 0; +} + +/** + * _mapping_commit_map_entry - write a particular map table entry in DPM page0. + * @sc: per adapter object + * @enc_entry: enclosure table entry + * + * Returns 0 for success, non-zero for failure. + */ + +static int +_mapping_commit_map_entry(struct mps_softc *sc, + struct dev_mapping_table *mt_entry) +{ + Mpi2DriverMap0Entry_t *dpm_entry; + Mpi2ConfigReply_t mpi_reply; + Mpi2DriverMappingPage0_t config_page; + + if (!sc->is_dpm_enable) + return 0; + + memset(&config_page, 0, sizeof(Mpi2DriverMappingPage0_t)); + memcpy(&config_page.Header, (u8 *)sc->dpm_pg0, + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *) sc->dpm_pg0 + + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + dpm_entry = dpm_entry + mt_entry->dpm_entry_num; + dpm_entry->PhysicalIdentifier.Low = (0xFFFFFFFF & + mt_entry->physical_id); + dpm_entry->PhysicalIdentifier.High = (mt_entry->physical_id >> 32); + dpm_entry->DeviceIndex = htole16(mt_entry->id); + dpm_entry->MappingInformation = htole16(mt_entry->missing_count); + dpm_entry->PhysicalBitsMapping = 0; + dpm_entry->Reserved1 = 0; + dpm_entry->MappingInformation = htole16(dpm_entry->MappingInformation); + memcpy(&config_page.Entry, (u8 *)dpm_entry, + sizeof(Mpi2DriverMap0Entry_t)); + if (mps_config_set_dpm_pg0(sc, &mpi_reply, &config_page, + mt_entry->dpm_entry_num)) { + printf("%s: write of dpm entry %d for device failed\n", + __func__, mt_entry->dpm_entry_num); + dpm_entry->MappingInformation = le16toh(dpm_entry-> + MappingInformation); + dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex); + return -1; + } + + dpm_entry->MappingInformation = le16toh(dpm_entry->MappingInformation); + dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex); + return 0; +} + +/** + * _mapping_get_ir_maprange - get start and end index for IR map range. + * @sc: per adapter object + * @start_idx: place holder for start index + * @end_idx: place holder for end index + * + * The IR volumes can be mapped either at start or end of the mapping table + * this function gets the detail of where IR volume mapping starts and ends + * in the device mapping table + * + * Returns nothing. + */ +static void +_mapping_get_ir_maprange(struct mps_softc *sc, u32 *start_idx, u32 *end_idx) +{ + u16 volume_mapping_flags; + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + + volume_mapping_flags = le16toh(sc->ioc_pg8.IRVolumeMappingFlags) & + MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; + if (volume_mapping_flags == MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) { + *start_idx = 0; + if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0) + *start_idx = 1; + } else + *start_idx = sc->max_devices - sc->max_volumes; + *end_idx = *start_idx + sc->max_volumes - 1; +} + +/** + * _mapping_get_enc_idx_from_id - get enclosure index from enclosure ID + * @sc: per adapter object + * @enc_id: enclosure logical identifier + * + * Returns the index of enclosure entry on success or bad index. + */ +static u8 +_mapping_get_enc_idx_from_id(struct mps_softc *sc, u64 enc_id, + u64 phy_bits) +{ + struct enc_mapping_table *et_entry; + u8 enc_idx = 0; + + for (enc_idx = 0; enc_idx < sc->num_enc_table_entries; enc_idx++) { + et_entry = &sc->enclosure_table[enc_idx]; + if ((et_entry->enclosure_id == le64toh(enc_id)) && + (!et_entry->phy_bits || (et_entry->phy_bits & + le32toh(phy_bits)))) + return enc_idx; + } + return MPS_ENCTABLE_BAD_IDX; +} + +/** + * _mapping_get_enc_idx_from_handle - get enclosure index from handle + * @sc: per adapter object + * @enc_id: enclosure handle + * + * Returns the index of enclosure entry on success or bad index. + */ +static u8 +_mapping_get_enc_idx_from_handle(struct mps_softc *sc, u16 handle) +{ + struct enc_mapping_table *et_entry; + u8 enc_idx = 0; + + for (enc_idx = 0; enc_idx < sc->num_enc_table_entries; enc_idx++) { + et_entry = &sc->enclosure_table[enc_idx]; + if (et_entry->missing_count) + continue; + if (et_entry->enc_handle == handle) + return enc_idx; + } + return MPS_ENCTABLE_BAD_IDX; +} + +/** + * _mapping_get_high_missing_et_idx - get missing enclosure index + * @sc: per adapter object + * + * Search through the enclosure table and identifies the enclosure entry + * with high missing count and returns it's index + * + * Returns the index of enclosure entry on success or bad index. + */ +static u8 +_mapping_get_high_missing_et_idx(struct mps_softc *sc) +{ + struct enc_mapping_table *et_entry; + u8 high_missing_count = 0; + u8 enc_idx, high_idx = MPS_ENCTABLE_BAD_IDX; + + for (enc_idx = 0; enc_idx < sc->num_enc_table_entries; enc_idx++) { + et_entry = &sc->enclosure_table[enc_idx]; + if ((et_entry->missing_count > high_missing_count) && + !et_entry->skip_search) { + high_missing_count = et_entry->missing_count; + high_idx = enc_idx; + } + } + return high_idx; +} + +/** + * _mapping_get_high_missing_mt_idx - get missing map table index + * @sc: per adapter object + * + * Search through the map table and identifies the device entry + * with high missing count and returns it's index + * + * Returns the index of map table entry on success or bad index. + */ +static u32 +_mapping_get_high_missing_mt_idx(struct mps_softc *sc) +{ + u32 map_idx, high_idx = MPS_ENCTABLE_BAD_IDX; + u8 high_missing_count = 0; + u32 start_idx, end_idx, start_idx_ir, end_idx_ir; + struct dev_mapping_table *mt_entry; + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + + start_idx = 0; + end_idx = sc->max_devices; + if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0) + start_idx = 1; + if (sc->ir_firmware) + _mapping_get_ir_maprange(sc, &start_idx_ir, &end_idx_ir); + if (start_idx == start_idx_ir) + start_idx = end_idx_ir + 1; + else + end_idx = start_idx_ir; + mt_entry = &sc->mapping_table[start_idx]; + for (map_idx = start_idx; map_idx < end_idx; map_idx++, mt_entry++) { + if (mt_entry->missing_count > high_missing_count) { + high_missing_count = mt_entry->missing_count; + high_idx = map_idx; + } + } + return high_idx; +} + +/** + * _mapping_get_ir_mt_idx_from_wwid - get map table index from volume WWID + * @sc: per adapter object + * @wwid: world wide unique ID of the volume + * + * Returns the index of map table entry on success or bad index. + */ +static u32 +_mapping_get_ir_mt_idx_from_wwid(struct mps_softc *sc, u64 wwid) +{ + u32 start_idx, end_idx, map_idx; + struct dev_mapping_table *mt_entry; + + _mapping_get_ir_maprange(sc, &start_idx, &end_idx); + mt_entry = &sc->mapping_table[start_idx]; + for (map_idx = start_idx; map_idx <= end_idx; map_idx++, mt_entry++) + if (mt_entry->physical_id == wwid) + return map_idx; + + return MPS_MAPTABLE_BAD_IDX; +} + +/** + * _mapping_get_mt_idx_from_id - get map table index from a device ID + * @sc: per adapter object + * @dev_id: device identifer (SAS Address) + * + * Returns the index of map table entry on success or bad index. + */ +static u32 +_mapping_get_mt_idx_from_id(struct mps_softc *sc, u64 dev_id) +{ + u32 map_idx; + struct dev_mapping_table *mt_entry; + + for (map_idx = 0; map_idx < sc->max_devices; map_idx++) { + mt_entry = &sc->mapping_table[map_idx]; + if (mt_entry->physical_id == dev_id) + return map_idx; + } + return MPS_MAPTABLE_BAD_IDX; +} + +/** + * _mapping_get_ir_mt_idx_from_handle - get map table index from volume handle + * @sc: per adapter object + * @wwid: volume device handle + * + * Returns the index of map table entry on success or bad index. + */ +static u32 +_mapping_get_ir_mt_idx_from_handle(struct mps_softc *sc, u16 volHandle) +{ + u32 start_idx, end_idx, map_idx; + struct dev_mapping_table *mt_entry; + + _mapping_get_ir_maprange(sc, &start_idx, &end_idx); + mt_entry = &sc->mapping_table[start_idx]; + for (map_idx = start_idx; map_idx <= end_idx; map_idx++, mt_entry++) + if (mt_entry->dev_handle == volHandle) + return map_idx; + + return MPS_MAPTABLE_BAD_IDX; +} + +/** + * _mapping_get_mt_idx_from_handle - get map table index from handle + * @sc: per adapter object + * @dev_id: device handle + * + * Returns the index of map table entry on success or bad index. + */ +static u32 +_mapping_get_mt_idx_from_handle(struct mps_softc *sc, u16 handle) +{ + u32 map_idx; + struct dev_mapping_table *mt_entry; + + for (map_idx = 0; map_idx < sc->max_devices; map_idx++) { + mt_entry = &sc->mapping_table[map_idx]; + if (mt_entry->dev_handle == handle) + return map_idx; + } + return MPS_MAPTABLE_BAD_IDX; +} + +/** + * _mapping_get_free_ir_mt_idx - get first free index for a volume + * @sc: per adapter object + * + * Search through mapping table for free index for a volume and if no free + * index then looks for a volume with high mapping index + * + * Returns the index of map table entry on success or bad index. + */ +static u32 +_mapping_get_free_ir_mt_idx(struct mps_softc *sc) +{ + u8 high_missing_count = 0; + u32 start_idx, end_idx, map_idx; + u32 high_idx = MPS_MAPTABLE_BAD_IDX; + struct dev_mapping_table *mt_entry; + + _mapping_get_ir_maprange(sc, &start_idx, &end_idx); + + mt_entry = &sc->mapping_table[start_idx]; + for (map_idx = start_idx; map_idx <= end_idx; map_idx++, mt_entry++) + if (!(mt_entry->device_info & MPS_MAP_IN_USE)) + return map_idx; + + mt_entry = &sc->mapping_table[start_idx]; + for (map_idx = start_idx; map_idx <= end_idx; map_idx++, mt_entry++) { + if (mt_entry->missing_count > high_missing_count) { + high_missing_count = mt_entry->missing_count; + high_idx = map_idx; + } + } + return high_idx; +} + +/** + * _mapping_get_free_mt_idx - get first free index for a device + * @sc: per adapter object + * @start_idx: offset in the table to start search + * + * Returns the index of map table entry on success or bad index. + */ +static u32 +_mapping_get_free_mt_idx(struct mps_softc *sc, u32 start_idx) +{ + u32 map_idx, max_idx = sc->max_devices; + struct dev_mapping_table *mt_entry = &sc->mapping_table[start_idx]; + u16 volume_mapping_flags; + + volume_mapping_flags = le16toh(sc->ioc_pg8.IRVolumeMappingFlags) & + MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; + if (sc->ir_firmware && (volume_mapping_flags == + MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING)) + max_idx -= sc->max_volumes; + for (map_idx = start_idx; map_idx < max_idx; map_idx++, mt_entry++) + if (!(mt_entry->device_info & (MPS_MAP_IN_USE | + MPS_DEV_RESERVED))) + return map_idx; + + return MPS_MAPTABLE_BAD_IDX; +} + +/** + * _mapping_get_dpm_idx_from_id - get DPM index from ID + * @sc: per adapter object + * @id: volume WWID or enclosure ID or device ID + * + * Returns the index of DPM entry on success or bad index. + */ +static u16 +_mapping_get_dpm_idx_from_id(struct mps_softc *sc, u64 id, u32 phy_bits) +{ + u16 entry_num; + uint64_t PhysicalIdentifier; + Mpi2DriverMap0Entry_t *dpm_entry; + + dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 + + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + PhysicalIdentifier = dpm_entry->PhysicalIdentifier.High; + PhysicalIdentifier = (PhysicalIdentifier << 32) | + dpm_entry->PhysicalIdentifier.Low; + for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++, + dpm_entry++) + if ((id == PhysicalIdentifier) && + (!phy_bits || !dpm_entry->PhysicalBitsMapping || + (phy_bits & dpm_entry->PhysicalBitsMapping))) + return entry_num; + + return MPS_DPM_BAD_IDX; +} + + +/** + * _mapping_get_free_dpm_idx - get first available DPM index + * @sc: per adapter object + * + * Returns the index of DPM entry on success or bad index. + */ +static u32 +_mapping_get_free_dpm_idx(struct mps_softc *sc) +{ + u16 entry_num; + + for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++) { + if (!sc->dpm_entry_used[entry_num]) + return entry_num; + } + return MPS_DPM_BAD_IDX; +} + +/** + * _mapping_update_ir_missing_cnt - Updates missing count for a volume + * @sc: per adapter object + * @map_idx: map table index of the volume + * @element: IR configuration change element + * @wwid: IR volume ID. + * + * Updates the missing count in the map table and in the DPM entry for a volume + * + * Returns nothing. + */ +static void +_mapping_update_ir_missing_cnt(struct mps_softc *sc, u32 map_idx, + Mpi2EventIrConfigElement_t *element, u64 wwid) +{ + struct dev_mapping_table *mt_entry; + u8 missing_cnt, reason = element->ReasonCode; + u16 dpm_idx; + Mpi2DriverMap0Entry_t *dpm_entry; + + if (!sc->is_dpm_enable) + return; + mt_entry = &sc->mapping_table[map_idx]; + if (reason == MPI2_EVENT_IR_CHANGE_RC_ADDED) { + mt_entry->missing_count = 0; + } else if (reason == MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED) { + mt_entry->missing_count = 0; + mt_entry->init_complete = 0; + } else if ((reason == MPI2_EVENT_IR_CHANGE_RC_REMOVED) || + (reason == MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED)) { + if (!mt_entry->init_complete) { + if (mt_entry->missing_count < MPS_MAX_MISSING_COUNT) + mt_entry->missing_count++; + else + mt_entry->init_complete = 1; + } + if (!mt_entry->missing_count) + mt_entry->missing_count++; + mt_entry->dev_handle = 0; + } + + dpm_idx = mt_entry->dpm_entry_num; + if (dpm_idx == MPS_DPM_BAD_IDX) { + if ((reason == MPI2_EVENT_IR_CHANGE_RC_ADDED) || + (reason == MPI2_EVENT_IR_CHANGE_RC_REMOVED)) + dpm_idx = _mapping_get_dpm_idx_from_id(sc, + mt_entry->physical_id, 0); + else if (reason == MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED) + return; + } + if (dpm_idx != MPS_DPM_BAD_IDX) { + dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 + + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + dpm_entry += dpm_idx; + missing_cnt = dpm_entry->MappingInformation & + MPI2_DRVMAP0_MAPINFO_MISSING_MASK; + if ((mt_entry->physical_id == + le64toh((u64)dpm_entry->PhysicalIdentifier.High | + dpm_entry->PhysicalIdentifier.Low)) && (missing_cnt == + mt_entry->missing_count)) + mt_entry->init_complete = 1; + } else { + dpm_idx = _mapping_get_free_dpm_idx(sc); + mt_entry->init_complete = 0; + } + + if ((dpm_idx != MPS_DPM_BAD_IDX) && !mt_entry->init_complete) { + mt_entry->init_complete = 1; + mt_entry->dpm_entry_num = dpm_idx; + dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 + + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + dpm_entry += dpm_idx; + dpm_entry->PhysicalIdentifier.Low = + (0xFFFFFFFF & mt_entry->physical_id); + dpm_entry->PhysicalIdentifier.High = + (mt_entry->physical_id >> 32); + dpm_entry->DeviceIndex = map_idx; + dpm_entry->MappingInformation = mt_entry->missing_count; + dpm_entry->PhysicalBitsMapping = 0; + dpm_entry->Reserved1 = 0; + sc->dpm_flush_entry[dpm_idx] = 1; + sc->dpm_entry_used[dpm_idx] = 1; + } else if (dpm_idx == MPS_DPM_BAD_IDX) { + printf("%s: no space to add entry in DPM table\n", __func__); + mt_entry->init_complete = 1; + } +} + +/** + * _mapping_add_to_removal_table - mark an entry for removal + * @sc: per adapter object + * @handle: Handle of enclosures/device/volume + * + * Adds the handle or DPM entry number in removal table. + * + * Returns nothing. + */ +static void +_mapping_add_to_removal_table(struct mps_softc *sc, u16 handle, + u16 dpm_idx) +{ + struct map_removal_table *remove_entry; + u32 i; + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + + remove_entry = sc->removal_table; + + for (i = 0; i < sc->max_devices; i++, remove_entry++) { + if (remove_entry->dev_handle || remove_entry->dpm_entry_num != + MPS_DPM_BAD_IDX) + continue; + if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) { + if (dpm_idx) + remove_entry->dpm_entry_num = dpm_idx; + if (remove_entry->dpm_entry_num == MPS_DPM_BAD_IDX) + remove_entry->dev_handle = handle; + } else if ((ioc_pg8_flags & + MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) + remove_entry->dev_handle = handle; + break; + } + +} + +/** + * _mapping_update_missing_count - Update missing count for a device + * @sc: per adapter object + * @topo_change: Topology change event entry + * + * Search through the topology change list and if any device is found not + * responding it's associated map table entry and DPM entry is updated + * + * Returns nothing. + */ +static void +_mapping_update_missing_count(struct mps_softc *sc, + struct _map_topology_change *topo_change) +{ + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + u8 entry; + struct _map_phy_change *phy_change; + u32 map_idx; + struct dev_mapping_table *mt_entry; + Mpi2DriverMap0Entry_t *dpm_entry; + + for (entry = 0; entry < topo_change->num_entries; entry++) { + phy_change = &topo_change->phy_details[entry]; + if (!phy_change->dev_handle || (phy_change->reason != + MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING)) + continue; + map_idx = _mapping_get_mt_idx_from_handle(sc, phy_change-> + dev_handle); + phy_change->is_processed = 1; + if (map_idx == MPS_MAPTABLE_BAD_IDX) { + printf("%s: device is already removed from mapping " + "table\n", __func__); + continue; + } + mt_entry = &sc->mapping_table[map_idx]; + if (!mt_entry->init_complete) { + if (mt_entry->missing_count < MPS_MAX_MISSING_COUNT) + mt_entry->missing_count++; + else + mt_entry->init_complete = 1; + } + if (!mt_entry->missing_count) + mt_entry->missing_count++; + _mapping_add_to_removal_table(sc, mt_entry->dev_handle, 0); + mt_entry->dev_handle = 0; + + if (((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) && + sc->is_dpm_enable && !mt_entry->init_complete && + mt_entry->dpm_entry_num != MPS_DPM_BAD_IDX) { + dpm_entry = + (Mpi2DriverMap0Entry_t *) ((u8 *)sc->dpm_pg0 + + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + dpm_entry += mt_entry->dpm_entry_num; + dpm_entry->MappingInformation = mt_entry->missing_count; + sc->dpm_flush_entry[mt_entry->dpm_entry_num] = 1; + } + mt_entry->init_complete = 1; + } +} + +/** + * _mapping_find_enc_map_space -find map table entries for enclosure + * @sc: per adapter object + * @et_entry: enclosure entry + * + * Search through the mapping table defragment it and provide contiguous + * space in map table for a particular enclosure entry + * + * Returns start index in map table or bad index. + */ +static u32 +_mapping_find_enc_map_space(struct mps_softc *sc, + struct enc_mapping_table *et_entry) +{ + u16 vol_mapping_flags; + u32 skip_count, end_of_table, map_idx, enc_idx; + u16 num_found; + u32 start_idx = MPS_MAPTABLE_BAD_IDX; + struct dev_mapping_table *mt_entry; + struct enc_mapping_table *enc_entry; + unsigned char done_flag = 0, found_space; + u16 max_num_phy_ids = le16toh(sc->ioc_pg8.MaxNumPhysicalMappedIDs); + + skip_count = sc->num_rsvd_entries; + num_found = 0; + + vol_mapping_flags = le16toh(sc->ioc_pg8.IRVolumeMappingFlags) & + MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; + + if (!sc->ir_firmware) + end_of_table = sc->max_devices; + else if (vol_mapping_flags == MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) + end_of_table = sc->max_devices; + else + end_of_table = sc->max_devices - sc->max_volumes; + + for (map_idx = (max_num_phy_ids + skip_count); + map_idx < end_of_table; map_idx++) { + mt_entry = &sc->mapping_table[map_idx]; + if ((et_entry->enclosure_id == mt_entry->physical_id) && + (!mt_entry->phy_bits || (mt_entry->phy_bits & + et_entry->phy_bits))) { + num_found += 1; + if (num_found == et_entry->num_slots) { + start_idx = (map_idx - num_found) + 1; + return start_idx; + } + } else + num_found = 0; + } + for (map_idx = (max_num_phy_ids + skip_count); + map_idx < end_of_table; map_idx++) { + mt_entry = &sc->mapping_table[map_idx]; + if (!(mt_entry->device_info & MPS_DEV_RESERVED)) { + num_found += 1; + if (num_found == et_entry->num_slots) { + start_idx = (map_idx - num_found) + 1; + return start_idx; + } + } else + num_found = 0; + } + + while (!done_flag) { + enc_idx = _mapping_get_high_missing_et_idx(sc); + if (enc_idx == MPS_ENCTABLE_BAD_IDX) + return MPS_MAPTABLE_BAD_IDX; + enc_entry = &sc->enclosure_table[enc_idx]; + /*VSP FIXME*/ + enc_entry->skip_search = 1; + mt_entry = &sc->mapping_table[enc_entry->start_index]; + for (map_idx = enc_entry->start_index; map_idx < + (enc_entry->start_index + enc_entry->num_slots); map_idx++, + mt_entry++) + mt_entry->device_info &= ~MPS_DEV_RESERVED; + found_space = 0; + for (map_idx = (max_num_phy_ids + + skip_count); map_idx < end_of_table; map_idx++) { + mt_entry = &sc->mapping_table[map_idx]; + if (!(mt_entry->device_info & MPS_DEV_RESERVED)) { + num_found += 1; + if (num_found == et_entry->num_slots) { + start_idx = (map_idx - num_found) + 1; + found_space = 1; + } + } else + num_found = 0; + } + + if (!found_space) + continue; + for (map_idx = start_idx; map_idx < (start_idx + num_found); + map_idx++) { + enc_entry = sc->enclosure_table; + for (enc_idx = 0; enc_idx < sc->num_enc_table_entries; + enc_idx++, enc_entry++) { + if (map_idx < enc_entry->start_index || + map_idx > (enc_entry->start_index + + enc_entry->num_slots)) + continue; + if (!enc_entry->removal_flag) { + enc_entry->removal_flag = 1; + _mapping_add_to_removal_table(sc, 0, + enc_entry->dpm_entry_num); + } + mt_entry = &sc->mapping_table[map_idx]; + if (mt_entry->device_info & + MPS_MAP_IN_USE) { + _mapping_add_to_removal_table(sc, + mt_entry->dev_handle, 0); + _mapping_clear_map_entry(mt_entry); + } + if (map_idx == (enc_entry->start_index + + enc_entry->num_slots - 1)) + _mapping_clear_enc_entry(et_entry); + } + } + enc_entry = sc->enclosure_table; + for (enc_idx = 0; enc_idx < sc->num_enc_table_entries; + enc_idx++, enc_entry++) { + if (!enc_entry->removal_flag) { + mt_entry = &sc->mapping_table[enc_entry-> + start_index]; + for (map_idx = enc_entry->start_index; map_idx < + (enc_entry->start_index + + enc_entry->num_slots); map_idx++, + mt_entry++) + mt_entry->device_info |= + MPS_DEV_RESERVED; + et_entry->skip_search = 0; + } + } + done_flag = 1; + } + return start_idx; +} + +/** + * _mapping_get_dev_info -get information about newly added devices + * @sc: per adapter object + * @topo_change: Topology change event entry + * + * Search through the topology change event list and issues sas device pg0 + * requests for the newly added device and reserved entries in tables + * + * Returns nothing + */ +static void +_mapping_get_dev_info(struct mps_softc *sc, + struct _map_topology_change *topo_change) +{ + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + Mpi2ConfigReply_t mpi_reply; + Mpi2SasDevicePage0_t sas_device_pg0; + u8 entry, enc_idx, phy_idx; + u32 map_idx, index, device_info; + struct _map_phy_change *phy_change, *tmp_phy_change; + uint64_t sas_address; + struct enc_mapping_table *et_entry; + struct dev_mapping_table *mt_entry; + u8 add_code = MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED; + int rc; + + for (entry = 0; entry < topo_change->num_entries; entry++) { + phy_change = &topo_change->phy_details[entry]; + if (phy_change->is_processed || !phy_change->dev_handle || + phy_change->reason != MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED) + continue; + if (mps_config_get_sas_device_pg0(sc, &mpi_reply, + &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, + phy_change->dev_handle)) { + phy_change->is_processed = 1; + continue; + } + + device_info = le32toh(sas_device_pg0.DeviceInfo); + if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) { + if ((device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE) && + (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)) { + rc = mpssas_get_sas_address_for_sata_disk(sc, + &sas_address, phy_change->dev_handle, + device_info); + if (rc) { + printf("%s: failed to compute the " + "hashed SAS Address for SATA " + "device with handle 0x%04x\n", + __func__, phy_change->dev_handle); + sas_address = + sas_device_pg0.SASAddress.High; + sas_address = (sas_address << 32) | + sas_device_pg0.SASAddress.Low; + } + mps_dprint(sc, MPS_INFO, "SAS Address for SATA " + "device = %jx\n", sas_address); + } else { + sas_address = + sas_device_pg0.SASAddress.High; + sas_address = (sas_address << 32) | + sas_device_pg0.SASAddress.Low; + } + } else { + sas_address = sas_device_pg0.SASAddress.High; + sas_address = (sas_address << 32) | + sas_device_pg0.SASAddress.Low; + } + phy_change->physical_id = sas_address; + phy_change->slot = le16toh(sas_device_pg0.Slot); + phy_change->device_info = + le32toh(sas_device_pg0.DeviceInfo); + + if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) { + enc_idx = _mapping_get_enc_idx_from_handle(sc, + topo_change->enc_handle); + if (enc_idx == MPS_ENCTABLE_BAD_IDX) { + phy_change->is_processed = 1; + printf("%s: failed to add the device with " + "handle 0x%04x because the enclosure is " + "not in the mapping table\n", __func__, + phy_change->dev_handle); + continue; + } + if (!((phy_change->device_info & + MPI2_SAS_DEVICE_INFO_END_DEVICE) && + (phy_change->device_info & + (MPI2_SAS_DEVICE_INFO_SSP_TARGET | + MPI2_SAS_DEVICE_INFO_STP_TARGET | + MPI2_SAS_DEVICE_INFO_SATA_DEVICE)))) { + phy_change->is_processed = 1; + continue; + } + et_entry = &sc->enclosure_table[enc_idx]; + if (et_entry->start_index != MPS_MAPTABLE_BAD_IDX) + continue; + if (!topo_change->exp_handle) { + map_idx = sc->num_rsvd_entries; + et_entry->start_index = map_idx; + } else { + map_idx = _mapping_find_enc_map_space(sc, + et_entry); + et_entry->start_index = map_idx; + if (et_entry->start_index == + MPS_MAPTABLE_BAD_IDX) { + phy_change->is_processed = 1; + for (phy_idx = 0; phy_idx < + topo_change->num_entries; + phy_idx++) { + tmp_phy_change = + &topo_change->phy_details + [phy_idx]; + if (tmp_phy_change->reason == + add_code) + tmp_phy_change-> + is_processed = 1; + } + break; + } + } + mt_entry = &sc->mapping_table[map_idx]; + for (index = map_idx; index < (et_entry->num_slots + + map_idx); index++, mt_entry++) { + mt_entry->device_info = MPS_DEV_RESERVED; + mt_entry->physical_id = et_entry->enclosure_id; + mt_entry->phy_bits = et_entry->phy_bits; + } + } + } +} + +/** + * _mapping_set_mid_to_eid -set map table data from enclosure table + * @sc: per adapter object + * @et_entry: enclosure entry + * + * Returns nothing + */ +static inline void +_mapping_set_mid_to_eid(struct mps_softc *sc, + struct enc_mapping_table *et_entry) +{ + struct dev_mapping_table *mt_entry; + u16 slots = et_entry->num_slots, map_idx; + u32 start_idx = et_entry->start_index; + if (start_idx != MPS_MAPTABLE_BAD_IDX) { + mt_entry = &sc->mapping_table[start_idx]; + for (map_idx = 0; map_idx < slots; map_idx++, mt_entry++) + mt_entry->physical_id = et_entry->enclosure_id; + } +} + +/** + * _mapping_clear_removed_entries - mark the entries to be cleared + * @sc: per adapter object + * + * Search through the removal table and mark the entries which needs to be + * flushed to DPM and also updates the map table and enclosure table by + * clearing the corresponding entries. + * + * Returns nothing + */ +static void +_mapping_clear_removed_entries(struct mps_softc *sc) +{ + u32 remove_idx; + struct map_removal_table *remove_entry; + Mpi2DriverMap0Entry_t *dpm_entry; + u8 done_flag = 0, num_entries, m, i; + struct enc_mapping_table *et_entry, *from, *to; + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + + if (sc->is_dpm_enable) { + remove_entry = sc->removal_table; + for (remove_idx = 0; remove_idx < sc->max_devices; + remove_idx++, remove_entry++) { + if (remove_entry->dpm_entry_num != MPS_DPM_BAD_IDX) { + dpm_entry = (Mpi2DriverMap0Entry_t *) + ((u8 *) sc->dpm_pg0 + + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + dpm_entry += remove_entry->dpm_entry_num; + dpm_entry->PhysicalIdentifier.Low = 0; + dpm_entry->PhysicalIdentifier.High = 0; + dpm_entry->DeviceIndex = 0; + dpm_entry->MappingInformation = 0; + dpm_entry->PhysicalBitsMapping = 0; + sc->dpm_flush_entry[remove_entry-> + dpm_entry_num] = 1; + sc->dpm_entry_used[remove_entry->dpm_entry_num] + = 0; + remove_entry->dpm_entry_num = MPS_DPM_BAD_IDX; + } + } + } + if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) { + num_entries = sc->num_enc_table_entries; + while (!done_flag) { + done_flag = 1; + et_entry = sc->enclosure_table; + for (i = 0; i < num_entries; i++, et_entry++) { + if (!et_entry->enc_handle && et_entry-> + init_complete) { + done_flag = 0; + if (i != (num_entries - 1)) { + from = &sc->enclosure_table + [i+1]; + to = &sc->enclosure_table[i]; + for (m = i; m < (num_entries - + 1); m++, from++, to++) { + _mapping_set_mid_to_eid + (sc, to); + *to = *from; + } + _mapping_clear_enc_entry(to); + sc->num_enc_table_entries--; + num_entries = + sc->num_enc_table_entries; + } else { + _mapping_clear_enc_entry + (et_entry); + sc->num_enc_table_entries--; + num_entries = + sc->num_enc_table_entries; + } + } + } + } + } +} + +/** + * _mapping_add_new_device -Add the new device into mapping table + * @sc: per adapter object + * @topo_change: Topology change event entry + * + * Search through the topology change event list and updates map table, + * enclosure table and DPM pages for for the newly added devices. + * + * Returns nothing + */ +static void +_mapping_add_new_device(struct mps_softc *sc, + struct _map_topology_change *topo_change) +{ + u8 enc_idx, missing_cnt, is_removed = 0; + u16 dpm_idx; + u32 search_idx, map_idx; + u32 entry; + struct dev_mapping_table *mt_entry; + struct enc_mapping_table *et_entry; + struct _map_phy_change *phy_change; + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + Mpi2DriverMap0Entry_t *dpm_entry; + uint64_t temp64_var; + u8 map_shift = MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT; + u8 hdr_sz = sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER); + u16 max_num_phy_ids = le16toh(sc->ioc_pg8.MaxNumPhysicalMappedIDs); + + for (entry = 0; entry < topo_change->num_entries; entry++) { + phy_change = &topo_change->phy_details[entry]; + if (phy_change->is_processed) + continue; + if (phy_change->reason != MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED || + !phy_change->dev_handle) { + phy_change->is_processed = 1; + continue; + } + if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) { + enc_idx = _mapping_get_enc_idx_from_handle + (sc, topo_change->enc_handle); + if (enc_idx == MPS_ENCTABLE_BAD_IDX) { + phy_change->is_processed = 1; + printf("%s: failed to add the device with " + "handle 0x%04x because the enclosure is " + "not in the mapping table\n", __func__, + phy_change->dev_handle); + continue; + } + et_entry = &sc->enclosure_table[enc_idx]; + if (et_entry->start_index == MPS_MAPTABLE_BAD_IDX) { + phy_change->is_processed = 1; + if (!sc->mt_full_retry) { + sc->mt_add_device_failed = 1; + continue; + } + printf("%s: failed to add the device with " + "handle 0x%04x because there is no free " + "space available in the mapping table\n", + __func__, phy_change->dev_handle); + continue; + } + map_idx = et_entry->start_index + phy_change->slot - + et_entry->start_slot; + mt_entry = &sc->mapping_table[map_idx]; + mt_entry->physical_id = phy_change->physical_id; + mt_entry->channel = 0; + mt_entry->id = map_idx; + mt_entry->dev_handle = phy_change->dev_handle; + mt_entry->missing_count = 0; + mt_entry->dpm_entry_num = et_entry->dpm_entry_num; + mt_entry->device_info = phy_change->device_info | + (MPS_DEV_RESERVED | MPS_MAP_IN_USE); + if (sc->is_dpm_enable) { + dpm_idx = et_entry->dpm_entry_num; + if (dpm_idx == MPS_DPM_BAD_IDX) + dpm_idx = _mapping_get_dpm_idx_from_id + (sc, et_entry->enclosure_id, + et_entry->phy_bits); + if (dpm_idx == MPS_DPM_BAD_IDX) { + dpm_idx = _mapping_get_free_dpm_idx(sc); + if (dpm_idx != MPS_DPM_BAD_IDX) { + dpm_entry = + (Mpi2DriverMap0Entry_t *) + ((u8 *) sc->dpm_pg0 + + hdr_sz); + dpm_entry += dpm_idx; + dpm_entry-> + PhysicalIdentifier.Low = + (0xFFFFFFFF & + et_entry->enclosure_id); + dpm_entry-> + PhysicalIdentifier.High = + ( et_entry->enclosure_id + >> 32); + dpm_entry->DeviceIndex = + (U16)et_entry->start_index; + dpm_entry->MappingInformation = + et_entry->num_slots; + dpm_entry->MappingInformation + <<= map_shift; + dpm_entry->PhysicalBitsMapping + = et_entry->phy_bits; + et_entry->dpm_entry_num = + dpm_idx; + /* FIXME Do I need to set the dpm_idxin mt_entry too */ + sc->dpm_entry_used[dpm_idx] = 1; + sc->dpm_flush_entry[dpm_idx] = + 1; + phy_change->is_processed = 1; + } else { + phy_change->is_processed = 1; + printf("%s: failed to add the " + "device with handle 0x%04x " + "to persistent table " + "because there is no free " + "space available\n", + __func__, + phy_change->dev_handle); + } + } else { + et_entry->dpm_entry_num = dpm_idx; + mt_entry->dpm_entry_num = dpm_idx; + } + } + /* FIXME Why not mt_entry too? */ + et_entry->init_complete = 1; + } else if ((ioc_pg8_flags & + MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) { + map_idx = _mapping_get_mt_idx_from_id + (sc, phy_change->physical_id); + if (map_idx == MPS_MAPTABLE_BAD_IDX) { + search_idx = sc->num_rsvd_entries; + if (topo_change->exp_handle) + search_idx += max_num_phy_ids; + map_idx = _mapping_get_free_mt_idx(sc, + search_idx); + } + if (map_idx == MPS_MAPTABLE_BAD_IDX) { + map_idx = _mapping_get_high_missing_mt_idx(sc); + if (map_idx != MPS_MAPTABLE_BAD_IDX) { + mt_entry = &sc->mapping_table[map_idx]; + if (mt_entry->dev_handle) { + _mapping_add_to_removal_table + (sc, mt_entry->dev_handle, + 0); + is_removed = 1; + } + mt_entry->init_complete = 0; + } + } + if (map_idx != MPS_MAPTABLE_BAD_IDX) { + mt_entry = &sc->mapping_table[map_idx]; + mt_entry->physical_id = phy_change->physical_id; + mt_entry->channel = 0; + mt_entry->id = map_idx; + mt_entry->dev_handle = phy_change->dev_handle; + mt_entry->missing_count = 0; + mt_entry->device_info = phy_change->device_info + | (MPS_DEV_RESERVED | MPS_MAP_IN_USE); + } else { + phy_change->is_processed = 1; + if (!sc->mt_full_retry) { + sc->mt_add_device_failed = 1; + continue; + } + printf("%s: failed to add the device with " + "handle 0x%04x because there is no free " + "space available in the mapping table\n", + __func__, phy_change->dev_handle); + continue; + } + if (sc->is_dpm_enable) { + if (mt_entry->dpm_entry_num != + MPS_DPM_BAD_IDX) { + dpm_idx = mt_entry->dpm_entry_num; + dpm_entry = (Mpi2DriverMap0Entry_t *) + ((u8 *)sc->dpm_pg0 + hdr_sz); + dpm_entry += dpm_idx; + missing_cnt = dpm_entry-> + MappingInformation & + MPI2_DRVMAP0_MAPINFO_MISSING_MASK; + temp64_var = dpm_entry-> + PhysicalIdentifier.High; + temp64_var = (temp64_var << 32) | + dpm_entry->PhysicalIdentifier.Low; + if ((mt_entry->physical_id == + temp64_var) && !missing_cnt) + mt_entry->init_complete = 1; + } else { + dpm_idx = _mapping_get_free_dpm_idx(sc); + mt_entry->init_complete = 0; + } + if (dpm_idx != MPS_DPM_BAD_IDX && + !mt_entry->init_complete) { + mt_entry->init_complete = 1; + mt_entry->dpm_entry_num = dpm_idx; + dpm_entry = (Mpi2DriverMap0Entry_t *) + ((u8 *)sc->dpm_pg0 + hdr_sz); + dpm_entry += dpm_idx; + dpm_entry->PhysicalIdentifier.Low = + (0xFFFFFFFF & + mt_entry->physical_id); + dpm_entry->PhysicalIdentifier.High = + (mt_entry->physical_id >> 32); + dpm_entry->DeviceIndex = (U16) map_idx; + dpm_entry->MappingInformation = 0; + dpm_entry->PhysicalBitsMapping = 0; + sc->dpm_entry_used[dpm_idx] = 1; + sc->dpm_flush_entry[dpm_idx] = 1; + phy_change->is_processed = 1; + } else if (dpm_idx == MPS_DPM_BAD_IDX) { + phy_change->is_processed = 1; + printf("%s: failed to add the " + "device with handle 0x%04x " + "to persistent table " + "because there is no free " + "space available\n", + __func__, + phy_change->dev_handle); + } + } + mt_entry->init_complete = 1; + } + + phy_change->is_processed = 1; + } + if (is_removed) + _mapping_clear_removed_entries(sc); +} + +/** + * _mapping_flush_dpm_pages -Flush the DPM pages to NVRAM + * @sc: per adapter object + * + * Returns nothing + */ +static void +_mapping_flush_dpm_pages(struct mps_softc *sc) +{ + Mpi2DriverMap0Entry_t *dpm_entry; + Mpi2ConfigReply_t mpi_reply; + Mpi2DriverMappingPage0_t config_page; + u16 entry_num; + + for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++) { + if (!sc->dpm_flush_entry[entry_num]) + continue; + memset(&config_page, 0, sizeof(Mpi2DriverMappingPage0_t)); + memcpy(&config_page.Header, (u8 *)sc->dpm_pg0, + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + dpm_entry = (Mpi2DriverMap0Entry_t *) ((u8 *)sc->dpm_pg0 + + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + dpm_entry += entry_num; + dpm_entry->MappingInformation = htole16(dpm_entry-> + MappingInformation); + dpm_entry->DeviceIndex = htole16(dpm_entry->DeviceIndex); + dpm_entry->PhysicalBitsMapping = htole32(dpm_entry-> + PhysicalBitsMapping); + memcpy(&config_page.Entry, (u8 *)dpm_entry, + sizeof(Mpi2DriverMap0Entry_t)); + /* TODO-How to handle failed writes? */ + if (mps_config_set_dpm_pg0(sc, &mpi_reply, &config_page, + entry_num)) { + printf("%s: write of dpm entry %d for device failed\n", + __func__, entry_num); + } else + sc->dpm_flush_entry[entry_num] = 0; + dpm_entry->MappingInformation = le16toh(dpm_entry-> + MappingInformation); + dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex); + dpm_entry->PhysicalBitsMapping = le32toh(dpm_entry-> + PhysicalBitsMapping); + } +} + +/** + * _mapping_allocate_memory- allocates the memory required for mapping tables + * @sc: per adapter object + * + * Allocates the memory for all the tables required for host mapping + * + * Return 0 on success or non-zero on failure. + */ +int +mps_mapping_allocate_memory(struct mps_softc *sc) +{ + uint32_t dpm_pg0_sz; + + sc->mapping_table = malloc((sizeof(struct dev_mapping_table) * + sc->max_devices), M_MPT2, M_ZERO|M_NOWAIT); + if (!sc->mapping_table) + goto free_resources; + + sc->removal_table = malloc((sizeof(struct map_removal_table) * + sc->max_devices), M_MPT2, M_ZERO|M_NOWAIT); + if (!sc->removal_table) + goto free_resources; + + sc->enclosure_table = malloc((sizeof(struct enc_mapping_table) * + sc->max_enclosures), M_MPT2, M_ZERO|M_NOWAIT); + if (!sc->enclosure_table) + goto free_resources; + + sc->dpm_entry_used = malloc((sizeof(u8) * sc->max_dpm_entries), + M_MPT2, M_ZERO|M_NOWAIT); + if (!sc->dpm_entry_used) + goto free_resources; + + sc->dpm_flush_entry = malloc((sizeof(u8) * sc->max_dpm_entries), + M_MPT2, M_ZERO|M_NOWAIT); + if (!sc->dpm_flush_entry) + goto free_resources; + + dpm_pg0_sz = sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER) + + (sc->max_dpm_entries * sizeof(MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY)); + + sc->dpm_pg0 = malloc(dpm_pg0_sz, M_MPT2, M_ZERO|M_NOWAIT); + if (!sc->dpm_pg0) { + printf("%s: memory alloc failed for dpm page; disabling dpm\n", + __func__); + sc->is_dpm_enable = 0; + } + + return 0; + +free_resources: + free(sc->mapping_table, M_MPT2); + free(sc->removal_table, M_MPT2); + free(sc->enclosure_table, M_MPT2); + free(sc->dpm_entry_used, M_MPT2); + free(sc->dpm_flush_entry, M_MPT2); + free(sc->dpm_pg0, M_MPT2); + printf("%s: device initialization failed due to failure in mapping " + "table memory allocation\n", __func__); + return -1; +} + +/** + * mps_mapping_free_memory- frees the memory allocated for mapping tables + * @sc: per adapter object + * + * Returns nothing. + */ +void +mps_mapping_free_memory(struct mps_softc *sc) +{ + free(sc->mapping_table, M_MPT2); + free(sc->removal_table, M_MPT2); + free(sc->enclosure_table, M_MPT2); + free(sc->dpm_entry_used, M_MPT2); + free(sc->dpm_flush_entry, M_MPT2); + free(sc->dpm_pg0, M_MPT2); +} + + +static void +_mapping_process_dpm_pg0(struct mps_softc *sc) +{ + u8 missing_cnt, enc_idx; + u16 slot_id, entry_num, num_slots; + u32 map_idx, dev_idx, start_idx, end_idx; + struct dev_mapping_table *mt_entry; + Mpi2DriverMap0Entry_t *dpm_entry; + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + u16 max_num_phy_ids = le16toh(sc->ioc_pg8.MaxNumPhysicalMappedIDs); + struct enc_mapping_table *et_entry; + u64 physical_id; + u32 phy_bits = 0; + + if (sc->ir_firmware) + _mapping_get_ir_maprange(sc, &start_idx, &end_idx); + + dpm_entry = (Mpi2DriverMap0Entry_t *) ((uint8_t *) sc->dpm_pg0 + + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++, + dpm_entry++) { + physical_id = dpm_entry->PhysicalIdentifier.High; + physical_id = (physical_id << 32) | + dpm_entry->PhysicalIdentifier.Low; + if (!physical_id) { + sc->dpm_entry_used[entry_num] = 0; + continue; + } + sc->dpm_entry_used[entry_num] = 1; + dpm_entry->MappingInformation = le16toh(dpm_entry-> + MappingInformation); + missing_cnt = dpm_entry->MappingInformation & + MPI2_DRVMAP0_MAPINFO_MISSING_MASK; + dev_idx = le16toh(dpm_entry->DeviceIndex); + phy_bits = le32toh(dpm_entry->PhysicalBitsMapping); + if (sc->ir_firmware && (dev_idx >= start_idx) && + (dev_idx <= end_idx)) { + mt_entry = &sc->mapping_table[dev_idx]; + mt_entry->physical_id = dpm_entry->PhysicalIdentifier.High; + mt_entry->physical_id = (mt_entry->physical_id << 32) | + dpm_entry->PhysicalIdentifier.Low; + mt_entry->channel = MPS_RAID_CHANNEL; + mt_entry->id = dev_idx; + mt_entry->missing_count = missing_cnt; + mt_entry->dpm_entry_num = entry_num; + mt_entry->device_info = MPS_DEV_RESERVED; + continue; + } + if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) { + if (dev_idx < (sc->num_rsvd_entries + + max_num_phy_ids)) { + slot_id = 0; + if (ioc_pg8_flags & + MPI2_IOCPAGE8_FLAGS_DA_START_SLOT_1) + slot_id = 1; + num_slots = max_num_phy_ids; + } else { + slot_id = 0; + num_slots = dpm_entry->MappingInformation & + MPI2_DRVMAP0_MAPINFO_SLOT_MASK; + num_slots >>= MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT; + } + enc_idx = sc->num_enc_table_entries; + if (enc_idx >= sc->max_enclosures) { + printf("%s: enclosure entries exceed max " + "enclosures of %d\n", __func__, + sc->max_enclosures); + break; + } + sc->num_enc_table_entries++; + et_entry = &sc->enclosure_table[enc_idx]; + physical_id = dpm_entry->PhysicalIdentifier.High; + et_entry->enclosure_id = (physical_id << 32) | + dpm_entry->PhysicalIdentifier.Low; + et_entry->start_index = dev_idx; + et_entry->dpm_entry_num = entry_num; + et_entry->num_slots = num_slots; + et_entry->start_slot = slot_id; + et_entry->missing_count = missing_cnt; + et_entry->phy_bits = phy_bits; + + mt_entry = &sc->mapping_table[dev_idx]; + for (map_idx = dev_idx; map_idx < (dev_idx + num_slots); + map_idx++, mt_entry++) { + if (mt_entry->dpm_entry_num != + MPS_DPM_BAD_IDX) { + printf("%s: conflict in mapping table " + "for enclosure %d\n", __func__, + enc_idx); + break; + } + physical_id = dpm_entry->PhysicalIdentifier.High; + mt_entry->physical_id = (physical_id << 32) | + dpm_entry->PhysicalIdentifier.Low; + mt_entry->phy_bits = phy_bits; + mt_entry->channel = 0; + mt_entry->id = dev_idx; + mt_entry->dpm_entry_num = entry_num; + mt_entry->missing_count = missing_cnt; + mt_entry->device_info = MPS_DEV_RESERVED; + } + } else if ((ioc_pg8_flags & + MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) { + map_idx = dev_idx; + mt_entry = &sc->mapping_table[map_idx]; + if (mt_entry->dpm_entry_num != MPS_DPM_BAD_IDX) { + printf("%s: conflict in mapping table for " + "device %d\n", __func__, map_idx); + break; + } + physical_id = dpm_entry->PhysicalIdentifier.High; + mt_entry->physical_id = (physical_id << 32) | + dpm_entry->PhysicalIdentifier.Low; + mt_entry->phy_bits = phy_bits; + mt_entry->channel = 0; + mt_entry->id = dev_idx; + mt_entry->missing_count = missing_cnt; + mt_entry->dpm_entry_num = entry_num; + mt_entry->device_info = MPS_DEV_RESERVED; + } + } /*close the loop for DPM table */ +} + +/* + * mps_mapping_check_devices - start of the day check for device availabilty + * @sc: per adapter object + * @sleep_flag: Flag indicating whether this function can sleep or not + * + * Returns nothing. + */ +void +mps_mapping_check_devices(struct mps_softc *sc, int sleep_flag) +{ + u32 i; +/* u32 cntdn, i; + u32 timeout = 60;*/ + struct dev_mapping_table *mt_entry; + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + struct enc_mapping_table *et_entry; + u32 start_idx, end_idx; + + /* We need to ucomment this when this function is called + * from the port enable complete */ +#if 0 + sc->track_mapping_events = 0; + cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + do { + if (!sc->pending_map_events) + break; + if (sleep_flag == CAN_SLEEP) + pause("mps_pause", (hz/1000));/* 1msec sleep */ + else + DELAY(500); /* 500 useconds delay */ + } while (--cntdn); + + + if (!cntdn) + printf("%s: there are %d" + " pending events after %d seconds of delay\n", + __func__, sc->pending_map_events, timeout); +#endif + sc->pending_map_events = 0; + + if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) { + et_entry = sc->enclosure_table; + for (i = 0; i < sc->num_enc_table_entries; i++, et_entry++) { + if (!et_entry->init_complete) { + if (et_entry->missing_count < + MPS_MAX_MISSING_COUNT) { + et_entry->missing_count++; + if (et_entry->dpm_entry_num != + MPS_DPM_BAD_IDX) + _mapping_commit_enc_entry(sc, + et_entry); + } + et_entry->init_complete = 1; + } + } + if (!sc->ir_firmware) + return; + _mapping_get_ir_maprange(sc, &start_idx, &end_idx); + mt_entry = &sc->mapping_table[start_idx]; + for (i = start_idx; i < (end_idx + 1); i++, mt_entry++) { + if (mt_entry->device_info & MPS_DEV_RESERVED + && !mt_entry->physical_id) + mt_entry->init_complete = 1; + else if (mt_entry->device_info & MPS_DEV_RESERVED) { + if (!mt_entry->init_complete) { + if (mt_entry->missing_count < + MPS_MAX_MISSING_COUNT) { + mt_entry->missing_count++; + if (mt_entry->dpm_entry_num != + MPS_DPM_BAD_IDX) + _mapping_commit_map_entry(sc, + mt_entry); + } + mt_entry->init_complete = 1; + } + } + } + } else if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) == + MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) { + mt_entry = sc->mapping_table; + for (i = 0; i < sc->max_devices; i++, mt_entry++) { + if (mt_entry->device_info & MPS_DEV_RESERVED + && !mt_entry->physical_id) + mt_entry->init_complete = 1; + else if (mt_entry->device_info & MPS_DEV_RESERVED) { + if (!mt_entry->init_complete) { + if (mt_entry->missing_count < + MPS_MAX_MISSING_COUNT) { + mt_entry->missing_count++; + if (mt_entry->dpm_entry_num != + MPS_DPM_BAD_IDX) + _mapping_commit_map_entry(sc, + mt_entry); + } + mt_entry->init_complete = 1; + } + } + } + } +} + + +/** + * mps_mapping_is_reinit_required - check whether event replay required + * @sc: per adapter object + * + * Checks the per ioc flags and decide whether reinit of events required + * + * Returns 1 for reinit of ioc 0 for not. + */ +int mps_mapping_is_reinit_required(struct mps_softc *sc) +{ + if (!sc->mt_full_retry && sc->mt_add_device_failed) { + sc->mt_full_retry = 1; + sc->mt_add_device_failed = 0; + _mapping_flush_dpm_pages(sc); + return 1; + } + sc->mt_full_retry = 1; + return 0; +} + +/** + * mps_mapping_initialize - initialize mapping tables + * @sc: per adapter object + * + * Read controller persitant mapping tables into internal data area. + * + * Return 0 for success or non-zero for failure. + */ +int +mps_mapping_initialize(struct mps_softc *sc) +{ + uint16_t volume_mapping_flags, dpm_pg0_sz; + uint32_t i; + Mpi2ConfigReply_t mpi_reply; + int error; + uint8_t retry_count; + uint16_t ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + + /* The additional 1 accounts for the virtual enclosure + * created for the controller + */ + sc->max_enclosures = sc->facts->MaxEnclosures + 1; + sc->max_expanders = sc->facts->MaxSasExpanders; + sc->max_volumes = sc->facts->MaxVolumes; + sc->max_devices = sc->facts->MaxTargets + sc->max_volumes; + sc->pending_map_events = 0; + sc->num_enc_table_entries = 0; + sc->num_rsvd_entries = 0; + sc->num_channels = 1; + sc->max_dpm_entries = sc->ioc_pg8.MaxPersistentEntries; + sc->is_dpm_enable = (sc->max_dpm_entries) ? 1 : 0; + sc->track_mapping_events = 0; + + if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_DISABLE_PERSISTENT_MAPPING) + sc->is_dpm_enable = 0; + + if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0) + sc->num_rsvd_entries = 1; + + volume_mapping_flags = sc->ioc_pg8.IRVolumeMappingFlags & + MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; + if (sc->ir_firmware && (volume_mapping_flags == + MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING)) + sc->num_rsvd_entries += sc->max_volumes; + + error = mps_mapping_allocate_memory(sc); + if (error) + return (error); + + for (i = 0; i < sc->max_devices; i++) + _mapping_clear_map_entry(sc->mapping_table + i); + + for (i = 0; i < sc->max_enclosures; i++) + _mapping_clear_enc_entry(sc->enclosure_table + i); + + for (i = 0; i < sc->max_devices; i++) { + sc->removal_table[i].dev_handle = 0; + sc->removal_table[i].dpm_entry_num = MPS_DPM_BAD_IDX; + } + + memset(sc->dpm_entry_used, 0, sc->max_dpm_entries); + memset(sc->dpm_flush_entry, 0, sc->max_dpm_entries); + + if (sc->is_dpm_enable) { + dpm_pg0_sz = sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER) + + (sc->max_dpm_entries * + sizeof(MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY)); + retry_count = 0; + +retry_read_dpm: + if (mps_config_get_dpm_pg0(sc, &mpi_reply, sc->dpm_pg0, + dpm_pg0_sz)) { + printf("%s: dpm page read failed; disabling dpm\n", + __func__); + if (retry_count < 3) { + retry_count++; + goto retry_read_dpm; + } + sc->is_dpm_enable = 0; + } + } + + if (sc->is_dpm_enable) + _mapping_process_dpm_pg0(sc); + + sc->track_mapping_events = 1; + return 0; +} + +/** + * mps_mapping_exit - clear mapping table and associated memory + * @sc: per adapter object + * + * Returns nothing. + */ +void +mps_mapping_exit(struct mps_softc *sc) +{ + _mapping_flush_dpm_pages(sc); + mps_mapping_free_memory(sc); +} + +/** + * mps_mapping_get_sas_id - assign a target id for sas device + * @sc: per adapter object + * @sas_address: sas address of the device + * @handle: device handle + * + * Returns valid ID on success or BAD_ID. + */ +unsigned int +mps_mapping_get_sas_id(struct mps_softc *sc, uint64_t sas_address, u16 handle) +{ + u32 map_idx; + struct dev_mapping_table *mt_entry; + + for (map_idx = 0; map_idx < sc->max_devices; map_idx++) { + mt_entry = &sc->mapping_table[map_idx]; + if (mt_entry->dev_handle == handle && mt_entry->physical_id == + sas_address) + return mt_entry->id; + } + + return MPS_MAP_BAD_ID; +} + +/** + * mps_mapping_get_sas_id_from_handle - find a target id in mapping table using + * only the dev handle. This is just a wrapper function for the local function + * _mapping_get_mt_idx_from_handle. + * @sc: per adapter object + * @handle: device handle + * + * Returns valid ID on success or BAD_ID. + */ +unsigned int +mps_mapping_get_sas_id_from_handle(struct mps_softc *sc, u16 handle) +{ + return (_mapping_get_mt_idx_from_handle(sc, handle)); +} + +/** + * mps_mapping_get_raid_id - assign a target id for raid device + * @sc: per adapter object + * @wwid: world wide identifier for raid volume + * @handle: device handle + * + * Returns valid ID on success or BAD_ID. + */ +unsigned int +mps_mapping_get_raid_id(struct mps_softc *sc, u64 wwid, u16 handle) +{ + u32 map_idx; + struct dev_mapping_table *mt_entry; + + for (map_idx = 0; map_idx < sc->max_devices; map_idx++) { + mt_entry = &sc->mapping_table[map_idx]; + if (mt_entry->dev_handle == handle && mt_entry->physical_id == + wwid) + return mt_entry->id; + } + + return MPS_MAP_BAD_ID; +} + +/** + * mps_mapping_get_raid_id_from_handle - find raid device in mapping table + * using only the volume dev handle. This is just a wrapper function for the + * local function _mapping_get_ir_mt_idx_from_handle. + * @sc: per adapter object + * @volHandle: volume device handle + * + * Returns valid ID on success or BAD_ID. + */ +unsigned int +mps_mapping_get_raid_id_from_handle(struct mps_softc *sc, u16 volHandle) +{ + return (_mapping_get_ir_mt_idx_from_handle(sc, volHandle)); +} + +/** + * mps_mapping_enclosure_dev_status_change_event - handle enclosure events + * @sc: per adapter object + * @event_data: event data payload + * + * Return nothing. + */ +void +mps_mapping_enclosure_dev_status_change_event(struct mps_softc *sc, + Mpi2EventDataSasEnclDevStatusChange_t *event_data) +{ + u8 enc_idx, missing_count; + struct enc_mapping_table *et_entry; + Mpi2DriverMap0Entry_t *dpm_entry; + u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags); + u8 map_shift = MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT; + u8 update_phy_bits = 0; + u32 saved_phy_bits; + uint64_t temp64_var; + + if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) != + MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) + goto out; + + dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 + + sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); + + if (event_data->ReasonCode == MPI2_EVENT_SAS_ENCL_RC_ADDED) { + if (!event_data->NumSlots) { + printf("%s: enclosure with handle = 0x%x reported 0 " + "slots\n", __func__, + le16toh(event_data->EnclosureHandle)); + goto out; + } + temp64_var = event_data->EnclosureLogicalID.High; + temp64_var = (temp64_var << 32) | + event_data->EnclosureLogicalID.Low; + enc_idx = _mapping_get_enc_idx_from_id(sc, temp64_var, + event_data->PhyBits); + if (enc_idx != MPS_ENCTABLE_BAD_IDX) { + et_entry = &sc->enclosure_table[enc_idx]; + if (et_entry->init_complete && + !et_entry->missing_count) { + printf("%s: enclosure %d is already present " + "with handle = 0x%x\n",__func__, enc_idx, + et_entry->enc_handle); + goto out; + } + et_entry->enc_handle = le16toh(event_data-> + EnclosureHandle); + et_entry->start_slot = le16toh(event_data->StartSlot); + saved_phy_bits = et_entry->phy_bits; + et_entry->phy_bits |= le32toh(event_data->PhyBits); + if (saved_phy_bits != et_entry->phy_bits) + update_phy_bits = 1; + if (et_entry->missing_count || update_phy_bits) { + et_entry->missing_count = 0; + if (sc->is_dpm_enable && + et_entry->dpm_entry_num != + MPS_DPM_BAD_IDX) { + dpm_entry += et_entry->dpm_entry_num; + missing_count = + (u8)(dpm_entry->MappingInformation & + MPI2_DRVMAP0_MAPINFO_MISSING_MASK); + if (!et_entry->init_complete && ( + missing_count || update_phy_bits)) { + dpm_entry->MappingInformation + = et_entry->num_slots; + dpm_entry->MappingInformation + <<= map_shift; + dpm_entry->PhysicalBitsMapping + = et_entry->phy_bits; + sc->dpm_flush_entry[et_entry-> + dpm_entry_num] = 1; + } + } + } + } else { + enc_idx = sc->num_enc_table_entries; + if (enc_idx >= sc->max_enclosures) { + printf("%s: enclosure can not be added; " + "mapping table is full\n", __func__); + goto out; + } + sc->num_enc_table_entries++; + et_entry = &sc->enclosure_table[enc_idx]; + et_entry->enc_handle = le16toh(event_data-> + EnclosureHandle); + et_entry->enclosure_id = event_data-> + EnclosureLogicalID.High; + et_entry->enclosure_id = ( et_entry->enclosure_id << + 32) | event_data->EnclosureLogicalID.Low; + et_entry->start_index = MPS_MAPTABLE_BAD_IDX; + et_entry->dpm_entry_num = MPS_DPM_BAD_IDX; + et_entry->num_slots = le16toh(event_data->NumSlots); + et_entry->start_slot = le16toh(event_data->StartSlot); + et_entry->phy_bits = le32toh(event_data->PhyBits); + } + et_entry->init_complete = 1; + } else if (event_data->ReasonCode == + MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING) { + enc_idx = _mapping_get_enc_idx_from_handle(sc, + le16toh(event_data->EnclosureHandle)); + if (enc_idx == MPS_ENCTABLE_BAD_IDX) { + printf("%s: cannot unmap enclosure %d because it has " + "already been deleted", __func__, enc_idx); + goto out; + } + et_entry = &sc->enclosure_table[enc_idx]; + if (!et_entry->init_complete) { + if (et_entry->missing_count < MPS_MAX_MISSING_COUNT) + et_entry->missing_count++; + else + et_entry->init_complete = 1; + } + if (!et_entry->missing_count) + et_entry->missing_count++; + if (sc->is_dpm_enable && !et_entry->init_complete && + et_entry->dpm_entry_num != MPS_DPM_BAD_IDX) { + dpm_entry += et_entry->dpm_entry_num; + dpm_entry->MappingInformation = et_entry->num_slots; + dpm_entry->MappingInformation <<= map_shift; + dpm_entry->MappingInformation |= + et_entry->missing_count; + sc->dpm_flush_entry[et_entry->dpm_entry_num] = 1; + } + et_entry->init_complete = 1; + } + +out: + _mapping_flush_dpm_pages(sc); + if (sc->pending_map_events) + sc->pending_map_events--; +} + +/** + * mps_mapping_topology_change_event - handle topology change events + * @sc: per adapter object + * @event_data: event data payload + * + * Returns nothing. + */ +void +mps_mapping_topology_change_event(struct mps_softc *sc, + Mpi2EventDataSasTopologyChangeList_t *event_data) +{ + struct _map_topology_change topo_change; + struct _map_phy_change *phy_change; + Mpi2EventSasTopoPhyEntry_t *event_phy_change; + u8 i, num_entries; + + topo_change.enc_handle = le16toh(event_data->EnclosureHandle); + topo_change.exp_handle = le16toh(event_data->ExpanderDevHandle); + num_entries = event_data->NumEntries; + topo_change.num_entries = num_entries; + topo_change.start_phy_num = event_data->StartPhyNum; + topo_change.num_phys = event_data->NumPhys; + topo_change.exp_status = event_data->ExpStatus; + event_phy_change = event_data->PHY; + topo_change.phy_details = NULL; + + if (!num_entries) + goto out; + phy_change = malloc(sizeof(struct _map_phy_change) * num_entries, + M_MPT2, M_NOWAIT|M_ZERO); + topo_change.phy_details = phy_change; + if (!phy_change) + goto out; + for (i = 0; i < num_entries; i++, event_phy_change++, phy_change++) { + phy_change->dev_handle = le16toh(event_phy_change-> + AttachedDevHandle); + phy_change->reason = event_phy_change->PhyStatus & + MPI2_EVENT_SAS_TOPO_RC_MASK; + } + _mapping_update_missing_count(sc, &topo_change); + _mapping_get_dev_info(sc, &topo_change); + _mapping_clear_removed_entries(sc); + _mapping_add_new_device(sc, &topo_change); + +out: + free(topo_change.phy_details, M_MPT2); + _mapping_flush_dpm_pages(sc); + if (sc->pending_map_events) + sc->pending_map_events--; +} + +/** + * _mapping_check_update_ir_mt_idx - Check and update IR map table index + * @sc: per adapter object + * @event_data: event data payload + * @evt_idx: current event index + * @map_idx: current index and the place holder for new map table index + * @wwid_table: world wide name for volumes in the element table + * + * pass through IR events and find whether any events matches and if so + * tries to find new index if not returns failure + * + * Returns 0 on success and 1 on failure + */ +static int +_mapping_check_update_ir_mt_idx(struct mps_softc *sc, + Mpi2EventDataIrConfigChangeList_t *event_data, int evt_idx, u32 *map_idx, + u64 *wwid_table) +{ + struct dev_mapping_table *mt_entry; + u32 st_idx, end_idx, mt_idx = *map_idx; + u8 match = 0; + Mpi2EventIrConfigElement_t *element; + u16 element_flags; + int i; + + mt_entry = &sc->mapping_table[mt_idx]; + _mapping_get_ir_maprange(sc, &st_idx, &end_idx); +search_again: + match = 0; + for (i = evt_idx + 1; i < event_data->NumElements; i++) { + element = (Mpi2EventIrConfigElement_t *) + &event_data->ConfigElement[i]; + element_flags = le16toh(element->ElementFlags); + if ((element_flags & + MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK) != + MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT) + continue; + if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_ADDED || + element->ReasonCode == + MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED) { + if (mt_entry->physical_id == wwid_table[i]) { + match = 1; + break; + } + } + } + + if (match) { + do { + mt_idx++; + if (mt_idx > end_idx) + return 1; + mt_entry = &sc->mapping_table[mt_idx]; + } while (mt_entry->device_info & MPS_MAP_IN_USE); + goto search_again; + } + *map_idx = mt_idx; + return 0; +} + +/** + * mps_mapping_ir_config_change_event - handle IR config change list events + * @sc: per adapter object + * @event_data: event data payload + * + * Returns nothing. + */ +void +mps_mapping_ir_config_change_event(struct mps_softc *sc, + Mpi2EventDataIrConfigChangeList_t *event_data) +{ + Mpi2EventIrConfigElement_t *element; + int i; + u64 *wwid_table; + u32 map_idx, flags; + struct dev_mapping_table *mt_entry; + u16 element_flags; + u8 log_full_error = 0; + + wwid_table = malloc(sizeof(u64) * event_data->NumElements, M_MPT2, + M_NOWAIT | M_ZERO); + if (!wwid_table) + goto out; + element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; + flags = le32toh(event_data->Flags); + for (i = 0; i < event_data->NumElements; i++, element++) { + element_flags = le16toh(element->ElementFlags); + if ((element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_ADDED) && + (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_REMOVED) && + (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE) + && (element->ReasonCode != + MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED)) + continue; + if ((element_flags & + MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK) == + MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT) { + mps_config_get_volume_wwid(sc, + le16toh(element->VolDevHandle), &wwid_table[i]); + map_idx = _mapping_get_ir_mt_idx_from_wwid(sc, + wwid_table[i]); + if (map_idx != MPS_MAPTABLE_BAD_IDX) { + mt_entry = &sc->mapping_table[map_idx]; + mt_entry->device_info |= MPS_MAP_IN_USE; + } + } + } + if (flags == MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) + goto out; + else { + element = (Mpi2EventIrConfigElement_t *)&event_data-> + ConfigElement[0]; + for (i = 0; i < event_data->NumElements; i++, element++) { + if (element->ReasonCode == + MPI2_EVENT_IR_CHANGE_RC_ADDED || + element->ReasonCode == + MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED) { + map_idx = _mapping_get_ir_mt_idx_from_wwid + (sc, wwid_table[i]); + if (map_idx != MPS_MAPTABLE_BAD_IDX) { + mt_entry = &sc->mapping_table[map_idx]; + mt_entry->channel = MPS_RAID_CHANNEL; + mt_entry->id = map_idx; + mt_entry->dev_handle = le16toh + (element->VolDevHandle); + mt_entry->device_info = + MPS_DEV_RESERVED | MPS_MAP_IN_USE; + _mapping_update_ir_missing_cnt(sc, + map_idx, element, wwid_table[i]); + continue; + } + map_idx = _mapping_get_free_ir_mt_idx(sc); + if (map_idx == MPS_MAPTABLE_BAD_IDX) + log_full_error = 1; + else if (i < (event_data->NumElements - 1)) { + log_full_error = + _mapping_check_update_ir_mt_idx + (sc, event_data, i, &map_idx, + wwid_table); + } + if (log_full_error) { + printf("%s: no space to add the RAID " + "volume with handle 0x%04x in " + "mapping table\n", __func__, le16toh + (element->VolDevHandle)); + continue; + } + mt_entry = &sc->mapping_table[map_idx]; + mt_entry->physical_id = wwid_table[i]; + mt_entry->channel = MPS_RAID_CHANNEL; + mt_entry->id = map_idx; + mt_entry->dev_handle = le16toh(element-> + VolDevHandle); + mt_entry->device_info = MPS_DEV_RESERVED | + MPS_MAP_IN_USE; + mt_entry->init_complete = 0; + _mapping_update_ir_missing_cnt(sc, map_idx, + element, wwid_table[i]); + } else if (element->ReasonCode == + MPI2_EVENT_IR_CHANGE_RC_REMOVED) { + map_idx = _mapping_get_ir_mt_idx_from_wwid(sc, + wwid_table[i]); + if (map_idx == MPS_MAPTABLE_BAD_IDX) { + printf("%s: failed to remove a volume " + "because it has already been " + "removed\n", __func__); + continue; + } + _mapping_update_ir_missing_cnt(sc, map_idx, + element, wwid_table[i]); + } else if (element->ReasonCode == + MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED) { + map_idx = _mapping_get_mt_idx_from_handle(sc, + le16toh(element->VolDevHandle)); + if (map_idx == MPS_MAPTABLE_BAD_IDX) { + printf("%s: failed to remove volume " + "with handle 0x%04x because it has " + "already been removed\n", __func__, + le16toh(element->VolDevHandle)); + continue; + } + mt_entry = &sc->mapping_table[map_idx]; + _mapping_update_ir_missing_cnt(sc, map_idx, + element, mt_entry->physical_id); + } + } + } + +out: + _mapping_flush_dpm_pages(sc); + free(wwid_table, M_MPT2); + if (sc->pending_map_events) + sc->pending_map_events--; +} |