1/*******************************************************************
2 * This file is part of the Emulex Linux Device Driver for *
3 * Fibre Channel Host Bus Adapters. *
4 * Copyright (C) 2017-2023 Broadcom. All Rights Reserved. The term *
5 * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
6 * Copyright (C) 2004-2016 Emulex. All rights reserved. *
7 * EMULEX and SLI are trademarks of Emulex. *
8 * www.broadcom.com *
9 * Portions Copyright (C) 2004-2005 Christoph Hellwig *
10 * *
11 * This program is free software; you can redistribute it and/or *
12 * modify it under the terms of version 2 of the GNU General *
13 * Public License as published by the Free Software Foundation. *
14 * This program is distributed in the hope that it will be useful. *
15 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
16 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
17 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
18 * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
19 * TO BE LEGALLY INVALID. See the GNU General Public License for *
20 * more details, a copy of which can be found in the file COPYING *
21 * included with this package. *
22 *******************************************************************/
23
24#include <linux/interrupt.h>
25#include <linux/dma-direction.h>
26
27#include <scsi/scsi_transport_fc.h>
28
29#include "lpfc_hw4.h"
30#include "lpfc_hw.h"
31#include "lpfc_sli.h"
32#include "lpfc_sli4.h"
33#include "lpfc_nl.h"
34#include "lpfc_disc.h"
35#include "lpfc.h"
36#include "lpfc_crtn.h"
37
38
39/*
40 * lpfc_get_vmid_from_hashtable - search the UUID in the hash table
41 * @vport: The virtual port for which this call is being executed.
42 * @hash: calculated hash value
43 * @buf: uuid associated with the VE
44 * Return the VMID entry associated with the UUID
45 * Make sure to acquire the appropriate lock before invoking this routine.
46 */
47struct lpfc_vmid *lpfc_get_vmid_from_hashtable(struct lpfc_vport *vport,
48 u32 hash, u8 *buf)
49{
50 struct lpfc_vmid *vmp;
51
52 hash_for_each_possible(vport->hash_table, vmp, hnode, hash) {
53 if (memcmp(p: &vmp->host_vmid[0], q: buf, size: 16) == 0)
54 return vmp;
55 }
56 return NULL;
57}
58
59/*
60 * lpfc_put_vmid_in_hashtable - put the VMID in the hash table
61 * @vport: The virtual port for which this call is being executed.
62 * @hash - calculated hash value
63 * @vmp: Pointer to a VMID entry representing a VM sending I/O
64 *
65 * This routine will insert the newly acquired VMID entity in the hash table.
66 * Make sure to acquire the appropriate lock before invoking this routine.
67 */
68static void
69lpfc_put_vmid_in_hashtable(struct lpfc_vport *vport, u32 hash,
70 struct lpfc_vmid *vmp)
71{
72 hash_add(vport->hash_table, &vmp->hnode, hash);
73}
74
75/*
76 * lpfc_vmid_hash_fn - create a hash value of the UUID
77 * @vmid: uuid associated with the VE
78 * @len: length of the VMID string
79 * Returns the calculated hash value
80 */
81int lpfc_vmid_hash_fn(const char *vmid, int len)
82{
83 int c;
84 int hash = 0;
85
86 if (len == 0)
87 return 0;
88 while (len--) {
89 c = *vmid++;
90 if (c >= 'A' && c <= 'Z')
91 c += 'a' - 'A';
92
93 hash = (hash + (c << LPFC_VMID_HASH_SHIFT) +
94 (c >> LPFC_VMID_HASH_SHIFT)) * 19;
95 }
96
97 return hash & LPFC_VMID_HASH_MASK;
98}
99
100/*
101 * lpfc_vmid_update_entry - update the vmid entry in the hash table
102 * @vport: The virtual port for which this call is being executed.
103 * @iodir: io direction
104 * @vmp: Pointer to a VMID entry representing a VM sending I/O
105 * @tag: VMID tag
106 */
107static void lpfc_vmid_update_entry(struct lpfc_vport *vport,
108 enum dma_data_direction iodir,
109 struct lpfc_vmid *vmp,
110 union lpfc_vmid_io_tag *tag)
111{
112 u64 *lta;
113
114 if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO)
115 tag->cs_ctl_vmid = vmp->un.cs_ctl_vmid;
116 else if (vport->phba->cfg_vmid_app_header)
117 tag->app_id = vmp->un.app_id;
118
119 if (iodir == DMA_TO_DEVICE)
120 vmp->io_wr_cnt++;
121 else if (iodir == DMA_FROM_DEVICE)
122 vmp->io_rd_cnt++;
123
124 /* update the last access timestamp in the table */
125 lta = per_cpu_ptr(vmp->last_io_time, raw_smp_processor_id());
126 *lta = jiffies;
127}
128
129static void lpfc_vmid_assign_cs_ctl(struct lpfc_vport *vport,
130 struct lpfc_vmid *vmid)
131{
132 u32 hash;
133 struct lpfc_vmid *pvmid;
134
135 if (vport->port_type == LPFC_PHYSICAL_PORT) {
136 vmid->un.cs_ctl_vmid = lpfc_vmid_get_cs_ctl(vport);
137 } else {
138 hash = lpfc_vmid_hash_fn(vmid: vmid->host_vmid, len: vmid->vmid_len);
139 pvmid =
140 lpfc_get_vmid_from_hashtable(vport: vport->phba->pport, hash,
141 buf: vmid->host_vmid);
142 if (pvmid)
143 vmid->un.cs_ctl_vmid = pvmid->un.cs_ctl_vmid;
144 else
145 vmid->un.cs_ctl_vmid = lpfc_vmid_get_cs_ctl(vport);
146 }
147}
148
149/*
150 * lpfc_vmid_get_appid - get the VMID associated with the UUID
151 * @vport: The virtual port for which this call is being executed.
152 * @uuid: UUID associated with the VE
153 * @cmd: address of scsi_cmd descriptor
154 * @iodir: io direction
155 * @tag: VMID tag
156 * Returns status of the function
157 */
158int lpfc_vmid_get_appid(struct lpfc_vport *vport, char *uuid,
159 enum dma_data_direction iodir,
160 union lpfc_vmid_io_tag *tag)
161{
162 struct lpfc_vmid *vmp = NULL;
163 int hash, len, rc = -EPERM, i;
164
165 /* check if QFPA is complete */
166 if (lpfc_vmid_is_type_priority_tag(vport) &&
167 !(vport->vmid_flag & LPFC_VMID_QFPA_CMPL) &&
168 (vport->vmid_flag & LPFC_VMID_ISSUE_QFPA)) {
169 vport->work_port_events |= WORKER_CHECK_VMID_ISSUE_QFPA;
170 return -EAGAIN;
171 }
172
173 /* search if the UUID has already been mapped to the VMID */
174 len = strlen(uuid);
175 hash = lpfc_vmid_hash_fn(vmid: uuid, len);
176
177 /* search for the VMID in the table */
178 read_lock(&vport->vmid_lock);
179 vmp = lpfc_get_vmid_from_hashtable(vport, hash, buf: uuid);
180
181 /* if found, check if its already registered */
182 if (vmp && vmp->flag & LPFC_VMID_REGISTERED) {
183 read_unlock(&vport->vmid_lock);
184 lpfc_vmid_update_entry(vport, iodir, vmp, tag);
185 rc = 0;
186 } else if (vmp && (vmp->flag & LPFC_VMID_REQ_REGISTER ||
187 vmp->flag & LPFC_VMID_DE_REGISTER)) {
188 /* else if register or dereg request has already been sent */
189 /* Hence VMID tag will not be added for this I/O */
190 read_unlock(&vport->vmid_lock);
191 rc = -EBUSY;
192 } else {
193 /* The VMID was not found in the hashtable. At this point, */
194 /* drop the read lock first before proceeding further */
195 read_unlock(&vport->vmid_lock);
196 /* start the process to obtain one as per the */
197 /* type of the VMID indicated */
198 write_lock(&vport->vmid_lock);
199 vmp = lpfc_get_vmid_from_hashtable(vport, hash, buf: uuid);
200
201 /* while the read lock was released, in case the entry was */
202 /* added by other context or is in process of being added */
203 if (vmp && vmp->flag & LPFC_VMID_REGISTERED) {
204 lpfc_vmid_update_entry(vport, iodir, vmp, tag);
205 write_unlock(&vport->vmid_lock);
206 return 0;
207 } else if (vmp && vmp->flag & LPFC_VMID_REQ_REGISTER) {
208 write_unlock(&vport->vmid_lock);
209 return -EBUSY;
210 }
211
212 /* else search and allocate a free slot in the hash table */
213 if (vport->cur_vmid_cnt < vport->max_vmid) {
214 for (i = 0; i < vport->max_vmid; i++) {
215 vmp = vport->vmid + i;
216 if (vmp->flag == LPFC_VMID_SLOT_FREE)
217 break;
218 }
219 if (i == vport->max_vmid)
220 vmp = NULL;
221 } else {
222 vmp = NULL;
223 }
224
225 if (!vmp) {
226 write_unlock(&vport->vmid_lock);
227 return -ENOMEM;
228 }
229
230 /* Add the vmid and register */
231 lpfc_put_vmid_in_hashtable(vport, hash, vmp);
232 vmp->vmid_len = len;
233 memcpy(vmp->host_vmid, uuid, vmp->vmid_len);
234 vmp->io_rd_cnt = 0;
235 vmp->io_wr_cnt = 0;
236 vmp->flag = LPFC_VMID_SLOT_USED;
237
238 vmp->delete_inactive =
239 vport->vmid_inactivity_timeout ? 1 : 0;
240
241 /* if type priority tag, get next available VMID */
242 if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO)
243 lpfc_vmid_assign_cs_ctl(vport, vmid: vmp);
244
245 /* allocate the per cpu variable for holding */
246 /* the last access time stamp only if VMID is enabled */
247 if (!vmp->last_io_time)
248 vmp->last_io_time = alloc_percpu_gfp(u64, GFP_ATOMIC);
249 if (!vmp->last_io_time) {
250 hash_del(node: &vmp->hnode);
251 vmp->flag = LPFC_VMID_SLOT_FREE;
252 write_unlock(&vport->vmid_lock);
253 return -EIO;
254 }
255
256 write_unlock(&vport->vmid_lock);
257
258 /* complete transaction with switch */
259 if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO)
260 rc = lpfc_vmid_uvem(vport, vmid: vmp, ins: true);
261 else if (vport->phba->cfg_vmid_app_header)
262 rc = lpfc_vmid_cmd(vport, SLI_CTAS_RAPP_IDENT, vmid: vmp);
263 if (!rc) {
264 write_lock(&vport->vmid_lock);
265 vport->cur_vmid_cnt++;
266 vmp->flag |= LPFC_VMID_REQ_REGISTER;
267 write_unlock(&vport->vmid_lock);
268 } else {
269 write_lock(&vport->vmid_lock);
270 hash_del(node: &vmp->hnode);
271 vmp->flag = LPFC_VMID_SLOT_FREE;
272 free_percpu(pdata: vmp->last_io_time);
273 write_unlock(&vport->vmid_lock);
274 return -EIO;
275 }
276
277 /* finally, enable the idle timer once */
278 if (!(vport->phba->pport->vmid_flag & LPFC_VMID_TIMER_ENBLD)) {
279 mod_timer(timer: &vport->phba->inactive_vmid_poll,
280 expires: jiffies +
281 msecs_to_jiffies(m: 1000 * LPFC_VMID_TIMER));
282 vport->phba->pport->vmid_flag |= LPFC_VMID_TIMER_ENBLD;
283 }
284 }
285 return rc;
286}
287
288/*
289 * lpfc_reinit_vmid - reinitializes the vmid data structure
290 * @vport: pointer to vport data structure
291 *
292 * This routine reinitializes the vmid post flogi completion
293 *
294 * Return codes
295 * None
296 */
297void
298lpfc_reinit_vmid(struct lpfc_vport *vport)
299{
300 u32 bucket, i, cpu;
301 struct lpfc_vmid *cur;
302 struct lpfc_vmid *vmp = NULL;
303 struct hlist_node *tmp;
304
305 write_lock(&vport->vmid_lock);
306 vport->cur_vmid_cnt = 0;
307
308 for (i = 0; i < vport->max_vmid; i++) {
309 vmp = &vport->vmid[i];
310 vmp->flag = LPFC_VMID_SLOT_FREE;
311 memset(vmp->host_vmid, 0, sizeof(vmp->host_vmid));
312 vmp->io_rd_cnt = 0;
313 vmp->io_wr_cnt = 0;
314
315 if (vmp->last_io_time)
316 for_each_possible_cpu(cpu)
317 *per_cpu_ptr(vmp->last_io_time, cpu) = 0;
318 }
319
320 /* for all elements in the hash table */
321 if (!hash_empty(vport->hash_table))
322 hash_for_each_safe(vport->hash_table, bucket, tmp, cur, hnode)
323 hash_del(node: &cur->hnode);
324 write_unlock(&vport->vmid_lock);
325}
326

source code of linux/drivers/scsi/lpfc/lpfc_vmid.c