Commit | Line | Data |
---|---|---|
65be2c79 MO |
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" | |
2cb79266 | 23 | #include "vlun.h" |
65be2c79 MO |
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; | |
2cb79266 | 46 | lli->in_table = false; |
65be2c79 MO |
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); | |
2cb79266 | 213 | cxlflash_ba_terminate(&gli->blka.ba_lun); |
65be2c79 MO |
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 | } |