1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * CXL Flash Device Driver
5 * Written by: Manoj N. Kumar <manoj@linux.vnet.ibm.com>, IBM Corporation
6 * Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation
8 * Copyright (C) 2015 IBM Corporation
11 #include <asm/unaligned.h>
13 #include <linux/interrupt.h>
14 #include <linux/pci.h>
16 #include <scsi/scsi_host.h>
17 #include <uapi/scsi/cxlflash_ioctl.h>
22 #include "superpipe.h"
25 * create_local() - allocate and initialize a local LUN information structure
26 * @sdev: SCSI device associated with LUN.
27 * @wwid: World Wide Node Name for LUN.
29 * Return: Allocated local llun_info structure on success, NULL on failure
31 static struct llun_info
*create_local(struct scsi_device
*sdev
, u8
*wwid
)
33 struct cxlflash_cfg
*cfg
= shost_priv(sdev
->host
);
34 struct device
*dev
= &cfg
->dev
->dev
;
35 struct llun_info
*lli
= NULL
;
37 lli
= kzalloc(sizeof(*lli
), GFP_KERNEL
);
39 dev_err(dev
, "%s: could not allocate lli\n", __func__
);
44 lli
->host_no
= sdev
->host
->host_no
;
45 lli
->in_table
= false;
47 memcpy(lli
->wwid
, wwid
, DK_CXLFLASH_MANAGE_LUN_WWID_LEN
);
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.
57 * Return: Allocated global glun_info structure on success, NULL on failure
59 static struct glun_info
*create_global(struct scsi_device
*sdev
, u8
*wwid
)
61 struct cxlflash_cfg
*cfg
= shost_priv(sdev
->host
);
62 struct device
*dev
= &cfg
->dev
->dev
;
63 struct glun_info
*gli
= NULL
;
65 gli
= kzalloc(sizeof(*gli
), GFP_KERNEL
);
67 dev_err(dev
, "%s: could not allocate gli\n", __func__
);
71 mutex_init(&gli
->mutex
);
72 memcpy(gli
->wwid
, wwid
, DK_CXLFLASH_MANAGE_LUN_WWID_LEN
);
78 * lookup_local() - find a local LUN information structure by WWID
79 * @cfg: Internal structure associated with the host.
80 * @wwid: WWID associated with LUN.
82 * Return: Found local lun_info structure on success, NULL on failure
84 static struct llun_info
*lookup_local(struct cxlflash_cfg
*cfg
, u8
*wwid
)
86 struct llun_info
*lli
, *temp
;
88 list_for_each_entry_safe(lli
, temp
, &cfg
->lluns
, list
)
89 if (!memcmp(lli
->wwid
, wwid
, DK_CXLFLASH_MANAGE_LUN_WWID_LEN
))
96 * lookup_global() - find a global LUN information structure by WWID
97 * @wwid: WWID associated with LUN.
99 * Return: Found global lun_info structure on success, NULL on failure
101 static struct glun_info
*lookup_global(u8
*wwid
)
103 struct glun_info
*gli
, *temp
;
105 list_for_each_entry_safe(gli
, temp
, &global
.gluns
, list
)
106 if (!memcmp(gli
->wwid
, wwid
, DK_CXLFLASH_MANAGE_LUN_WWID_LEN
))
113 * find_and_create_lun() - find or create a local LUN information structure
114 * @sdev: SCSI device associated with LUN.
115 * @wwid: WWID associated with LUN.
117 * The LUN is kept both in a local list (per adapter) and in a global list
118 * (across all adapters). Certain attributes of the LUN are local to the
119 * adapter (such as index, port selection mask, etc.).
121 * The block allocation map is shared across all adapters (i.e. associated
122 * wih the global list). Since different attributes are associated with
123 * the per adapter and global entries, allocate two separate structures for each
124 * LUN (one local, one global).
126 * Keep a pointer back from the local to the global entry.
128 * This routine assumes the caller holds the global mutex.
130 * Return: Found/Allocated local lun_info structure on success, NULL on failure
132 static struct llun_info
*find_and_create_lun(struct scsi_device
*sdev
, u8
*wwid
)
134 struct cxlflash_cfg
*cfg
= shost_priv(sdev
->host
);
135 struct device
*dev
= &cfg
->dev
->dev
;
136 struct llun_info
*lli
= NULL
;
137 struct glun_info
*gli
= NULL
;
142 lli
= lookup_local(cfg
, wwid
);
146 lli
= create_local(sdev
, wwid
);
150 gli
= lookup_global(wwid
);
153 list_add(&lli
->list
, &cfg
->lluns
);
157 gli
= create_global(sdev
, wwid
);
158 if (unlikely(!gli
)) {
165 list_add(&lli
->list
, &cfg
->lluns
);
167 list_add(&gli
->list
, &global
.gluns
);
170 dev_dbg(dev
, "%s: returning lli=%p, gli=%p\n", __func__
, lli
, gli
);
175 * cxlflash_term_local_luns() - Delete all entries from local LUN list, free.
176 * @cfg: Internal structure associated with the host.
178 void cxlflash_term_local_luns(struct cxlflash_cfg
*cfg
)
180 struct llun_info
*lli
, *temp
;
182 mutex_lock(&global
.mutex
);
183 list_for_each_entry_safe(lli
, temp
, &cfg
->lluns
, list
) {
184 list_del(&lli
->list
);
187 mutex_unlock(&global
.mutex
);
191 * cxlflash_list_init() - initializes the global LUN list
193 void cxlflash_list_init(void)
195 INIT_LIST_HEAD(&global
.gluns
);
196 mutex_init(&global
.mutex
);
197 global
.err_page
= NULL
;
201 * cxlflash_term_global_luns() - frees resources associated with global LUN list
203 void cxlflash_term_global_luns(void)
205 struct glun_info
*gli
, *temp
;
207 mutex_lock(&global
.mutex
);
208 list_for_each_entry_safe(gli
, temp
, &global
.gluns
, list
) {
209 list_del(&gli
->list
);
210 cxlflash_ba_terminate(&gli
->blka
.ba_lun
);
213 mutex_unlock(&global
.mutex
);
217 * cxlflash_manage_lun() - handles LUN management activities
218 * @sdev: SCSI device associated with LUN.
219 * @manage: Manage ioctl data structure.
221 * This routine is used to notify the driver about a LUN's WWID and associate
222 * SCSI devices (sdev) with a global LUN instance. Additionally it serves to
223 * change a LUN's operating mode: legacy or superpipe.
225 * Return: 0 on success, -errno on failure
227 int cxlflash_manage_lun(struct scsi_device
*sdev
,
228 struct dk_cxlflash_manage_lun
*manage
)
230 struct cxlflash_cfg
*cfg
= shost_priv(sdev
->host
);
231 struct device
*dev
= &cfg
->dev
->dev
;
232 struct llun_info
*lli
= NULL
;
234 u64 flags
= manage
->hdr
.flags
;
235 u32 chan
= sdev
->channel
;
237 mutex_lock(&global
.mutex
);
238 lli
= find_and_create_lun(sdev
, manage
->wwid
);
239 dev_dbg(dev
, "%s: WWID=%016llx%016llx, flags=%016llx lli=%p\n",
240 __func__
, get_unaligned_be64(&manage
->wwid
[0]),
241 get_unaligned_be64(&manage
->wwid
[8]), manage
->hdr
.flags
, lli
);
242 if (unlikely(!lli
)) {
247 if (flags
& DK_CXLFLASH_MANAGE_LUN_ENABLE_SUPERPIPE
) {
249 * Update port selection mask based upon channel, store off LUN
250 * in unpacked, AFU-friendly format, and hang LUN reference in
253 lli
->port_sel
|= CHAN2PORTMASK(chan
);
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
)
261 * Clean up local LUN for this port and reset table
262 * tracking when no more references exist.
264 sdev
->hostdata
= NULL
;
265 lli
->port_sel
&= ~CHAN2PORTMASK(chan
);
266 if (lli
->port_sel
== 0U)
267 lli
->in_table
= false;
271 dev_dbg(dev
, "%s: port_sel=%08x chan=%u lun_id=%016llx\n",
272 __func__
, lli
->port_sel
, chan
, lli
->lun_id
[chan
]);
275 mutex_unlock(&global
.mutex
);
276 dev_dbg(dev
, "%s: returning rc=%d\n", __func__
, rc
);