1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * CXL Flash Device Driver |
4 | * |
5 | * Written by: Manoj N. Kumar <manoj@linux.vnet.ibm.com>, IBM Corporation |
6 | * Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation |
7 | * |
8 | * Copyright (C) 2015 IBM Corporation |
9 | */ |
10 | |
11 | #include <asm/unaligned.h> |
12 | |
13 | #include <linux/interrupt.h> |
14 | #include <linux/pci.h> |
15 | |
16 | #include <scsi/scsi_host.h> |
17 | #include <uapi/scsi/cxlflash_ioctl.h> |
18 | |
19 | #include "sislite.h" |
20 | #include "common.h" |
21 | #include "vlun.h" |
22 | #include "superpipe.h" |
23 | |
24 | /** |
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. |
28 | * |
29 | * Return: Allocated local llun_info structure on success, NULL on failure |
30 | */ |
31 | static struct llun_info *create_local(struct scsi_device *sdev, u8 *wwid) |
32 | { |
33 | struct cxlflash_cfg *cfg = shost_priv(shost: sdev->host); |
34 | struct device *dev = &cfg->dev->dev; |
35 | struct llun_info *lli = NULL; |
36 | |
37 | lli = kzalloc(size: sizeof(*lli), GFP_KERNEL); |
38 | if (unlikely(!lli)) { |
39 | dev_err(dev, "%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 cxlflash_cfg *cfg = shost_priv(shost: sdev->host); |
62 | struct device *dev = &cfg->dev->dev; |
63 | struct glun_info *gli = NULL; |
64 | |
65 | gli = kzalloc(size: sizeof(*gli), GFP_KERNEL); |
66 | if (unlikely(!gli)) { |
67 | dev_err(dev, "%s: could not allocate gli\n" , __func__); |
68 | goto out; |
69 | } |
70 | |
71 | mutex_init(&gli->mutex); |
72 | memcpy(gli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN); |
73 | out: |
74 | return gli; |
75 | } |
76 | |
77 | /** |
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. |
81 | * |
82 | * Return: Found local lun_info structure on success, NULL on failure |
83 | */ |
84 | static struct llun_info *lookup_local(struct cxlflash_cfg *cfg, u8 *wwid) |
85 | { |
86 | struct llun_info *lli, *temp; |
87 | |
88 | list_for_each_entry_safe(lli, temp, &cfg->lluns, list) |
89 | if (!memcmp(p: lli->wwid, q: wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) |
90 | return lli; |
91 | |
92 | return NULL; |
93 | } |
94 | |
95 | /** |
96 | * lookup_global() - find a global LUN information structure by WWID |
97 | * @wwid: WWID associated with LUN. |
98 | * |
99 | * Return: Found global lun_info structure on success, NULL on failure |
100 | */ |
101 | static struct glun_info *lookup_global(u8 *wwid) |
102 | { |
103 | struct glun_info *gli, *temp; |
104 | |
105 | list_for_each_entry_safe(gli, temp, &global.gluns, list) |
106 | if (!memcmp(p: gli->wwid, q: wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) |
107 | return gli; |
108 | |
109 | return NULL; |
110 | } |
111 | |
112 | /** |
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. |
116 | * |
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.). |
120 | * |
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). |
125 | * |
126 | * Keep a pointer back from the local to the global entry. |
127 | * |
128 | * This routine assumes the caller holds the global mutex. |
129 | * |
130 | * Return: Found/Allocated local lun_info structure on success, NULL on failure |
131 | */ |
132 | static struct llun_info *find_and_create_lun(struct scsi_device *sdev, u8 *wwid) |
133 | { |
134 | struct cxlflash_cfg *cfg = shost_priv(shost: sdev->host); |
135 | struct device *dev = &cfg->dev->dev; |
136 | struct llun_info *lli = NULL; |
137 | struct glun_info *gli = NULL; |
138 | |
139 | if (unlikely(!wwid)) |
140 | goto out; |
141 | |
142 | lli = lookup_local(cfg, wwid); |
143 | if (lli) |
144 | goto out; |
145 | |
146 | lli = create_local(sdev, wwid); |
147 | if (unlikely(!lli)) |
148 | goto out; |
149 | |
150 | gli = lookup_global(wwid); |
151 | if (gli) { |
152 | lli->parent = gli; |
153 | list_add(new: &lli->list, head: &cfg->lluns); |
154 | goto out; |
155 | } |
156 | |
157 | gli = create_global(sdev, wwid); |
158 | if (unlikely(!gli)) { |
159 | kfree(objp: lli); |
160 | lli = NULL; |
161 | goto out; |
162 | } |
163 | |
164 | lli->parent = gli; |
165 | list_add(new: &lli->list, head: &cfg->lluns); |
166 | |
167 | list_add(new: &gli->list, head: &global.gluns); |
168 | |
169 | out: |
170 | dev_dbg(dev, "%s: returning lli=%p, gli=%p\n" , __func__, lli, gli); |
171 | return lli; |
172 | } |
173 | |
174 | /** |
175 | * cxlflash_term_local_luns() - Delete all entries from local LUN list, free. |
176 | * @cfg: Internal structure associated with the host. |
177 | */ |
178 | void cxlflash_term_local_luns(struct cxlflash_cfg *cfg) |
179 | { |
180 | struct llun_info *lli, *temp; |
181 | |
182 | mutex_lock(&global.mutex); |
183 | list_for_each_entry_safe(lli, temp, &cfg->lluns, list) { |
184 | list_del(entry: &lli->list); |
185 | kfree(objp: lli); |
186 | } |
187 | mutex_unlock(lock: &global.mutex); |
188 | } |
189 | |
190 | /** |
191 | * cxlflash_list_init() - initializes the global LUN list |
192 | */ |
193 | void cxlflash_list_init(void) |
194 | { |
195 | INIT_LIST_HEAD(list: &global.gluns); |
196 | mutex_init(&global.mutex); |
197 | global.err_page = NULL; |
198 | } |
199 | |
200 | /** |
201 | * cxlflash_term_global_luns() - frees resources associated with global LUN list |
202 | */ |
203 | void cxlflash_term_global_luns(void) |
204 | { |
205 | struct glun_info *gli, *temp; |
206 | |
207 | mutex_lock(&global.mutex); |
208 | list_for_each_entry_safe(gli, temp, &global.gluns, list) { |
209 | list_del(entry: &gli->list); |
210 | cxlflash_ba_terminate(ba_lun: &gli->blka.ba_lun); |
211 | kfree(objp: gli); |
212 | } |
213 | mutex_unlock(lock: &global.mutex); |
214 | } |
215 | |
216 | /** |
217 | * cxlflash_manage_lun() - handles LUN management activities |
218 | * @sdev: SCSI device associated with LUN. |
219 | * @manage: Manage ioctl data structure. |
220 | * |
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. |
224 | * |
225 | * Return: 0 on success, -errno on failure |
226 | */ |
227 | int cxlflash_manage_lun(struct scsi_device *sdev, |
228 | struct dk_cxlflash_manage_lun *manage) |
229 | { |
230 | struct cxlflash_cfg *cfg = shost_priv(shost: sdev->host); |
231 | struct device *dev = &cfg->dev->dev; |
232 | struct llun_info *lli = NULL; |
233 | int rc = 0; |
234 | u64 flags = manage->hdr.flags; |
235 | u32 chan = sdev->channel; |
236 | |
237 | mutex_lock(&global.mutex); |
238 | lli = find_and_create_lun(sdev, wwid: 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)) { |
243 | rc = -ENOMEM; |
244 | goto out; |
245 | } |
246 | |
247 | if (flags & DK_CXLFLASH_MANAGE_LUN_ENABLE_SUPERPIPE) { |
248 | /* |
249 | * Update port selection mask based upon channel, store off LUN |
250 | * in unpacked, AFU-friendly format, and hang LUN reference in |
251 | * the sdev. |
252 | */ |
253 | lli->port_sel |= CHAN2PORTMASK(chan); |
254 | lli->lun_id[chan] = lun_to_lunid(lun: 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 | /* |
261 | * Clean up local LUN for this port and reset table |
262 | * tracking when no more references exist. |
263 | */ |
264 | sdev->hostdata = NULL; |
265 | lli->port_sel &= ~CHAN2PORTMASK(chan); |
266 | if (lli->port_sel == 0U) |
267 | lli->in_table = false; |
268 | } |
269 | } |
270 | |
271 | dev_dbg(dev, "%s: port_sel=%08x chan=%u lun_id=%016llx\n" , |
272 | __func__, lli->port_sel, chan, lli->lun_id[chan]); |
273 | |
274 | out: |
275 | mutex_unlock(lock: &global.mutex); |
276 | dev_dbg(dev, "%s: returning rc=%d\n" , __func__, rc); |
277 | return rc; |
278 | } |
279 | |