18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CXL Flash Device Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Written by: Manoj N. Kumar <manoj@linux.vnet.ibm.com>, IBM Corporation 68c2ecf20Sopenharmony_ci * Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2015 IBM Corporation 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 178c2ecf20Sopenharmony_ci#include <uapi/scsi/cxlflash_ioctl.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "sislite.h" 208c2ecf20Sopenharmony_ci#include "common.h" 218c2ecf20Sopenharmony_ci#include "vlun.h" 228c2ecf20Sopenharmony_ci#include "superpipe.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/** 258c2ecf20Sopenharmony_ci * create_local() - allocate and initialize a local LUN information structure 268c2ecf20Sopenharmony_ci * @sdev: SCSI device associated with LUN. 278c2ecf20Sopenharmony_ci * @wwid: World Wide Node Name for LUN. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Return: Allocated local llun_info structure on success, NULL on failure 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistatic struct llun_info *create_local(struct scsi_device *sdev, u8 *wwid) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct cxlflash_cfg *cfg = shost_priv(sdev->host); 348c2ecf20Sopenharmony_ci struct device *dev = &cfg->dev->dev; 358c2ecf20Sopenharmony_ci struct llun_info *lli = NULL; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci lli = kzalloc(sizeof(*lli), GFP_KERNEL); 388c2ecf20Sopenharmony_ci if (unlikely(!lli)) { 398c2ecf20Sopenharmony_ci dev_err(dev, "%s: could not allocate lli\n", __func__); 408c2ecf20Sopenharmony_ci goto out; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci lli->sdev = sdev; 448c2ecf20Sopenharmony_ci lli->host_no = sdev->host->host_no; 458c2ecf20Sopenharmony_ci lli->in_table = false; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci memcpy(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN); 488c2ecf20Sopenharmony_ciout: 498c2ecf20Sopenharmony_ci return lli; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/** 538c2ecf20Sopenharmony_ci * create_global() - allocate and initialize a global LUN information structure 548c2ecf20Sopenharmony_ci * @sdev: SCSI device associated with LUN. 558c2ecf20Sopenharmony_ci * @wwid: World Wide Node Name for LUN. 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * Return: Allocated global glun_info structure on success, NULL on failure 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_cistatic struct glun_info *create_global(struct scsi_device *sdev, u8 *wwid) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct cxlflash_cfg *cfg = shost_priv(sdev->host); 628c2ecf20Sopenharmony_ci struct device *dev = &cfg->dev->dev; 638c2ecf20Sopenharmony_ci struct glun_info *gli = NULL; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci gli = kzalloc(sizeof(*gli), GFP_KERNEL); 668c2ecf20Sopenharmony_ci if (unlikely(!gli)) { 678c2ecf20Sopenharmony_ci dev_err(dev, "%s: could not allocate gli\n", __func__); 688c2ecf20Sopenharmony_ci goto out; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci mutex_init(&gli->mutex); 728c2ecf20Sopenharmony_ci memcpy(gli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN); 738c2ecf20Sopenharmony_ciout: 748c2ecf20Sopenharmony_ci return gli; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/** 788c2ecf20Sopenharmony_ci * lookup_local() - find a local LUN information structure by WWID 798c2ecf20Sopenharmony_ci * @cfg: Internal structure associated with the host. 808c2ecf20Sopenharmony_ci * @wwid: WWID associated with LUN. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Return: Found local lun_info structure on success, NULL on failure 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic struct llun_info *lookup_local(struct cxlflash_cfg *cfg, u8 *wwid) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct llun_info *lli, *temp; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci list_for_each_entry_safe(lli, temp, &cfg->lluns, list) 898c2ecf20Sopenharmony_ci if (!memcmp(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) 908c2ecf20Sopenharmony_ci return lli; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return NULL; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/** 968c2ecf20Sopenharmony_ci * lookup_global() - find a global LUN information structure by WWID 978c2ecf20Sopenharmony_ci * @wwid: WWID associated with LUN. 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * Return: Found global lun_info structure on success, NULL on failure 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistatic struct glun_info *lookup_global(u8 *wwid) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct glun_info *gli, *temp; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci list_for_each_entry_safe(gli, temp, &global.gluns, list) 1068c2ecf20Sopenharmony_ci if (!memcmp(gli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) 1078c2ecf20Sopenharmony_ci return gli; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return NULL; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/** 1138c2ecf20Sopenharmony_ci * find_and_create_lun() - find or create a local LUN information structure 1148c2ecf20Sopenharmony_ci * @sdev: SCSI device associated with LUN. 1158c2ecf20Sopenharmony_ci * @wwid: WWID associated with LUN. 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * The LUN is kept both in a local list (per adapter) and in a global list 1188c2ecf20Sopenharmony_ci * (across all adapters). Certain attributes of the LUN are local to the 1198c2ecf20Sopenharmony_ci * adapter (such as index, port selection mask, etc.). 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * The block allocation map is shared across all adapters (i.e. associated 1228c2ecf20Sopenharmony_ci * wih the global list). Since different attributes are associated with 1238c2ecf20Sopenharmony_ci * the per adapter and global entries, allocate two separate structures for each 1248c2ecf20Sopenharmony_ci * LUN (one local, one global). 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * Keep a pointer back from the local to the global entry. 1278c2ecf20Sopenharmony_ci * 1288c2ecf20Sopenharmony_ci * This routine assumes the caller holds the global mutex. 1298c2ecf20Sopenharmony_ci * 1308c2ecf20Sopenharmony_ci * Return: Found/Allocated local lun_info structure on success, NULL on failure 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic struct llun_info *find_and_create_lun(struct scsi_device *sdev, u8 *wwid) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct cxlflash_cfg *cfg = shost_priv(sdev->host); 1358c2ecf20Sopenharmony_ci struct device *dev = &cfg->dev->dev; 1368c2ecf20Sopenharmony_ci struct llun_info *lli = NULL; 1378c2ecf20Sopenharmony_ci struct glun_info *gli = NULL; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (unlikely(!wwid)) 1408c2ecf20Sopenharmony_ci goto out; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci lli = lookup_local(cfg, wwid); 1438c2ecf20Sopenharmony_ci if (lli) 1448c2ecf20Sopenharmony_ci goto out; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci lli = create_local(sdev, wwid); 1478c2ecf20Sopenharmony_ci if (unlikely(!lli)) 1488c2ecf20Sopenharmony_ci goto out; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci gli = lookup_global(wwid); 1518c2ecf20Sopenharmony_ci if (gli) { 1528c2ecf20Sopenharmony_ci lli->parent = gli; 1538c2ecf20Sopenharmony_ci list_add(&lli->list, &cfg->lluns); 1548c2ecf20Sopenharmony_ci goto out; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci gli = create_global(sdev, wwid); 1588c2ecf20Sopenharmony_ci if (unlikely(!gli)) { 1598c2ecf20Sopenharmony_ci kfree(lli); 1608c2ecf20Sopenharmony_ci lli = NULL; 1618c2ecf20Sopenharmony_ci goto out; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci lli->parent = gli; 1658c2ecf20Sopenharmony_ci list_add(&lli->list, &cfg->lluns); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci list_add(&gli->list, &global.gluns); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ciout: 1708c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: returning lli=%p, gli=%p\n", __func__, lli, gli); 1718c2ecf20Sopenharmony_ci return lli; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/** 1758c2ecf20Sopenharmony_ci * cxlflash_term_local_luns() - Delete all entries from local LUN list, free. 1768c2ecf20Sopenharmony_ci * @cfg: Internal structure associated with the host. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_civoid cxlflash_term_local_luns(struct cxlflash_cfg *cfg) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct llun_info *lli, *temp; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci mutex_lock(&global.mutex); 1838c2ecf20Sopenharmony_ci list_for_each_entry_safe(lli, temp, &cfg->lluns, list) { 1848c2ecf20Sopenharmony_ci list_del(&lli->list); 1858c2ecf20Sopenharmony_ci kfree(lli); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci mutex_unlock(&global.mutex); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/** 1918c2ecf20Sopenharmony_ci * cxlflash_list_init() - initializes the global LUN list 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_civoid cxlflash_list_init(void) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&global.gluns); 1968c2ecf20Sopenharmony_ci mutex_init(&global.mutex); 1978c2ecf20Sopenharmony_ci global.err_page = NULL; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/** 2018c2ecf20Sopenharmony_ci * cxlflash_term_global_luns() - frees resources associated with global LUN list 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_civoid cxlflash_term_global_luns(void) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct glun_info *gli, *temp; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci mutex_lock(&global.mutex); 2088c2ecf20Sopenharmony_ci list_for_each_entry_safe(gli, temp, &global.gluns, list) { 2098c2ecf20Sopenharmony_ci list_del(&gli->list); 2108c2ecf20Sopenharmony_ci cxlflash_ba_terminate(&gli->blka.ba_lun); 2118c2ecf20Sopenharmony_ci kfree(gli); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci mutex_unlock(&global.mutex); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/** 2178c2ecf20Sopenharmony_ci * cxlflash_manage_lun() - handles LUN management activities 2188c2ecf20Sopenharmony_ci * @sdev: SCSI device associated with LUN. 2198c2ecf20Sopenharmony_ci * @manage: Manage ioctl data structure. 2208c2ecf20Sopenharmony_ci * 2218c2ecf20Sopenharmony_ci * This routine is used to notify the driver about a LUN's WWID and associate 2228c2ecf20Sopenharmony_ci * SCSI devices (sdev) with a global LUN instance. Additionally it serves to 2238c2ecf20Sopenharmony_ci * change a LUN's operating mode: legacy or superpipe. 2248c2ecf20Sopenharmony_ci * 2258c2ecf20Sopenharmony_ci * Return: 0 on success, -errno on failure 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ciint cxlflash_manage_lun(struct scsi_device *sdev, 2288c2ecf20Sopenharmony_ci struct dk_cxlflash_manage_lun *manage) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct cxlflash_cfg *cfg = shost_priv(sdev->host); 2318c2ecf20Sopenharmony_ci struct device *dev = &cfg->dev->dev; 2328c2ecf20Sopenharmony_ci struct llun_info *lli = NULL; 2338c2ecf20Sopenharmony_ci int rc = 0; 2348c2ecf20Sopenharmony_ci u64 flags = manage->hdr.flags; 2358c2ecf20Sopenharmony_ci u32 chan = sdev->channel; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci mutex_lock(&global.mutex); 2388c2ecf20Sopenharmony_ci lli = find_and_create_lun(sdev, manage->wwid); 2398c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: WWID=%016llx%016llx, flags=%016llx lli=%p\n", 2408c2ecf20Sopenharmony_ci __func__, get_unaligned_be64(&manage->wwid[0]), 2418c2ecf20Sopenharmony_ci get_unaligned_be64(&manage->wwid[8]), manage->hdr.flags, lli); 2428c2ecf20Sopenharmony_ci if (unlikely(!lli)) { 2438c2ecf20Sopenharmony_ci rc = -ENOMEM; 2448c2ecf20Sopenharmony_ci goto out; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (flags & DK_CXLFLASH_MANAGE_LUN_ENABLE_SUPERPIPE) { 2488c2ecf20Sopenharmony_ci /* 2498c2ecf20Sopenharmony_ci * Update port selection mask based upon channel, store off LUN 2508c2ecf20Sopenharmony_ci * in unpacked, AFU-friendly format, and hang LUN reference in 2518c2ecf20Sopenharmony_ci * the sdev. 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci lli->port_sel |= CHAN2PORTMASK(chan); 2548c2ecf20Sopenharmony_ci lli->lun_id[chan] = lun_to_lunid(sdev->lun); 2558c2ecf20Sopenharmony_ci sdev->hostdata = lli; 2568c2ecf20Sopenharmony_ci } else if (flags & DK_CXLFLASH_MANAGE_LUN_DISABLE_SUPERPIPE) { 2578c2ecf20Sopenharmony_ci if (lli->parent->mode != MODE_NONE) 2588c2ecf20Sopenharmony_ci rc = -EBUSY; 2598c2ecf20Sopenharmony_ci else { 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * Clean up local LUN for this port and reset table 2628c2ecf20Sopenharmony_ci * tracking when no more references exist. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci sdev->hostdata = NULL; 2658c2ecf20Sopenharmony_ci lli->port_sel &= ~CHAN2PORTMASK(chan); 2668c2ecf20Sopenharmony_ci if (lli->port_sel == 0U) 2678c2ecf20Sopenharmony_ci lli->in_table = false; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: port_sel=%08x chan=%u lun_id=%016llx\n", 2728c2ecf20Sopenharmony_ci __func__, lli->port_sel, chan, lli->lun_id[chan]); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ciout: 2758c2ecf20Sopenharmony_ci mutex_unlock(&global.mutex); 2768c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc); 2778c2ecf20Sopenharmony_ci return rc; 2788c2ecf20Sopenharmony_ci} 279