1 /* 2 * CXL Flash Device Driver 3 * 4 * Written by: Manoj N. Kumar <manoj@linux.vnet.ibm.com>, IBM Corporation 5 * Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation 6 * 7 * Copyright (C) 2015 IBM Corporation 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; either version 12 * 2 of the License, or (at your option) any later version. 13 */ 14 15 #include <misc/cxl.h> 16 #include <asm/unaligned.h> 17 18 #include <scsi/scsi_host.h> 19 #include <uapi/scsi/cxlflash_ioctl.h> 20 21 #include "sislite.h" 22 #include "common.h" 23 #include "vlun.h" 24 #include "superpipe.h" 25 26 /** 27 * create_local() - allocate and initialize a local LUN information structure 28 * @sdev: SCSI device associated with LUN. 29 * @wwid: World Wide Node Name for LUN. 30 * 31 * Return: Allocated local llun_info structure on success, NULL on failure 32 */ 33 static struct llun_info *create_local(struct scsi_device *sdev, u8 *wwid) 34 { 35 struct llun_info *lli = NULL; 36 37 lli = kzalloc(sizeof(*lli), GFP_KERNEL); 38 if (unlikely(!lli)) { 39 pr_err("%s: could not allocate lli\n", __func__); 40 goto out; 41 } 42 43 lli->sdev = sdev; 44 lli->newly_created = true; 45 lli->host_no = sdev->host->host_no; 46 lli->in_table = false; 47 48 memcpy(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN); 49 out: 50 return lli; 51 } 52 53 /** 54 * create_global() - allocate and initialize a global LUN information structure 55 * @sdev: SCSI device associated with LUN. 56 * @wwid: World Wide Node Name for LUN. 57 * 58 * Return: Allocated global glun_info structure on success, NULL on failure 59 */ 60 static struct glun_info *create_global(struct scsi_device *sdev, u8 *wwid) 61 { 62 struct glun_info *gli = NULL; 63 64 gli = kzalloc(sizeof(*gli), GFP_KERNEL); 65 if (unlikely(!gli)) { 66 pr_err("%s: could not allocate gli\n", __func__); 67 goto out; 68 } 69 70 mutex_init(&gli->mutex); 71 memcpy(gli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN); 72 out: 73 return gli; 74 } 75 76 /** 77 * refresh_local() - find and update local LUN information structure by WWID 78 * @cfg: Internal structure associated with the host. 79 * @wwid: WWID associated with LUN. 80 * 81 * When the LUN is found, mark it by updating it's newly_created field. 82 * 83 * Return: Found local lun_info structure on success, NULL on failure 84 * If a LUN with the WWID is found in the list, refresh it's state. 85 */ 86 static struct llun_info *refresh_local(struct cxlflash_cfg *cfg, u8 *wwid) 87 { 88 struct llun_info *lli, *temp; 89 90 list_for_each_entry_safe(lli, temp, &cfg->lluns, list) 91 if (!memcmp(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) { 92 lli->newly_created = false; 93 return lli; 94 } 95 96 return NULL; 97 } 98 99 /** 100 * lookup_global() - find a global LUN information structure by WWID 101 * @wwid: WWID associated with LUN. 102 * 103 * Return: Found global lun_info structure on success, NULL on failure 104 */ 105 static struct glun_info *lookup_global(u8 *wwid) 106 { 107 struct glun_info *gli, *temp; 108 109 list_for_each_entry_safe(gli, temp, &global.gluns, list) 110 if (!memcmp(gli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) 111 return gli; 112 113 return NULL; 114 } 115 116 /** 117 * find_and_create_lun() - find or create a local LUN information structure 118 * @sdev: SCSI device associated with LUN. 119 * @wwid: WWID associated with LUN. 120 * 121 * The LUN is kept both in a local list (per adapter) and in a global list 122 * (across all adapters). Certain attributes of the LUN are local to the 123 * adapter (such as index, port selection mask etc.). 124 * The block allocation map is shared across all adapters (i.e. associated 125 * wih the global list). Since different attributes are associated with 126 * the per adapter and global entries, allocate two separate structures for each 127 * LUN (one local, one global). 128 * 129 * Keep a pointer back from the local to the global entry. 130 * 131 * Return: Found/Allocated local lun_info structure on success, NULL on failure 132 */ 133 static struct llun_info *find_and_create_lun(struct scsi_device *sdev, u8 *wwid) 134 { 135 struct llun_info *lli = NULL; 136 struct glun_info *gli = NULL; 137 struct Scsi_Host *shost = sdev->host; 138 struct cxlflash_cfg *cfg = shost_priv(shost); 139 140 mutex_lock(&global.mutex); 141 if (unlikely(!wwid)) 142 goto out; 143 144 lli = refresh_local(cfg, wwid); 145 if (lli) 146 goto out; 147 148 lli = create_local(sdev, wwid); 149 if (unlikely(!lli)) 150 goto out; 151 152 gli = lookup_global(wwid); 153 if (gli) { 154 lli->parent = gli; 155 list_add(&lli->list, &cfg->lluns); 156 goto out; 157 } 158 159 gli = create_global(sdev, wwid); 160 if (unlikely(!gli)) { 161 kfree(lli); 162 lli = NULL; 163 goto out; 164 } 165 166 lli->parent = gli; 167 list_add(&lli->list, &cfg->lluns); 168 169 list_add(&gli->list, &global.gluns); 170 171 out: 172 mutex_unlock(&global.mutex); 173 pr_debug("%s: returning %p\n", __func__, lli); 174 return lli; 175 } 176 177 /** 178 * cxlflash_term_local_luns() - Delete all entries from local LUN list, free. 179 * @cfg: Internal structure associated with the host. 180 */ 181 void cxlflash_term_local_luns(struct cxlflash_cfg *cfg) 182 { 183 struct llun_info *lli, *temp; 184 185 mutex_lock(&global.mutex); 186 list_for_each_entry_safe(lli, temp, &cfg->lluns, list) { 187 list_del(&lli->list); 188 kfree(lli); 189 } 190 mutex_unlock(&global.mutex); 191 } 192 193 /** 194 * cxlflash_list_init() - initializes the global LUN list 195 */ 196 void cxlflash_list_init(void) 197 { 198 INIT_LIST_HEAD(&global.gluns); 199 mutex_init(&global.mutex); 200 global.err_page = NULL; 201 } 202 203 /** 204 * cxlflash_term_global_luns() - frees resources associated with global LUN list 205 */ 206 void cxlflash_term_global_luns(void) 207 { 208 struct glun_info *gli, *temp; 209 210 mutex_lock(&global.mutex); 211 list_for_each_entry_safe(gli, temp, &global.gluns, list) { 212 list_del(&gli->list); 213 cxlflash_ba_terminate(&gli->blka.ba_lun); 214 kfree(gli); 215 } 216 mutex_unlock(&global.mutex); 217 } 218 219 /** 220 * cxlflash_manage_lun() - handles LUN management activities 221 * @sdev: SCSI device associated with LUN. 222 * @manage: Manage ioctl data structure. 223 * 224 * This routine is used to notify the driver about a LUN's WWID and associate 225 * SCSI devices (sdev) with a global LUN instance. Additionally it serves to 226 * change a LUN's operating mode: legacy or superpipe. 227 * 228 * Return: 0 on success, -errno on failure 229 */ 230 int cxlflash_manage_lun(struct scsi_device *sdev, 231 struct dk_cxlflash_manage_lun *manage) 232 { 233 int rc = 0; 234 struct llun_info *lli = NULL; 235 u64 flags = manage->hdr.flags; 236 u32 chan = sdev->channel; 237 238 lli = find_and_create_lun(sdev, manage->wwid); 239 pr_debug("%s: ENTER: WWID = %016llX%016llX, flags = %016llX li = %p\n", 240 __func__, get_unaligned_le64(&manage->wwid[0]), 241 get_unaligned_le64(&manage->wwid[8]), 242 manage->hdr.flags, lli); 243 if (unlikely(!lli)) { 244 rc = -ENOMEM; 245 goto out; 246 } 247 248 if (flags & DK_CXLFLASH_MANAGE_LUN_ENABLE_SUPERPIPE) { 249 if (lli->newly_created) 250 lli->port_sel = CHAN2PORT(chan); 251 else 252 lli->port_sel = BOTH_PORTS; 253 /* Store off lun in unpacked, AFU-friendly format */ 254 lli->lun_id[chan] = lun_to_lunid(sdev->lun); 255 sdev->hostdata = lli; 256 } else if (flags & DK_CXLFLASH_MANAGE_LUN_DISABLE_SUPERPIPE) { 257 if (lli->parent->mode != MODE_NONE) 258 rc = -EBUSY; 259 else 260 sdev->hostdata = NULL; 261 } 262 263 out: 264 pr_debug("%s: returning rc=%d\n", __func__, rc); 265 return rc; 266 } 267