lpfc_vmid.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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. #include <linux/interrupt.h>
  24. #include <linux/dma-direction.h>
  25. #include <scsi/scsi_transport_fc.h>
  26. #include "lpfc_hw4.h"
  27. #include "lpfc_hw.h"
  28. #include "lpfc_sli.h"
  29. #include "lpfc_sli4.h"
  30. #include "lpfc_nl.h"
  31. #include "lpfc_disc.h"
  32. #include "lpfc.h"
  33. #include "lpfc_crtn.h"
  34. /*
  35. * lpfc_get_vmid_from_hashtable - search the UUID in the hash table
  36. * @vport: The virtual port for which this call is being executed.
  37. * @hash: calculated hash value
  38. * @buf: uuid associated with the VE
  39. * Return the VMID entry associated with the UUID
  40. * Make sure to acquire the appropriate lock before invoking this routine.
  41. */
  42. struct lpfc_vmid *lpfc_get_vmid_from_hashtable(struct lpfc_vport *vport,
  43. u32 hash, u8 *buf)
  44. {
  45. struct lpfc_vmid *vmp;
  46. hash_for_each_possible(vport->hash_table, vmp, hnode, hash) {
  47. if (memcmp(&vmp->host_vmid[0], buf, 16) == 0)
  48. return vmp;
  49. }
  50. return NULL;
  51. }
  52. /*
  53. * lpfc_put_vmid_in_hashtable - put the VMID in the hash table
  54. * @vport: The virtual port for which this call is being executed.
  55. * @hash - calculated hash value
  56. * @vmp: Pointer to a VMID entry representing a VM sending I/O
  57. *
  58. * This routine will insert the newly acquired VMID entity in the hash table.
  59. * Make sure to acquire the appropriate lock before invoking this routine.
  60. */
  61. static void
  62. lpfc_put_vmid_in_hashtable(struct lpfc_vport *vport, u32 hash,
  63. struct lpfc_vmid *vmp)
  64. {
  65. hash_add(vport->hash_table, &vmp->hnode, hash);
  66. }
  67. /*
  68. * lpfc_vmid_hash_fn - create a hash value of the UUID
  69. * @vmid: uuid associated with the VE
  70. * @len: length of the VMID string
  71. * Returns the calculated hash value
  72. */
  73. int lpfc_vmid_hash_fn(const char *vmid, int len)
  74. {
  75. int c;
  76. int hash = 0;
  77. if (len == 0)
  78. return 0;
  79. while (len--) {
  80. c = *vmid++;
  81. if (c >= 'A' && c <= 'Z')
  82. c += 'a' - 'A';
  83. hash = (hash + (c << LPFC_VMID_HASH_SHIFT) +
  84. (c >> LPFC_VMID_HASH_SHIFT)) * 19;
  85. }
  86. return hash & LPFC_VMID_HASH_MASK;
  87. }
  88. /*
  89. * lpfc_vmid_update_entry - update the vmid entry in the hash table
  90. * @vport: The virtual port for which this call is being executed.
  91. * @iodir: io direction
  92. * @vmp: Pointer to a VMID entry representing a VM sending I/O
  93. * @tag: VMID tag
  94. */
  95. static void lpfc_vmid_update_entry(struct lpfc_vport *vport,
  96. enum dma_data_direction iodir,
  97. struct lpfc_vmid *vmp,
  98. union lpfc_vmid_io_tag *tag)
  99. {
  100. u64 *lta;
  101. if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO)
  102. tag->cs_ctl_vmid = vmp->un.cs_ctl_vmid;
  103. else if (vport->phba->cfg_vmid_app_header)
  104. tag->app_id = vmp->un.app_id;
  105. if (iodir == DMA_TO_DEVICE)
  106. vmp->io_wr_cnt++;
  107. else if (iodir == DMA_FROM_DEVICE)
  108. vmp->io_rd_cnt++;
  109. /* update the last access timestamp in the table */
  110. lta = per_cpu_ptr(vmp->last_io_time, raw_smp_processor_id());
  111. *lta = jiffies;
  112. }
  113. static void lpfc_vmid_assign_cs_ctl(struct lpfc_vport *vport,
  114. struct lpfc_vmid *vmid)
  115. {
  116. u32 hash;
  117. struct lpfc_vmid *pvmid;
  118. if (vport->port_type == LPFC_PHYSICAL_PORT) {
  119. vmid->un.cs_ctl_vmid = lpfc_vmid_get_cs_ctl(vport);
  120. } else {
  121. hash = lpfc_vmid_hash_fn(vmid->host_vmid, vmid->vmid_len);
  122. pvmid =
  123. lpfc_get_vmid_from_hashtable(vport->phba->pport, hash,
  124. vmid->host_vmid);
  125. if (pvmid)
  126. vmid->un.cs_ctl_vmid = pvmid->un.cs_ctl_vmid;
  127. else
  128. vmid->un.cs_ctl_vmid = lpfc_vmid_get_cs_ctl(vport);
  129. }
  130. }
  131. /*
  132. * lpfc_vmid_get_appid - get the VMID associated with the UUID
  133. * @vport: The virtual port for which this call is being executed.
  134. * @uuid: UUID associated with the VE
  135. * @cmd: address of scsi_cmd descriptor
  136. * @iodir: io direction
  137. * @tag: VMID tag
  138. * Returns status of the function
  139. */
  140. int lpfc_vmid_get_appid(struct lpfc_vport *vport, char *uuid,
  141. enum dma_data_direction iodir,
  142. union lpfc_vmid_io_tag *tag)
  143. {
  144. struct lpfc_vmid *vmp = NULL;
  145. int hash, len, rc = -EPERM, i;
  146. /* check if QFPA is complete */
  147. if (lpfc_vmid_is_type_priority_tag(vport) &&
  148. !(vport->vmid_flag & LPFC_VMID_QFPA_CMPL) &&
  149. (vport->vmid_flag & LPFC_VMID_ISSUE_QFPA)) {
  150. vport->work_port_events |= WORKER_CHECK_VMID_ISSUE_QFPA;
  151. return -EAGAIN;
  152. }
  153. /* search if the UUID has already been mapped to the VMID */
  154. len = strlen(uuid);
  155. hash = lpfc_vmid_hash_fn(uuid, len);
  156. /* search for the VMID in the table */
  157. read_lock(&vport->vmid_lock);
  158. vmp = lpfc_get_vmid_from_hashtable(vport, hash, uuid);
  159. /* if found, check if its already registered */
  160. if (vmp && vmp->flag & LPFC_VMID_REGISTERED) {
  161. read_unlock(&vport->vmid_lock);
  162. lpfc_vmid_update_entry(vport, iodir, vmp, tag);
  163. rc = 0;
  164. } else if (vmp && (vmp->flag & LPFC_VMID_REQ_REGISTER ||
  165. vmp->flag & LPFC_VMID_DE_REGISTER)) {
  166. /* else if register or dereg request has already been sent */
  167. /* Hence VMID tag will not be added for this I/O */
  168. read_unlock(&vport->vmid_lock);
  169. rc = -EBUSY;
  170. } else {
  171. /* The VMID was not found in the hashtable. At this point, */
  172. /* drop the read lock first before proceeding further */
  173. read_unlock(&vport->vmid_lock);
  174. /* start the process to obtain one as per the */
  175. /* type of the VMID indicated */
  176. write_lock(&vport->vmid_lock);
  177. vmp = lpfc_get_vmid_from_hashtable(vport, hash, uuid);
  178. /* while the read lock was released, in case the entry was */
  179. /* added by other context or is in process of being added */
  180. if (vmp && vmp->flag & LPFC_VMID_REGISTERED) {
  181. lpfc_vmid_update_entry(vport, iodir, vmp, tag);
  182. write_unlock(&vport->vmid_lock);
  183. return 0;
  184. } else if (vmp && vmp->flag & LPFC_VMID_REQ_REGISTER) {
  185. write_unlock(&vport->vmid_lock);
  186. return -EBUSY;
  187. }
  188. /* else search and allocate a free slot in the hash table */
  189. if (vport->cur_vmid_cnt < vport->max_vmid) {
  190. for (i = 0; i < vport->max_vmid; i++) {
  191. vmp = vport->vmid + i;
  192. if (vmp->flag == LPFC_VMID_SLOT_FREE)
  193. break;
  194. }
  195. if (i == vport->max_vmid)
  196. vmp = NULL;
  197. } else {
  198. vmp = NULL;
  199. }
  200. if (!vmp) {
  201. write_unlock(&vport->vmid_lock);
  202. return -ENOMEM;
  203. }
  204. /* Add the vmid and register */
  205. lpfc_put_vmid_in_hashtable(vport, hash, vmp);
  206. vmp->vmid_len = len;
  207. memcpy(vmp->host_vmid, uuid, vmp->vmid_len);
  208. vmp->io_rd_cnt = 0;
  209. vmp->io_wr_cnt = 0;
  210. vmp->flag = LPFC_VMID_SLOT_USED;
  211. vmp->delete_inactive =
  212. vport->vmid_inactivity_timeout ? 1 : 0;
  213. /* if type priority tag, get next available VMID */
  214. if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO)
  215. lpfc_vmid_assign_cs_ctl(vport, vmp);
  216. /* allocate the per cpu variable for holding */
  217. /* the last access time stamp only if VMID is enabled */
  218. if (!vmp->last_io_time)
  219. vmp->last_io_time = alloc_percpu_gfp(u64, GFP_ATOMIC);
  220. if (!vmp->last_io_time) {
  221. hash_del(&vmp->hnode);
  222. vmp->flag = LPFC_VMID_SLOT_FREE;
  223. write_unlock(&vport->vmid_lock);
  224. return -EIO;
  225. }
  226. write_unlock(&vport->vmid_lock);
  227. /* complete transaction with switch */
  228. if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO)
  229. rc = lpfc_vmid_uvem(vport, vmp, true);
  230. else if (vport->phba->cfg_vmid_app_header)
  231. rc = lpfc_vmid_cmd(vport, SLI_CTAS_RAPP_IDENT, vmp);
  232. if (!rc) {
  233. write_lock(&vport->vmid_lock);
  234. vport->cur_vmid_cnt++;
  235. vmp->flag |= LPFC_VMID_REQ_REGISTER;
  236. write_unlock(&vport->vmid_lock);
  237. } else {
  238. write_lock(&vport->vmid_lock);
  239. hash_del(&vmp->hnode);
  240. vmp->flag = LPFC_VMID_SLOT_FREE;
  241. free_percpu(vmp->last_io_time);
  242. write_unlock(&vport->vmid_lock);
  243. return -EIO;
  244. }
  245. /* finally, enable the idle timer once */
  246. if (!(vport->phba->pport->vmid_flag & LPFC_VMID_TIMER_ENBLD)) {
  247. mod_timer(&vport->phba->inactive_vmid_poll,
  248. jiffies +
  249. msecs_to_jiffies(1000 * LPFC_VMID_TIMER));
  250. vport->phba->pport->vmid_flag |= LPFC_VMID_TIMER_ENBLD;
  251. }
  252. }
  253. return rc;
  254. }