GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / scsi / lpfc / lpfc_vmid.c
1 /*******************************************************************
2  * This file is part of the Emulex Linux Device Driver for         *
3  * Fibre Channel Host Bus Adapters.                                *
4  * Copyright (C) 2017-2022 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  */
47 struct 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(&vmp->host_vmid[0], buf, 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  */
68 static void
69 lpfc_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  */
81 int 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  */
107 static 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
129 static 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->host_vmid, vmid->vmid_len);
139                 pvmid =
140                     lpfc_get_vmid_from_hashtable(vport->phba->pport, hash,
141                                                  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  */
158 int 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(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, 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, 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, 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(sizeof(u64),
249                                                            __alignof__(struct
250                                                            lpfc_vmid));
251                 if (!vmp->last_io_time) {
252                         hash_del(&vmp->hnode);
253                         vmp->flag = LPFC_VMID_SLOT_FREE;
254                         write_unlock(&vport->vmid_lock);
255                         return -EIO;
256                 }
257
258                 write_unlock(&vport->vmid_lock);
259
260                 /* complete transaction with switch */
261                 if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO)
262                         rc = lpfc_vmid_uvem(vport, vmp, true);
263                 else if (vport->phba->cfg_vmid_app_header)
264                         rc = lpfc_vmid_cmd(vport, SLI_CTAS_RAPP_IDENT, vmp);
265                 if (!rc) {
266                         write_lock(&vport->vmid_lock);
267                         vport->cur_vmid_cnt++;
268                         vmp->flag |= LPFC_VMID_REQ_REGISTER;
269                         write_unlock(&vport->vmid_lock);
270                 } else {
271                         write_lock(&vport->vmid_lock);
272                         hash_del(&vmp->hnode);
273                         vmp->flag = LPFC_VMID_SLOT_FREE;
274                         free_percpu(vmp->last_io_time);
275                         write_unlock(&vport->vmid_lock);
276                         return -EIO;
277                 }
278
279                 /* finally, enable the idle timer once */
280                 if (!(vport->phba->pport->vmid_flag & LPFC_VMID_TIMER_ENBLD)) {
281                         mod_timer(&vport->phba->inactive_vmid_poll,
282                                   jiffies +
283                                   msecs_to_jiffies(1000 * LPFC_VMID_TIMER));
284                         vport->phba->pport->vmid_flag |= LPFC_VMID_TIMER_ENBLD;
285                 }
286         }
287         return rc;
288 }