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->host_no = sdev->host->host_no; 45 lli->in_table = false; 46 47 memcpy(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN); 48 out: 49 return lli; 50 } 51 52 /** 53 * create_global() - allocate and initialize a global LUN information structure 54 * @sdev: SCSI device associated with LUN. 55 * @wwid: World Wide Node Name for LUN. 56 * 57 * Return: Allocated global glun_info structure on success, NULL on failure 58 */ 59 static struct glun_info *create_global(struct scsi_device *sdev, u8 *wwid) 60 { 61 struct glun_info *gli = NULL; 62 63 gli = kzalloc(sizeof(*gli), GFP_KERNEL); 64 if (unlikely(!gli)) { 65 pr_err("%s: could not allocate gli\n", __func__); 66 goto out; 67 } 68 69 mutex_init(&gli->mutex); 70 memcpy(gli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN); 71 out: 72 return gli; 73 } 74 75 /** 76 * lookup_local() - find a local LUN information structure by WWID 77 * @cfg: Internal structure associated with the host. 78 * @wwid: WWID associated with LUN. 79 * 80 * Return: Found local lun_info structure on success, NULL on failure 81 */ 82 static struct llun_info *lookup_local(struct cxlflash_cfg *cfg, u8 *wwid) 83 { 84 struct llun_info *lli, *temp; 85 86 list_for_each_entry_safe(lli, temp, &cfg->lluns, list) 87 if (!memcmp(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) 88 return lli; 89 90 return NULL; 91 } 92 93 /** 94 * lookup_global() - find a global LUN information structure by WWID 95 * @wwid: WWID associated with LUN. 96 * 97 * Return: Found global lun_info structure on success, NULL on failure 98 */ 99 static struct glun_info *lookup_global(u8 *wwid) 100 { 101 struct glun_info *gli, *temp; 102 103 list_for_each_entry_safe(gli, temp, &global.gluns, list) 104 if (!memcmp(gli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) 105 return gli; 106 107 return NULL; 108 } 109 110 /** 111 * find_and_create_lun() - find or create a local LUN information structure 112 * @sdev: SCSI device associated with LUN. 113 * @wwid: WWID associated with LUN. 114 * 115 * The LUN is kept both in a local list (per adapter) and in a global list 116 * (across all adapters). Certain attributes of the LUN are local to the 117 * adapter (such as index, port selection mask, etc.). 118 * 119 * The block allocation map is shared across all adapters (i.e. associated 120 * wih the global list). Since different attributes are associated with 121 * the per adapter and global entries, allocate two separate structures for each 122 * LUN (one local, one global). 123 * 124 * Keep a pointer back from the local to the global entry. 125 * 126 * This routine assumes the caller holds the global mutex. 127 * 128 * Return: Found/Allocated local lun_info structure on success, NULL on failure 129 */ 130 static struct llun_info *find_and_create_lun(struct scsi_device *sdev, u8 *wwid) 131 { 132 struct llun_info *lli = NULL; 133 struct glun_info *gli = NULL; 134 struct Scsi_Host *shost = sdev->host; 135 struct cxlflash_cfg *cfg = shost_priv(shost); 136 137 if (unlikely(!wwid)) 138 goto out; 139 140 lli = lookup_local(cfg, wwid); 141 if (lli) 142 goto out; 143 144 lli = create_local(sdev, wwid); 145 if (unlikely(!lli)) 146 goto out; 147 148 gli = lookup_global(wwid); 149 if (gli) { 150 lli->parent = gli; 151 list_add(&lli->list, &cfg->lluns); 152 goto out; 153 } 154 155 gli = create_global(sdev, wwid); 156 if (unlikely(!gli)) { 157 kfree(lli); 158 lli = NULL; 159 goto out; 160 } 161 162 lli->parent = gli; 163 list_add(&lli->list, &cfg->lluns); 164 165 list_add(&gli->list, &global.gluns); 166 167 out: 168 pr_debug("%s: returning %p\n", __func__, lli); 169 return lli; 170 } 171 172 /** 173 * cxlflash_term_local_luns() - Delete all entries from local LUN list, free. 174 * @cfg: Internal structure associated with the host. 175 */ 176 void cxlflash_term_local_luns(struct cxlflash_cfg *cfg) 177 { 178 struct llun_info *lli, *temp; 179 180 mutex_lock(&global.mutex); 181 list_for_each_entry_safe(lli, temp, &cfg->lluns, list) { 182 list_del(&lli->list); 183 kfree(lli); 184 } 185 mutex_unlock(&global.mutex); 186 } 187 188 /** 189 * cxlflash_list_init() - initializes the global LUN list 190 */ 191 void cxlflash_list_init(void) 192 { 193 INIT_LIST_HEAD(&global.gluns); 194 mutex_init(&global.mutex); 195 global.err_page = NULL; 196 } 197 198 /** 199 * cxlflash_term_global_luns() - frees resources associated with global LUN list 200 */ 201 void cxlflash_term_global_luns(void) 202 { 203 struct glun_info *gli, *temp; 204 205 mutex_lock(&global.mutex); 206 list_for_each_entry_safe(gli, temp, &global.gluns, list) { 207 list_del(&gli->list); 208 cxlflash_ba_terminate(&gli->blka.ba_lun); 209 kfree(gli); 210 } 211 mutex_unlock(&global.mutex); 212 } 213 214 /** 215 * cxlflash_manage_lun() - handles LUN management activities 216 * @sdev: SCSI device associated with LUN. 217 * @manage: Manage ioctl data structure. 218 * 219 * This routine is used to notify the driver about a LUN's WWID and associate 220 * SCSI devices (sdev) with a global LUN instance. Additionally it serves to 221 * change a LUN's operating mode: legacy or superpipe. 222 * 223 * Return: 0 on success, -errno on failure 224 */ 225 int cxlflash_manage_lun(struct scsi_device *sdev, 226 struct dk_cxlflash_manage_lun *manage) 227 { 228 int rc = 0; 229 struct llun_info *lli = NULL; 230 u64 flags = manage->hdr.flags; 231 u32 chan = sdev->channel; 232 233 mutex_lock(&global.mutex); 234 lli = find_and_create_lun(sdev, manage->wwid); 235 pr_debug("%s: ENTER: WWID = %016llX%016llX, flags = %016llX li = %p\n", 236 __func__, get_unaligned_be64(&manage->wwid[0]), 237 get_unaligned_be64(&manage->wwid[8]), 238 manage->hdr.flags, lli); 239 if (unlikely(!lli)) { 240 rc = -ENOMEM; 241 goto out; 242 } 243 244 if (flags & DK_CXLFLASH_MANAGE_LUN_ENABLE_SUPERPIPE) { 245 /* 246 * Update port selection mask based upon channel, store off LUN 247 * in unpacked, AFU-friendly format, and hang LUN reference in 248 * the sdev. 249 */ 250 lli->port_sel |= CHAN2PORT(chan); 251 lli->lun_id[chan] = lun_to_lunid(sdev->lun); 252 sdev->hostdata = lli; 253 } else if (flags & DK_CXLFLASH_MANAGE_LUN_DISABLE_SUPERPIPE) { 254 if (lli->parent->mode != MODE_NONE) 255 rc = -EBUSY; 256 else { 257 sdev->hostdata = NULL; 258 lli->port_sel &= ~CHAN2PORT(chan); 259 } 260 } 261 262 pr_debug("%s: port_sel = %08X chan = %u lun_id = %016llX\n", __func__, 263 lli->port_sel, chan, lli->lun_id[chan]); 264 265 out: 266 mutex_unlock(&global.mutex); 267 pr_debug("%s: returning rc=%d\n", __func__, rc); 268 return rc; 269 } 270