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; | |
65be2c79 | 44 | lli->host_no = sdev->host->host_no; |
2cb79266 | 45 | lli->in_table = false; |
65be2c79 MO |
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 | /** | |
1a47401b | 76 | * lookup_local() - find a local LUN information structure by WWID |
65be2c79 MO |
77 | * @cfg: Internal structure associated with the host. |
78 | * @wwid: WWID associated with LUN. | |
79 | * | |
65be2c79 | 80 | * Return: Found local lun_info structure on success, NULL on failure |
65be2c79 | 81 | */ |
1a47401b | 82 | static struct llun_info *lookup_local(struct cxlflash_cfg *cfg, u8 *wwid) |
65be2c79 MO |
83 | { |
84 | struct llun_info *lli, *temp; | |
85 | ||
86 | list_for_each_entry_safe(lli, temp, &cfg->lluns, list) | |
1a47401b | 87 | if (!memcmp(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) |
65be2c79 | 88 | return lli; |
65be2c79 MO |
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 | |
fa4aa632 MK |
117 | * adapter (such as index, port selection mask, etc.). |
118 | * | |
65be2c79 MO |
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 | * | |
fa4aa632 MK |
126 | * This routine assumes the caller holds the global mutex. |
127 | * | |
65be2c79 MO |
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 | ||
65be2c79 MO |
137 | if (unlikely(!wwid)) |
138 | goto out; | |
139 | ||
1a47401b | 140 | lli = lookup_local(cfg, wwid); |
65be2c79 MO |
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: | |
65be2c79 MO |
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); | |
2cb79266 | 208 | cxlflash_ba_terminate(&gli->blka.ba_lun); |
65be2c79 MO |
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 | ||
fa4aa632 | 233 | mutex_lock(&global.mutex); |
65be2c79 MO |
234 | lli = find_and_create_lun(sdev, manage->wwid); |
235 | pr_debug("%s: ENTER: WWID = %016llX%016llX, flags = %016llX li = %p\n", | |
1a47401b MO |
236 | __func__, get_unaligned_be64(&manage->wwid[0]), |
237 | get_unaligned_be64(&manage->wwid[8]), | |
65be2c79 MO |
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) { | |
1a47401b MO |
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); | |
65be2c79 MO |
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; | |
1a47401b | 256 | else { |
65be2c79 | 257 | sdev->hostdata = NULL; |
1a47401b MO |
258 | lli->port_sel &= ~CHAN2PORT(chan); |
259 | } | |
65be2c79 MO |
260 | } |
261 | ||
1a47401b MO |
262 | pr_debug("%s: port_sel = %08X chan = %u lun_id = %016llX\n", __func__, |
263 | lli->port_sel, chan, lli->lun_id[chan]); | |
264 | ||
65be2c79 | 265 | out: |
fa4aa632 | 266 | mutex_unlock(&global.mutex); |
65be2c79 MO |
267 | pr_debug("%s: returning rc=%d\n", __func__, rc); |
268 | return rc; | |
269 | } |