1 /* SPDX-License-Identifier: BSD-3-Clause 2 * 3 * Copyright (c) 2016-2018 Solarflare Communications Inc. 4 * All rights reserved. 5 * 6 * This software was jointly developed between OKTET Labs (under contract 7 * for Solarflare) and Solarflare Communications, Inc. 8 */ 9 10 /* 11 * At the momemt of writing DPDK v16.07 has notion of two types of 12 * interrupts: LSC (link status change) and RXQ (receive indication). 13 * It allows to register interrupt callback for entire device which is 14 * not intended to be used for receive indication (i.e. link status 15 * change indication only). The handler has no information which HW 16 * interrupt has triggered it, so we don't know which event queue should 17 * be polled/reprimed (except qmask in the case of legacy line interrupt). 18 */ 19 20 #include <rte_common.h> 21 #include <rte_interrupts.h> 22 23 #include "efx.h" 24 25 #include "sfc.h" 26 #include "sfc_log.h" 27 #include "sfc_ev.h" 28 29 static void 30 sfc_intr_handle_mgmt_evq(struct sfc_adapter *sa) 31 { 32 struct sfc_evq *evq; 33 34 rte_spinlock_lock(&sa->mgmt_evq_lock); 35 36 evq = sa->mgmt_evq; 37 38 if (!sa->mgmt_evq_running) { 39 sfc_log_init(sa, "interrupt on not running management EVQ %u", 40 evq->evq_index); 41 } else { 42 sfc_ev_qpoll(evq); 43 44 if (sfc_ev_qprime(evq) != 0) 45 sfc_err(sa, "cannot prime EVQ %u", evq->evq_index); 46 } 47 48 rte_spinlock_unlock(&sa->mgmt_evq_lock); 49 } 50 51 static void 52 sfc_intr_line_handler(void *cb_arg) 53 { 54 struct sfc_adapter *sa = (struct sfc_adapter *)cb_arg; 55 efx_nic_t *enp = sa->nic; 56 boolean_t fatal; 57 uint32_t qmask; 58 unsigned int lsc_seq = sa->port.lsc_seq; 59 struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(sa->eth_dev); 60 61 sfc_log_init(sa, "entry"); 62 63 if (sa->state != SFC_ADAPTER_STARTED && 64 sa->state != SFC_ADAPTER_STARTING && 65 sa->state != SFC_ADAPTER_STOPPING) { 66 sfc_log_init(sa, 67 "interrupt on stopped adapter, don't reenable"); 68 goto exit; 69 } 70 71 efx_intr_status_line(enp, &fatal, &qmask); 72 if (fatal) { 73 (void)efx_intr_disable(enp); 74 (void)efx_intr_fatal(enp); 75 sfc_err(sa, "fatal, interrupts disabled"); 76 goto exit; 77 } 78 79 if (qmask & (1 << sa->mgmt_evq_index)) 80 sfc_intr_handle_mgmt_evq(sa); 81 82 if (rte_intr_enable(&pci_dev->intr_handle) != 0) 83 sfc_err(sa, "cannot reenable interrupts"); 84 85 sfc_log_init(sa, "done"); 86 87 exit: 88 if (lsc_seq != sa->port.lsc_seq) { 89 sfc_info(sa, "link status change event: link %s", 90 sa->eth_dev->data->dev_link.link_status ? 91 "UP" : "DOWN"); 92 _rte_eth_dev_callback_process(sa->eth_dev, 93 RTE_ETH_EVENT_INTR_LSC, 94 NULL); 95 } 96 } 97 98 static void 99 sfc_intr_message_handler(void *cb_arg) 100 { 101 struct sfc_adapter *sa = (struct sfc_adapter *)cb_arg; 102 efx_nic_t *enp = sa->nic; 103 boolean_t fatal; 104 unsigned int lsc_seq = sa->port.lsc_seq; 105 struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(sa->eth_dev); 106 107 sfc_log_init(sa, "entry"); 108 109 if (sa->state != SFC_ADAPTER_STARTED && 110 sa->state != SFC_ADAPTER_STARTING && 111 sa->state != SFC_ADAPTER_STOPPING) { 112 sfc_log_init(sa, "adapter not-started, don't reenable"); 113 goto exit; 114 } 115 116 efx_intr_status_message(enp, sa->mgmt_evq_index, &fatal); 117 if (fatal) { 118 (void)efx_intr_disable(enp); 119 (void)efx_intr_fatal(enp); 120 sfc_err(sa, "fatal, interrupts disabled"); 121 goto exit; 122 } 123 124 sfc_intr_handle_mgmt_evq(sa); 125 126 if (rte_intr_enable(&pci_dev->intr_handle) != 0) 127 sfc_err(sa, "cannot reenable interrupts"); 128 129 sfc_log_init(sa, "done"); 130 131 exit: 132 if (lsc_seq != sa->port.lsc_seq) { 133 sfc_info(sa, "link status change event"); 134 _rte_eth_dev_callback_process(sa->eth_dev, 135 RTE_ETH_EVENT_INTR_LSC, 136 NULL); 137 } 138 } 139 140 int 141 sfc_intr_start(struct sfc_adapter *sa) 142 { 143 struct sfc_intr *intr = &sa->intr; 144 struct rte_intr_handle *intr_handle; 145 struct rte_pci_device *pci_dev; 146 int rc; 147 148 sfc_log_init(sa, "entry"); 149 150 /* 151 * The EFX common code event queue module depends on the interrupt 152 * module. Ensure that the interrupt module is always initialized 153 * (even if interrupts are not used). Status memory is required 154 * for Siena only and may be NULL for EF10. 155 */ 156 sfc_log_init(sa, "efx_intr_init"); 157 rc = efx_intr_init(sa->nic, intr->type, NULL); 158 if (rc != 0) 159 goto fail_intr_init; 160 161 pci_dev = RTE_ETH_DEV_TO_PCI(sa->eth_dev); 162 intr_handle = &pci_dev->intr_handle; 163 164 if (intr->handler != NULL) { 165 sfc_log_init(sa, "rte_intr_callback_register"); 166 rc = rte_intr_callback_register(intr_handle, intr->handler, 167 (void *)sa); 168 if (rc != 0) { 169 sfc_err(sa, 170 "cannot register interrupt handler (rc=%d)", 171 rc); 172 /* 173 * Convert error code from negative returned by RTE API 174 * to positive used in the driver. 175 */ 176 rc = -rc; 177 goto fail_rte_intr_cb_reg; 178 } 179 180 sfc_log_init(sa, "rte_intr_enable"); 181 rc = rte_intr_enable(intr_handle); 182 if (rc != 0) { 183 sfc_err(sa, "cannot enable interrupts (rc=%d)", rc); 184 /* 185 * Convert error code from negative returned by RTE API 186 * to positive used in the driver. 187 */ 188 rc = -rc; 189 goto fail_rte_intr_enable; 190 } 191 192 sfc_log_init(sa, "efx_intr_enable"); 193 efx_intr_enable(sa->nic); 194 } 195 196 sfc_log_init(sa, "done type=%u max_intr=%d nb_efd=%u vec=%p", 197 intr_handle->type, intr_handle->max_intr, 198 intr_handle->nb_efd, intr_handle->intr_vec); 199 return 0; 200 201 fail_rte_intr_enable: 202 rte_intr_callback_unregister(intr_handle, intr->handler, (void *)sa); 203 204 fail_rte_intr_cb_reg: 205 efx_intr_fini(sa->nic); 206 207 fail_intr_init: 208 sfc_log_init(sa, "failed %d", rc); 209 return rc; 210 } 211 212 void 213 sfc_intr_stop(struct sfc_adapter *sa) 214 { 215 struct sfc_intr *intr = &sa->intr; 216 struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(sa->eth_dev); 217 218 sfc_log_init(sa, "entry"); 219 220 if (intr->handler != NULL) { 221 struct rte_intr_handle *intr_handle; 222 int rc; 223 224 efx_intr_disable(sa->nic); 225 226 intr_handle = &pci_dev->intr_handle; 227 if (rte_intr_disable(intr_handle) != 0) 228 sfc_err(sa, "cannot disable interrupts"); 229 230 while ((rc = rte_intr_callback_unregister(intr_handle, 231 intr->handler, (void *)sa)) == -EAGAIN) 232 ; 233 if (rc != 1) 234 sfc_err(sa, 235 "cannot unregister interrupt handler %d", 236 rc); 237 } 238 239 efx_intr_fini(sa->nic); 240 241 sfc_log_init(sa, "done"); 242 } 243 244 int 245 sfc_intr_configure(struct sfc_adapter *sa) 246 { 247 struct sfc_intr *intr = &sa->intr; 248 249 sfc_log_init(sa, "entry"); 250 251 intr->handler = NULL; 252 intr->lsc_intr = (sa->eth_dev->data->dev_conf.intr_conf.lsc != 0); 253 if (!intr->lsc_intr) { 254 sfc_info(sa, "LSC tracking using interrupts is disabled"); 255 goto done; 256 } 257 258 switch (intr->type) { 259 case EFX_INTR_MESSAGE: 260 intr->handler = sfc_intr_message_handler; 261 break; 262 case EFX_INTR_LINE: 263 intr->handler = sfc_intr_line_handler; 264 break; 265 case EFX_INTR_INVALID: 266 sfc_warn(sa, "interrupts are not supported"); 267 break; 268 default: 269 sfc_panic(sa, "unexpected EFX interrupt type %u\n", intr->type); 270 break; 271 } 272 273 done: 274 sfc_log_init(sa, "done"); 275 return 0; 276 } 277 278 void 279 sfc_intr_close(struct sfc_adapter *sa) 280 { 281 sfc_log_init(sa, "entry"); 282 283 sfc_log_init(sa, "done"); 284 } 285 286 int 287 sfc_intr_attach(struct sfc_adapter *sa) 288 { 289 struct sfc_intr *intr = &sa->intr; 290 struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(sa->eth_dev); 291 292 sfc_log_init(sa, "entry"); 293 294 switch (pci_dev->intr_handle.type) { 295 #ifdef RTE_EXEC_ENV_LINUXAPP 296 case RTE_INTR_HANDLE_UIO_INTX: 297 case RTE_INTR_HANDLE_VFIO_LEGACY: 298 intr->type = EFX_INTR_LINE; 299 break; 300 case RTE_INTR_HANDLE_UIO: 301 case RTE_INTR_HANDLE_VFIO_MSI: 302 case RTE_INTR_HANDLE_VFIO_MSIX: 303 intr->type = EFX_INTR_MESSAGE; 304 break; 305 #endif 306 default: 307 intr->type = EFX_INTR_INVALID; 308 break; 309 } 310 311 sfc_log_init(sa, "done"); 312 return 0; 313 } 314 315 void 316 sfc_intr_detach(struct sfc_adapter *sa) 317 { 318 sfc_log_init(sa, "entry"); 319 320 sa->intr.type = EFX_INTR_INVALID; 321 322 sfc_log_init(sa, "done"); 323 } 324