19517SBill.Taylor@Sun.COM /*
29517SBill.Taylor@Sun.COM * CDDL HEADER START
39517SBill.Taylor@Sun.COM *
49517SBill.Taylor@Sun.COM * The contents of this file are subject to the terms of the
59517SBill.Taylor@Sun.COM * Common Development and Distribution License (the "License").
69517SBill.Taylor@Sun.COM * You may not use this file except in compliance with the License.
79517SBill.Taylor@Sun.COM *
89517SBill.Taylor@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99517SBill.Taylor@Sun.COM * or http://www.opensolaris.org/os/licensing.
109517SBill.Taylor@Sun.COM * See the License for the specific language governing permissions
119517SBill.Taylor@Sun.COM * and limitations under the License.
129517SBill.Taylor@Sun.COM *
139517SBill.Taylor@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
149517SBill.Taylor@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159517SBill.Taylor@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
169517SBill.Taylor@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
179517SBill.Taylor@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
189517SBill.Taylor@Sun.COM *
199517SBill.Taylor@Sun.COM * CDDL HEADER END
209517SBill.Taylor@Sun.COM */
219517SBill.Taylor@Sun.COM
229517SBill.Taylor@Sun.COM /*
23*12965SWilliam.Taylor@Oracle.COM * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
249517SBill.Taylor@Sun.COM */
259517SBill.Taylor@Sun.COM
269517SBill.Taylor@Sun.COM /*
279517SBill.Taylor@Sun.COM * hermon_cq.c
289517SBill.Taylor@Sun.COM * Hermon Completion Queue Processing Routines
299517SBill.Taylor@Sun.COM *
309517SBill.Taylor@Sun.COM * Implements all the routines necessary for allocating, freeing, resizing,
319517SBill.Taylor@Sun.COM * and handling the completion type events that the Hermon hardware can
329517SBill.Taylor@Sun.COM * generate.
339517SBill.Taylor@Sun.COM */
349517SBill.Taylor@Sun.COM
359517SBill.Taylor@Sun.COM #include <sys/types.h>
369517SBill.Taylor@Sun.COM #include <sys/conf.h>
379517SBill.Taylor@Sun.COM #include <sys/ddi.h>
389517SBill.Taylor@Sun.COM #include <sys/sunddi.h>
399517SBill.Taylor@Sun.COM #include <sys/modctl.h>
409517SBill.Taylor@Sun.COM #include <sys/bitmap.h>
419517SBill.Taylor@Sun.COM #include <sys/sysmacros.h>
429517SBill.Taylor@Sun.COM
439517SBill.Taylor@Sun.COM #include <sys/ib/adapters/hermon/hermon.h>
449517SBill.Taylor@Sun.COM
459517SBill.Taylor@Sun.COM int hermon_should_panic = 0; /* debugging aid */
469517SBill.Taylor@Sun.COM
479517SBill.Taylor@Sun.COM #define hermon_cq_update_ci_doorbell(cq) \
489517SBill.Taylor@Sun.COM /* Build the doorbell record data (low 24 bits only) */ \
499517SBill.Taylor@Sun.COM HERMON_UAR_DB_RECORD_WRITE(cq->cq_arm_ci_vdbr, \
509517SBill.Taylor@Sun.COM cq->cq_consindx & 0x00FFFFFF)
519517SBill.Taylor@Sun.COM
529517SBill.Taylor@Sun.COM static int hermon_cq_arm_doorbell(hermon_state_t *state, hermon_cqhdl_t cq,
539517SBill.Taylor@Sun.COM uint_t cmd);
549517SBill.Taylor@Sun.COM #pragma inline(hermon_cq_arm_doorbell)
559517SBill.Taylor@Sun.COM static void hermon_arm_cq_dbr_init(hermon_dbr_t *cq_arm_dbr);
569517SBill.Taylor@Sun.COM #pragma inline(hermon_arm_cq_dbr_init)
579517SBill.Taylor@Sun.COM static void hermon_cq_cqe_consume(hermon_state_t *state, hermon_cqhdl_t cq,
589517SBill.Taylor@Sun.COM hermon_hw_cqe_t *cqe, ibt_wc_t *wc);
599517SBill.Taylor@Sun.COM static void hermon_cq_errcqe_consume(hermon_state_t *state, hermon_cqhdl_t cq,
609517SBill.Taylor@Sun.COM hermon_hw_cqe_t *cqe, ibt_wc_t *wc);
619517SBill.Taylor@Sun.COM
629517SBill.Taylor@Sun.COM
639517SBill.Taylor@Sun.COM /*
649517SBill.Taylor@Sun.COM * hermon_cq_alloc()
659517SBill.Taylor@Sun.COM * Context: Can be called only from user or kernel context.
669517SBill.Taylor@Sun.COM */
679517SBill.Taylor@Sun.COM int
hermon_cq_alloc(hermon_state_t * state,ibt_cq_hdl_t ibt_cqhdl,ibt_cq_attr_t * cq_attr,uint_t * actual_size,hermon_cqhdl_t * cqhdl,uint_t sleepflag)689517SBill.Taylor@Sun.COM hermon_cq_alloc(hermon_state_t *state, ibt_cq_hdl_t ibt_cqhdl,
699517SBill.Taylor@Sun.COM ibt_cq_attr_t *cq_attr, uint_t *actual_size, hermon_cqhdl_t *cqhdl,
709517SBill.Taylor@Sun.COM uint_t sleepflag)
719517SBill.Taylor@Sun.COM {
729517SBill.Taylor@Sun.COM hermon_rsrc_t *cqc, *rsrc;
739517SBill.Taylor@Sun.COM hermon_umap_db_entry_t *umapdb;
749517SBill.Taylor@Sun.COM hermon_hw_cqc_t cqc_entry;
759517SBill.Taylor@Sun.COM hermon_cqhdl_t cq;
769517SBill.Taylor@Sun.COM ibt_mr_attr_t mr_attr;
779517SBill.Taylor@Sun.COM hermon_mr_options_t op;
789517SBill.Taylor@Sun.COM hermon_pdhdl_t pd;
799517SBill.Taylor@Sun.COM hermon_mrhdl_t mr;
809517SBill.Taylor@Sun.COM hermon_hw_cqe_t *buf;
819517SBill.Taylor@Sun.COM uint64_t value;
829517SBill.Taylor@Sun.COM uint32_t log_cq_size, uarpg;
839517SBill.Taylor@Sun.COM uint_t cq_is_umap;
849517SBill.Taylor@Sun.COM uint32_t status, flag;
85*12965SWilliam.Taylor@Oracle.COM hermon_cq_sched_t *cq_schedp;
869517SBill.Taylor@Sun.COM
879517SBill.Taylor@Sun.COM _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cq_attr))
889517SBill.Taylor@Sun.COM
899517SBill.Taylor@Sun.COM /*
909517SBill.Taylor@Sun.COM * Determine whether CQ is being allocated for userland access or
919517SBill.Taylor@Sun.COM * whether it is being allocated for kernel access. If the CQ is
929517SBill.Taylor@Sun.COM * being allocated for userland access, then lookup the UAR
939517SBill.Taylor@Sun.COM * page number for the current process. Note: If this is not found
949517SBill.Taylor@Sun.COM * (e.g. if the process has not previously open()'d the Hermon driver),
959517SBill.Taylor@Sun.COM * then an error is returned.
969517SBill.Taylor@Sun.COM */
979517SBill.Taylor@Sun.COM cq_is_umap = (cq_attr->cq_flags & IBT_CQ_USER_MAP) ? 1 : 0;
989517SBill.Taylor@Sun.COM if (cq_is_umap) {
999517SBill.Taylor@Sun.COM status = hermon_umap_db_find(state->hs_instance, ddi_get_pid(),
1009517SBill.Taylor@Sun.COM MLNX_UMAP_UARPG_RSRC, &value, 0, NULL);
1019517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
1029517SBill.Taylor@Sun.COM status = IBT_INVALID_PARAM;
1039517SBill.Taylor@Sun.COM goto cqalloc_fail;
1049517SBill.Taylor@Sun.COM }
1059517SBill.Taylor@Sun.COM uarpg = ((hermon_rsrc_t *)(uintptr_t)value)->hr_indx;
1069517SBill.Taylor@Sun.COM } else {
1079517SBill.Taylor@Sun.COM uarpg = state->hs_kernel_uar_index;
1089517SBill.Taylor@Sun.COM }
1099517SBill.Taylor@Sun.COM
1109517SBill.Taylor@Sun.COM /* Use the internal protection domain (PD) for setting up CQs */
1119517SBill.Taylor@Sun.COM pd = state->hs_pdhdl_internal;
1129517SBill.Taylor@Sun.COM
1139517SBill.Taylor@Sun.COM /* Increment the reference count on the protection domain (PD) */
1149517SBill.Taylor@Sun.COM hermon_pd_refcnt_inc(pd);
1159517SBill.Taylor@Sun.COM
1169517SBill.Taylor@Sun.COM /*
1179517SBill.Taylor@Sun.COM * Allocate an CQ context entry. This will be filled in with all
1189517SBill.Taylor@Sun.COM * the necessary parameters to define the Completion Queue. And then
1199517SBill.Taylor@Sun.COM * ownership will be passed to the hardware in the final step
1209517SBill.Taylor@Sun.COM * below. If we fail here, we must undo the protection domain
1219517SBill.Taylor@Sun.COM * reference count.
1229517SBill.Taylor@Sun.COM */
1239517SBill.Taylor@Sun.COM status = hermon_rsrc_alloc(state, HERMON_CQC, 1, sleepflag, &cqc);
1249517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
1259517SBill.Taylor@Sun.COM status = IBT_INSUFF_RESOURCE;
1269517SBill.Taylor@Sun.COM goto cqalloc_fail1;
1279517SBill.Taylor@Sun.COM }
1289517SBill.Taylor@Sun.COM
1299517SBill.Taylor@Sun.COM /*
1309517SBill.Taylor@Sun.COM * Allocate the software structure for tracking the completion queue
1319517SBill.Taylor@Sun.COM * (i.e. the Hermon Completion Queue handle). If we fail here, we must
1329517SBill.Taylor@Sun.COM * undo the protection domain reference count and the previous
1339517SBill.Taylor@Sun.COM * resource allocation.
1349517SBill.Taylor@Sun.COM */
1359517SBill.Taylor@Sun.COM status = hermon_rsrc_alloc(state, HERMON_CQHDL, 1, sleepflag, &rsrc);
1369517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
1379517SBill.Taylor@Sun.COM status = IBT_INSUFF_RESOURCE;
1389517SBill.Taylor@Sun.COM goto cqalloc_fail2;
1399517SBill.Taylor@Sun.COM }
1409517SBill.Taylor@Sun.COM cq = (hermon_cqhdl_t)rsrc->hr_addr;
1419517SBill.Taylor@Sun.COM _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cq))
1429517SBill.Taylor@Sun.COM cq->cq_is_umap = cq_is_umap;
1439517SBill.Taylor@Sun.COM cq->cq_cqnum = cqc->hr_indx; /* just use index, implicit in Hermon */
144*12965SWilliam.Taylor@Oracle.COM cq->cq_intmod_count = 0;
145*12965SWilliam.Taylor@Oracle.COM cq->cq_intmod_usec = 0;
1469517SBill.Taylor@Sun.COM
1479517SBill.Taylor@Sun.COM /*
1489517SBill.Taylor@Sun.COM * If this will be a user-mappable CQ, then allocate an entry for
1499517SBill.Taylor@Sun.COM * the "userland resources database". This will later be added to
1509517SBill.Taylor@Sun.COM * the database (after all further CQ operations are successful).
1519517SBill.Taylor@Sun.COM * If we fail here, we must undo the reference counts and the
1529517SBill.Taylor@Sun.COM * previous resource allocation.
1539517SBill.Taylor@Sun.COM */
1549517SBill.Taylor@Sun.COM if (cq->cq_is_umap) {
1559517SBill.Taylor@Sun.COM umapdb = hermon_umap_db_alloc(state->hs_instance, cq->cq_cqnum,
1569517SBill.Taylor@Sun.COM MLNX_UMAP_CQMEM_RSRC, (uint64_t)(uintptr_t)rsrc);
1579517SBill.Taylor@Sun.COM if (umapdb == NULL) {
1589517SBill.Taylor@Sun.COM status = IBT_INSUFF_RESOURCE;
1599517SBill.Taylor@Sun.COM goto cqalloc_fail3;
1609517SBill.Taylor@Sun.COM }
1619517SBill.Taylor@Sun.COM }
1629517SBill.Taylor@Sun.COM
1639517SBill.Taylor@Sun.COM
1649517SBill.Taylor@Sun.COM /*
1659517SBill.Taylor@Sun.COM * Allocate the doorbell record. We'll need one for the CQ, handling
1669517SBill.Taylor@Sun.COM * both consumer index (SET CI) and the CQ state (CQ ARM).
1679517SBill.Taylor@Sun.COM */
1689517SBill.Taylor@Sun.COM
1699517SBill.Taylor@Sun.COM status = hermon_dbr_alloc(state, uarpg, &cq->cq_arm_ci_dbr_acchdl,
1709517SBill.Taylor@Sun.COM &cq->cq_arm_ci_vdbr, &cq->cq_arm_ci_pdbr, &cq->cq_dbr_mapoffset);
1719517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
1729517SBill.Taylor@Sun.COM status = IBT_INSUFF_RESOURCE;
1739517SBill.Taylor@Sun.COM goto cqalloc_fail4;
1749517SBill.Taylor@Sun.COM }
1759517SBill.Taylor@Sun.COM
1769517SBill.Taylor@Sun.COM /*
1779517SBill.Taylor@Sun.COM * Calculate the appropriate size for the completion queue.
1789517SBill.Taylor@Sun.COM * Note: All Hermon CQs must be a power-of-2 minus 1 in size. Also
1799517SBill.Taylor@Sun.COM * they may not be any smaller than HERMON_CQ_MIN_SIZE. This step is
1809517SBill.Taylor@Sun.COM * to round the requested size up to the next highest power-of-2
1819517SBill.Taylor@Sun.COM */
1829517SBill.Taylor@Sun.COM cq_attr->cq_size = max(cq_attr->cq_size, HERMON_CQ_MIN_SIZE);
1839517SBill.Taylor@Sun.COM log_cq_size = highbit(cq_attr->cq_size);
1849517SBill.Taylor@Sun.COM
1859517SBill.Taylor@Sun.COM /*
1869517SBill.Taylor@Sun.COM * Next we verify that the rounded-up size is valid (i.e. consistent
1879517SBill.Taylor@Sun.COM * with the device limits and/or software-configured limits)
1889517SBill.Taylor@Sun.COM */
1899517SBill.Taylor@Sun.COM if (log_cq_size > state->hs_cfg_profile->cp_log_max_cq_sz) {
1909517SBill.Taylor@Sun.COM status = IBT_HCA_CQ_EXCEEDED;
1919517SBill.Taylor@Sun.COM goto cqalloc_fail4a;
1929517SBill.Taylor@Sun.COM }
1939517SBill.Taylor@Sun.COM
1949517SBill.Taylor@Sun.COM /*
1959517SBill.Taylor@Sun.COM * Allocate the memory for Completion Queue.
1969517SBill.Taylor@Sun.COM *
1979517SBill.Taylor@Sun.COM * Note: Although we use the common queue allocation routine, we
1989517SBill.Taylor@Sun.COM * always specify HERMON_QUEUE_LOCATION_NORMAL (i.e. CQ located in
1999517SBill.Taylor@Sun.COM * kernel system memory) for kernel CQs because it would be
2009517SBill.Taylor@Sun.COM * inefficient to have CQs located in DDR memory. This is primarily
2019517SBill.Taylor@Sun.COM * because CQs are read from (by software) more than they are written
2029517SBill.Taylor@Sun.COM * to. (We always specify HERMON_QUEUE_LOCATION_USERLAND for all
2039517SBill.Taylor@Sun.COM * user-mappable CQs for a similar reason.)
2049517SBill.Taylor@Sun.COM * It is also worth noting that, unlike Hermon QP work queues,
2059517SBill.Taylor@Sun.COM * completion queues do not have the same strict alignment
2069517SBill.Taylor@Sun.COM * requirements. It is sufficient for the CQ memory to be both
2079517SBill.Taylor@Sun.COM * aligned to and bound to addresses which are a multiple of CQE size.
2089517SBill.Taylor@Sun.COM */
2099517SBill.Taylor@Sun.COM cq->cq_cqinfo.qa_size = (1 << log_cq_size) * sizeof (hermon_hw_cqe_t);
2109517SBill.Taylor@Sun.COM
2119517SBill.Taylor@Sun.COM cq->cq_cqinfo.qa_alloc_align = PAGESIZE;
2129517SBill.Taylor@Sun.COM cq->cq_cqinfo.qa_bind_align = PAGESIZE;
2139517SBill.Taylor@Sun.COM if (cq->cq_is_umap) {
2149517SBill.Taylor@Sun.COM cq->cq_cqinfo.qa_location = HERMON_QUEUE_LOCATION_USERLAND;
2159517SBill.Taylor@Sun.COM } else {
2169517SBill.Taylor@Sun.COM cq->cq_cqinfo.qa_location = HERMON_QUEUE_LOCATION_NORMAL;
2179517SBill.Taylor@Sun.COM hermon_arm_cq_dbr_init(cq->cq_arm_ci_vdbr);
2189517SBill.Taylor@Sun.COM }
2199517SBill.Taylor@Sun.COM status = hermon_queue_alloc(state, &cq->cq_cqinfo, sleepflag);
2209517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
2219517SBill.Taylor@Sun.COM status = IBT_INSUFF_RESOURCE;
2229517SBill.Taylor@Sun.COM goto cqalloc_fail4;
2239517SBill.Taylor@Sun.COM }
2249517SBill.Taylor@Sun.COM buf = (hermon_hw_cqe_t *)cq->cq_cqinfo.qa_buf_aligned;
2259517SBill.Taylor@Sun.COM _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*buf))
2269517SBill.Taylor@Sun.COM
2279517SBill.Taylor@Sun.COM /*
2289517SBill.Taylor@Sun.COM * The ownership bit of the CQE's is set by the HW during the process
2299517SBill.Taylor@Sun.COM * of transferrring ownership of the CQ (PRM 09.35c, 14.2.1, note D1
2309517SBill.Taylor@Sun.COM *
2319517SBill.Taylor@Sun.COM */
2329517SBill.Taylor@Sun.COM
2339517SBill.Taylor@Sun.COM /*
2349517SBill.Taylor@Sun.COM * Register the memory for the CQ. The memory for the CQ must
2359517SBill.Taylor@Sun.COM * be registered in the Hermon TPT tables. This gives us the LKey
2369517SBill.Taylor@Sun.COM * to specify in the CQ context below. Note: If this is a user-
2379517SBill.Taylor@Sun.COM * mappable CQ, then we will force DDI_DMA_CONSISTENT mapping.
2389517SBill.Taylor@Sun.COM */
2399517SBill.Taylor@Sun.COM flag = (sleepflag == HERMON_SLEEP) ? IBT_MR_SLEEP : IBT_MR_NOSLEEP;
2409517SBill.Taylor@Sun.COM mr_attr.mr_vaddr = (uint64_t)(uintptr_t)buf;
2419517SBill.Taylor@Sun.COM mr_attr.mr_len = cq->cq_cqinfo.qa_size;
2429517SBill.Taylor@Sun.COM mr_attr.mr_as = NULL;
2439517SBill.Taylor@Sun.COM mr_attr.mr_flags = flag | IBT_MR_ENABLE_LOCAL_WRITE;
2449517SBill.Taylor@Sun.COM op.mro_bind_type = state->hs_cfg_profile->cp_iommu_bypass;
2459517SBill.Taylor@Sun.COM op.mro_bind_dmahdl = cq->cq_cqinfo.qa_dmahdl;
2469517SBill.Taylor@Sun.COM op.mro_bind_override_addr = 0;
2479517SBill.Taylor@Sun.COM status = hermon_mr_register(state, pd, &mr_attr, &mr, &op,
2489517SBill.Taylor@Sun.COM HERMON_CQ_CMPT);
2499517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
2509517SBill.Taylor@Sun.COM status = IBT_INSUFF_RESOURCE;
2519517SBill.Taylor@Sun.COM goto cqalloc_fail5;
2529517SBill.Taylor@Sun.COM }
2539517SBill.Taylor@Sun.COM _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
2549517SBill.Taylor@Sun.COM
255*12965SWilliam.Taylor@Oracle.COM cq->cq_erreqnum = HERMON_CQ_ERREQNUM_GET(state);
256*12965SWilliam.Taylor@Oracle.COM if (cq_attr->cq_flags & IBT_CQ_HID) {
257*12965SWilliam.Taylor@Oracle.COM if (!HERMON_HID_VALID(state, cq_attr->cq_hid)) {
258*12965SWilliam.Taylor@Oracle.COM IBTF_DPRINTF_L2("CQalloc", "bad handler id 0x%x",
259*12965SWilliam.Taylor@Oracle.COM cq_attr->cq_hid);
260*12965SWilliam.Taylor@Oracle.COM status = IBT_INVALID_PARAM;
261*12965SWilliam.Taylor@Oracle.COM goto cqalloc_fail5;
262*12965SWilliam.Taylor@Oracle.COM }
263*12965SWilliam.Taylor@Oracle.COM cq->cq_eqnum = HERMON_HID_TO_EQNUM(state, cq_attr->cq_hid);
264*12965SWilliam.Taylor@Oracle.COM IBTF_DPRINTF_L2("cqalloc", "hid: eqn %d", cq->cq_eqnum);
265*12965SWilliam.Taylor@Oracle.COM } else {
266*12965SWilliam.Taylor@Oracle.COM cq_schedp = (hermon_cq_sched_t *)cq_attr->cq_sched;
267*12965SWilliam.Taylor@Oracle.COM if (cq_schedp == NULL) {
268*12965SWilliam.Taylor@Oracle.COM cq_schedp = &state->hs_cq_sched_default;
269*12965SWilliam.Taylor@Oracle.COM } else if (cq_schedp != &state->hs_cq_sched_default) {
270*12965SWilliam.Taylor@Oracle.COM int i;
271*12965SWilliam.Taylor@Oracle.COM hermon_cq_sched_t *tmp;
272*12965SWilliam.Taylor@Oracle.COM
273*12965SWilliam.Taylor@Oracle.COM tmp = state->hs_cq_sched_array;
274*12965SWilliam.Taylor@Oracle.COM for (i = 0; i < state->hs_cq_sched_array_size; i++)
275*12965SWilliam.Taylor@Oracle.COM if (cq_schedp == &tmp[i])
276*12965SWilliam.Taylor@Oracle.COM break; /* found it */
277*12965SWilliam.Taylor@Oracle.COM if (i >= state->hs_cq_sched_array_size) {
278*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "!Invalid cq_sched argument: "
279*12965SWilliam.Taylor@Oracle.COM "ignored\n");
280*12965SWilliam.Taylor@Oracle.COM cq_schedp = &state->hs_cq_sched_default;
281*12965SWilliam.Taylor@Oracle.COM }
282*12965SWilliam.Taylor@Oracle.COM }
283*12965SWilliam.Taylor@Oracle.COM cq->cq_eqnum = HERMON_HID_TO_EQNUM(state,
284*12965SWilliam.Taylor@Oracle.COM HERMON_CQSCHED_NEXT_HID(cq_schedp));
285*12965SWilliam.Taylor@Oracle.COM IBTF_DPRINTF_L2("cqalloc", "sched: first-1 %d, len %d, "
286*12965SWilliam.Taylor@Oracle.COM "eqn %d", cq_schedp->cqs_start_hid - 1,
287*12965SWilliam.Taylor@Oracle.COM cq_schedp->cqs_len, cq->cq_eqnum);
288*12965SWilliam.Taylor@Oracle.COM }
2899517SBill.Taylor@Sun.COM
2909517SBill.Taylor@Sun.COM /*
2919517SBill.Taylor@Sun.COM * Fill in the CQC entry. This is the final step before passing
2929517SBill.Taylor@Sun.COM * ownership of the CQC entry to the Hermon hardware. We use all of
2939517SBill.Taylor@Sun.COM * the information collected/calculated above to fill in the
2949517SBill.Taylor@Sun.COM * requisite portions of the CQC. Note: If this CQ is going to be
2959517SBill.Taylor@Sun.COM * used for userland access, then we need to set the UAR page number
2969517SBill.Taylor@Sun.COM * appropriately (otherwise it's a "don't care")
2979517SBill.Taylor@Sun.COM */
2989517SBill.Taylor@Sun.COM bzero(&cqc_entry, sizeof (hermon_hw_cqc_t));
2999517SBill.Taylor@Sun.COM
3009517SBill.Taylor@Sun.COM cqc_entry.state = HERMON_CQ_DISARMED;
3019517SBill.Taylor@Sun.COM cqc_entry.pg_offs = cq->cq_cqinfo.qa_pgoffs >> 5;
3029517SBill.Taylor@Sun.COM cqc_entry.log_cq_sz = log_cq_size;
3039517SBill.Taylor@Sun.COM cqc_entry.usr_page = uarpg;
3049517SBill.Taylor@Sun.COM cqc_entry.c_eqn = cq->cq_eqnum;
3059517SBill.Taylor@Sun.COM cqc_entry.log2_pgsz = mr->mr_log2_pgsz;
3069517SBill.Taylor@Sun.COM cqc_entry.mtt_base_addh = (uint32_t)((mr->mr_mttaddr >> 32) & 0xFF);
3079517SBill.Taylor@Sun.COM cqc_entry.mtt_base_addl = mr->mr_mttaddr >> 3;
3089517SBill.Taylor@Sun.COM cqc_entry.dbr_addrh = (uint32_t)((uint64_t)cq->cq_arm_ci_pdbr >> 32);
3099517SBill.Taylor@Sun.COM cqc_entry.dbr_addrl = (uint32_t)((uint64_t)cq->cq_arm_ci_pdbr >> 3);
3109517SBill.Taylor@Sun.COM
3119517SBill.Taylor@Sun.COM /*
3129517SBill.Taylor@Sun.COM * Write the CQC entry to hardware - we pass ownership of
3139517SBill.Taylor@Sun.COM * the entry to the hardware (using the Hermon SW2HW_CQ firmware
3149517SBill.Taylor@Sun.COM * command). Note: In general, this operation shouldn't fail. But
3159517SBill.Taylor@Sun.COM * if it does, we have to undo everything we've done above before
3169517SBill.Taylor@Sun.COM * returning error.
3179517SBill.Taylor@Sun.COM */
3189517SBill.Taylor@Sun.COM status = hermon_cmn_ownership_cmd_post(state, SW2HW_CQ, &cqc_entry,
3199517SBill.Taylor@Sun.COM sizeof (hermon_hw_cqc_t), cq->cq_cqnum, sleepflag);
3209517SBill.Taylor@Sun.COM if (status != HERMON_CMD_SUCCESS) {
3219517SBill.Taylor@Sun.COM cmn_err(CE_CONT, "Hermon: SW2HW_CQ command failed: %08x\n",
3229517SBill.Taylor@Sun.COM status);
3239517SBill.Taylor@Sun.COM if (status == HERMON_CMD_INVALID_STATUS) {
3249517SBill.Taylor@Sun.COM hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
3259517SBill.Taylor@Sun.COM }
3269517SBill.Taylor@Sun.COM status = ibc_get_ci_failure(0);
3279517SBill.Taylor@Sun.COM goto cqalloc_fail6;
3289517SBill.Taylor@Sun.COM }
3299517SBill.Taylor@Sun.COM
3309517SBill.Taylor@Sun.COM /*
3319517SBill.Taylor@Sun.COM * Fill in the rest of the Hermon Completion Queue handle. Having
3329517SBill.Taylor@Sun.COM * successfully transferred ownership of the CQC, we can update the
3339517SBill.Taylor@Sun.COM * following fields for use in further operations on the CQ.
3349517SBill.Taylor@Sun.COM */
3359517SBill.Taylor@Sun.COM cq->cq_resize_hdl = 0;
3369517SBill.Taylor@Sun.COM cq->cq_cqcrsrcp = cqc;
3379517SBill.Taylor@Sun.COM cq->cq_rsrcp = rsrc;
3389517SBill.Taylor@Sun.COM cq->cq_consindx = 0;
3399517SBill.Taylor@Sun.COM /* least restrictive */
3409517SBill.Taylor@Sun.COM cq->cq_buf = buf;
3419517SBill.Taylor@Sun.COM cq->cq_bufsz = (1 << log_cq_size);
3429517SBill.Taylor@Sun.COM cq->cq_log_cqsz = log_cq_size;
3439517SBill.Taylor@Sun.COM cq->cq_mrhdl = mr;
3449517SBill.Taylor@Sun.COM cq->cq_refcnt = 0;
3459517SBill.Taylor@Sun.COM cq->cq_is_special = 0;
3469517SBill.Taylor@Sun.COM cq->cq_uarpg = uarpg;
3479517SBill.Taylor@Sun.COM cq->cq_umap_dhp = (devmap_cookie_t)NULL;
3489517SBill.Taylor@Sun.COM avl_create(&cq->cq_wrid_wqhdr_avl_tree, hermon_wrid_workq_compare,
3499517SBill.Taylor@Sun.COM sizeof (struct hermon_workq_avl_s),
3509517SBill.Taylor@Sun.COM offsetof(struct hermon_workq_avl_s, wqa_link));
3519517SBill.Taylor@Sun.COM
3529517SBill.Taylor@Sun.COM cq->cq_hdlrarg = (void *)ibt_cqhdl;
3539517SBill.Taylor@Sun.COM
3549517SBill.Taylor@Sun.COM /*
3559517SBill.Taylor@Sun.COM * Put CQ handle in Hermon CQNum-to-CQHdl list. Then fill in the
3569517SBill.Taylor@Sun.COM * "actual_size" and "cqhdl" and return success
3579517SBill.Taylor@Sun.COM */
358*12965SWilliam.Taylor@Oracle.COM hermon_icm_set_num_to_hdl(state, HERMON_CQC, cqc->hr_indx, cq);
3599517SBill.Taylor@Sun.COM
3609517SBill.Taylor@Sun.COM /*
3619517SBill.Taylor@Sun.COM * If this is a user-mappable CQ, then we need to insert the previously
3629517SBill.Taylor@Sun.COM * allocated entry into the "userland resources database". This will
3639517SBill.Taylor@Sun.COM * allow for later lookup during devmap() (i.e. mmap()) calls.
3649517SBill.Taylor@Sun.COM */
3659517SBill.Taylor@Sun.COM if (cq->cq_is_umap) {
3669517SBill.Taylor@Sun.COM hermon_umap_db_add(umapdb);
3679517SBill.Taylor@Sun.COM }
3689517SBill.Taylor@Sun.COM
3699517SBill.Taylor@Sun.COM /*
3709517SBill.Taylor@Sun.COM * Fill in the return arguments (if necessary). This includes the
3719517SBill.Taylor@Sun.COM * real completion queue size.
3729517SBill.Taylor@Sun.COM */
3739517SBill.Taylor@Sun.COM if (actual_size != NULL) {
3749517SBill.Taylor@Sun.COM *actual_size = (1 << log_cq_size) - 1;
3759517SBill.Taylor@Sun.COM }
3769517SBill.Taylor@Sun.COM *cqhdl = cq;
3779517SBill.Taylor@Sun.COM
3789517SBill.Taylor@Sun.COM return (DDI_SUCCESS);
3799517SBill.Taylor@Sun.COM
3809517SBill.Taylor@Sun.COM /*
3819517SBill.Taylor@Sun.COM * The following is cleanup for all possible failure cases in this routine
3829517SBill.Taylor@Sun.COM */
3839517SBill.Taylor@Sun.COM cqalloc_fail6:
3849517SBill.Taylor@Sun.COM if (hermon_mr_deregister(state, &mr, HERMON_MR_DEREG_ALL,
3859517SBill.Taylor@Sun.COM sleepflag) != DDI_SUCCESS) {
3869517SBill.Taylor@Sun.COM HERMON_WARNING(state, "failed to deregister CQ memory");
3879517SBill.Taylor@Sun.COM }
3889517SBill.Taylor@Sun.COM cqalloc_fail5:
3899517SBill.Taylor@Sun.COM hermon_queue_free(&cq->cq_cqinfo);
3909517SBill.Taylor@Sun.COM cqalloc_fail4a:
3919517SBill.Taylor@Sun.COM hermon_dbr_free(state, uarpg, cq->cq_arm_ci_vdbr);
3929517SBill.Taylor@Sun.COM cqalloc_fail4:
3939517SBill.Taylor@Sun.COM if (cq_is_umap) {
3949517SBill.Taylor@Sun.COM hermon_umap_db_free(umapdb);
3959517SBill.Taylor@Sun.COM }
3969517SBill.Taylor@Sun.COM cqalloc_fail3:
3979517SBill.Taylor@Sun.COM hermon_rsrc_free(state, &rsrc);
3989517SBill.Taylor@Sun.COM cqalloc_fail2:
3999517SBill.Taylor@Sun.COM hermon_rsrc_free(state, &cqc);
4009517SBill.Taylor@Sun.COM cqalloc_fail1:
4019517SBill.Taylor@Sun.COM hermon_pd_refcnt_dec(pd);
4029517SBill.Taylor@Sun.COM cqalloc_fail:
4039517SBill.Taylor@Sun.COM return (status);
4049517SBill.Taylor@Sun.COM }
4059517SBill.Taylor@Sun.COM
4069517SBill.Taylor@Sun.COM
4079517SBill.Taylor@Sun.COM /*
4089517SBill.Taylor@Sun.COM * hermon_cq_free()
4099517SBill.Taylor@Sun.COM * Context: Can be called only from user or kernel context.
4109517SBill.Taylor@Sun.COM */
4119517SBill.Taylor@Sun.COM /* ARGSUSED */
4129517SBill.Taylor@Sun.COM int
hermon_cq_free(hermon_state_t * state,hermon_cqhdl_t * cqhdl,uint_t sleepflag)4139517SBill.Taylor@Sun.COM hermon_cq_free(hermon_state_t *state, hermon_cqhdl_t *cqhdl, uint_t sleepflag)
4149517SBill.Taylor@Sun.COM {
4159517SBill.Taylor@Sun.COM hermon_rsrc_t *cqc, *rsrc;
4169517SBill.Taylor@Sun.COM hermon_umap_db_entry_t *umapdb;
4179517SBill.Taylor@Sun.COM hermon_hw_cqc_t cqc_entry;
4189517SBill.Taylor@Sun.COM hermon_pdhdl_t pd;
4199517SBill.Taylor@Sun.COM hermon_mrhdl_t mr;
4209517SBill.Taylor@Sun.COM hermon_cqhdl_t cq, resize;
4219517SBill.Taylor@Sun.COM uint32_t cqnum;
4229517SBill.Taylor@Sun.COM uint64_t value;
4239517SBill.Taylor@Sun.COM uint_t maxprot;
4249517SBill.Taylor@Sun.COM int status;
4259517SBill.Taylor@Sun.COM
4269517SBill.Taylor@Sun.COM /*
4279517SBill.Taylor@Sun.COM * Pull all the necessary information from the Hermon Completion Queue
4289517SBill.Taylor@Sun.COM * handle. This is necessary here because the resource for the
4299517SBill.Taylor@Sun.COM * CQ handle is going to be freed up as part of this operation.
4309517SBill.Taylor@Sun.COM */
4319517SBill.Taylor@Sun.COM cq = *cqhdl;
4329517SBill.Taylor@Sun.COM mutex_enter(&cq->cq_lock);
4339517SBill.Taylor@Sun.COM cqc = cq->cq_cqcrsrcp;
4349517SBill.Taylor@Sun.COM rsrc = cq->cq_rsrcp;
4359517SBill.Taylor@Sun.COM pd = state->hs_pdhdl_internal;
4369517SBill.Taylor@Sun.COM mr = cq->cq_mrhdl;
4379517SBill.Taylor@Sun.COM cqnum = cq->cq_cqnum;
4389517SBill.Taylor@Sun.COM
4399517SBill.Taylor@Sun.COM resize = cq->cq_resize_hdl; /* save the handle for later */
4409517SBill.Taylor@Sun.COM
4419517SBill.Taylor@Sun.COM /*
4429517SBill.Taylor@Sun.COM * If there are work queues still associated with the CQ, then return
4439517SBill.Taylor@Sun.COM * an error. Otherwise, we will be holding the CQ lock.
4449517SBill.Taylor@Sun.COM */
4459517SBill.Taylor@Sun.COM if (cq->cq_refcnt != 0) {
4469517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
4479517SBill.Taylor@Sun.COM return (IBT_CQ_BUSY);
4489517SBill.Taylor@Sun.COM }
4499517SBill.Taylor@Sun.COM
4509517SBill.Taylor@Sun.COM /*
4519517SBill.Taylor@Sun.COM * If this was a user-mappable CQ, then we need to remove its entry
4529517SBill.Taylor@Sun.COM * from the "userland resources database". If it is also currently
4539517SBill.Taylor@Sun.COM * mmap()'d out to a user process, then we need to call
4549517SBill.Taylor@Sun.COM * devmap_devmem_remap() to remap the CQ memory to an invalid mapping.
4559517SBill.Taylor@Sun.COM * We also need to invalidate the CQ tracking information for the
4569517SBill.Taylor@Sun.COM * user mapping.
4579517SBill.Taylor@Sun.COM */
4589517SBill.Taylor@Sun.COM if (cq->cq_is_umap) {
4599517SBill.Taylor@Sun.COM status = hermon_umap_db_find(state->hs_instance, cqnum,
4609517SBill.Taylor@Sun.COM MLNX_UMAP_CQMEM_RSRC, &value, HERMON_UMAP_DB_REMOVE,
4619517SBill.Taylor@Sun.COM &umapdb);
4629517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
4639517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
4649517SBill.Taylor@Sun.COM HERMON_WARNING(state, "failed to find in database");
4659517SBill.Taylor@Sun.COM return (ibc_get_ci_failure(0));
4669517SBill.Taylor@Sun.COM }
4679517SBill.Taylor@Sun.COM hermon_umap_db_free(umapdb);
4689517SBill.Taylor@Sun.COM if (cq->cq_umap_dhp != NULL) {
4699517SBill.Taylor@Sun.COM maxprot = (PROT_READ | PROT_WRITE | PROT_USER);
4709517SBill.Taylor@Sun.COM status = devmap_devmem_remap(cq->cq_umap_dhp,
4719517SBill.Taylor@Sun.COM state->hs_dip, 0, 0, cq->cq_cqinfo.qa_size,
4729517SBill.Taylor@Sun.COM maxprot, DEVMAP_MAPPING_INVALID, NULL);
4739517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
4749517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
4759517SBill.Taylor@Sun.COM HERMON_WARNING(state, "failed in CQ memory "
4769517SBill.Taylor@Sun.COM "devmap_devmem_remap()");
4779517SBill.Taylor@Sun.COM return (ibc_get_ci_failure(0));
4789517SBill.Taylor@Sun.COM }
4799517SBill.Taylor@Sun.COM cq->cq_umap_dhp = (devmap_cookie_t)NULL;
4809517SBill.Taylor@Sun.COM }
4819517SBill.Taylor@Sun.COM }
4829517SBill.Taylor@Sun.COM
4839517SBill.Taylor@Sun.COM /*
4849517SBill.Taylor@Sun.COM * Put NULL into the Arbel CQNum-to-CQHdl list. This will allow any
4859517SBill.Taylor@Sun.COM * in-progress events to detect that the CQ corresponding to this
4869517SBill.Taylor@Sun.COM * number has been freed.
4879517SBill.Taylor@Sun.COM */
488*12965SWilliam.Taylor@Oracle.COM hermon_icm_set_num_to_hdl(state, HERMON_CQC, cqc->hr_indx, NULL);
4899517SBill.Taylor@Sun.COM
4909517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
4919517SBill.Taylor@Sun.COM _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cq))
4929517SBill.Taylor@Sun.COM
4939517SBill.Taylor@Sun.COM /*
4949517SBill.Taylor@Sun.COM * Reclaim CQC entry from hardware (using the Hermon HW2SW_CQ
4959517SBill.Taylor@Sun.COM * firmware command). If the ownership transfer fails for any reason,
4969517SBill.Taylor@Sun.COM * then it is an indication that something (either in HW or SW) has
4979517SBill.Taylor@Sun.COM * gone seriously wrong.
4989517SBill.Taylor@Sun.COM */
4999517SBill.Taylor@Sun.COM status = hermon_cmn_ownership_cmd_post(state, HW2SW_CQ, &cqc_entry,
5009517SBill.Taylor@Sun.COM sizeof (hermon_hw_cqc_t), cqnum, sleepflag);
5019517SBill.Taylor@Sun.COM if (status != HERMON_CMD_SUCCESS) {
5029517SBill.Taylor@Sun.COM HERMON_WARNING(state, "failed to reclaim CQC ownership");
5039517SBill.Taylor@Sun.COM cmn_err(CE_CONT, "Hermon: HW2SW_CQ command failed: %08x\n",
5049517SBill.Taylor@Sun.COM status);
5059517SBill.Taylor@Sun.COM if (status == HERMON_CMD_INVALID_STATUS) {
5069517SBill.Taylor@Sun.COM hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
5079517SBill.Taylor@Sun.COM }
5089517SBill.Taylor@Sun.COM return (ibc_get_ci_failure(0));
5099517SBill.Taylor@Sun.COM }
5109517SBill.Taylor@Sun.COM
5119517SBill.Taylor@Sun.COM /*
5129517SBill.Taylor@Sun.COM * From here on, we start reliquishing resources - but check to see
5139517SBill.Taylor@Sun.COM * if a resize was in progress - if so, we need to relinquish those
5149517SBill.Taylor@Sun.COM * resources as well
5159517SBill.Taylor@Sun.COM */
5169517SBill.Taylor@Sun.COM
5179517SBill.Taylor@Sun.COM
5189517SBill.Taylor@Sun.COM /*
5199517SBill.Taylor@Sun.COM * Deregister the memory for the Completion Queue. If this fails
5209517SBill.Taylor@Sun.COM * for any reason, then it is an indication that something (either
5219517SBill.Taylor@Sun.COM * in HW or SW) has gone seriously wrong. So we print a warning
5229517SBill.Taylor@Sun.COM * message and return.
5239517SBill.Taylor@Sun.COM */
5249517SBill.Taylor@Sun.COM status = hermon_mr_deregister(state, &mr, HERMON_MR_DEREG_ALL,
5259517SBill.Taylor@Sun.COM sleepflag);
5269517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
5279517SBill.Taylor@Sun.COM HERMON_WARNING(state, "failed to deregister CQ memory");
5289517SBill.Taylor@Sun.COM return (ibc_get_ci_failure(0));
5299517SBill.Taylor@Sun.COM }
5309517SBill.Taylor@Sun.COM
5319517SBill.Taylor@Sun.COM if (resize) { /* there was a pointer to a handle */
5329517SBill.Taylor@Sun.COM mr = resize->cq_mrhdl; /* reuse the pointer to the region */
5339517SBill.Taylor@Sun.COM status = hermon_mr_deregister(state, &mr, HERMON_MR_DEREG_ALL,
5349517SBill.Taylor@Sun.COM sleepflag);
5359517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
5369517SBill.Taylor@Sun.COM HERMON_WARNING(state, "failed to deregister resize CQ "
5379517SBill.Taylor@Sun.COM "memory");
5389517SBill.Taylor@Sun.COM return (ibc_get_ci_failure(0));
5399517SBill.Taylor@Sun.COM }
5409517SBill.Taylor@Sun.COM }
5419517SBill.Taylor@Sun.COM
5429517SBill.Taylor@Sun.COM /* Free the memory for the CQ */
5439517SBill.Taylor@Sun.COM hermon_queue_free(&cq->cq_cqinfo);
5449517SBill.Taylor@Sun.COM if (resize) {
5459517SBill.Taylor@Sun.COM hermon_queue_free(&resize->cq_cqinfo);
5469517SBill.Taylor@Sun.COM /* and the temporary handle */
5479517SBill.Taylor@Sun.COM kmem_free(resize, sizeof (struct hermon_sw_cq_s));
5489517SBill.Taylor@Sun.COM }
5499517SBill.Taylor@Sun.COM
5509517SBill.Taylor@Sun.COM /* everything else does not matter for the resize in progress */
5519517SBill.Taylor@Sun.COM
5529517SBill.Taylor@Sun.COM /* Free the dbr */
5539517SBill.Taylor@Sun.COM hermon_dbr_free(state, cq->cq_uarpg, cq->cq_arm_ci_vdbr);
5549517SBill.Taylor@Sun.COM
5559517SBill.Taylor@Sun.COM /* Free the Hermon Completion Queue handle */
5569517SBill.Taylor@Sun.COM hermon_rsrc_free(state, &rsrc);
5579517SBill.Taylor@Sun.COM
5589517SBill.Taylor@Sun.COM /* Free up the CQC entry resource */
5599517SBill.Taylor@Sun.COM hermon_rsrc_free(state, &cqc);
5609517SBill.Taylor@Sun.COM
5619517SBill.Taylor@Sun.COM /* Decrement the reference count on the protection domain (PD) */
5629517SBill.Taylor@Sun.COM hermon_pd_refcnt_dec(pd);
5639517SBill.Taylor@Sun.COM
5649517SBill.Taylor@Sun.COM /* Set the cqhdl pointer to NULL and return success */
5659517SBill.Taylor@Sun.COM *cqhdl = NULL;
5669517SBill.Taylor@Sun.COM
5679517SBill.Taylor@Sun.COM return (DDI_SUCCESS);
5689517SBill.Taylor@Sun.COM }
5699517SBill.Taylor@Sun.COM
5709517SBill.Taylor@Sun.COM
5719517SBill.Taylor@Sun.COM /*
5729517SBill.Taylor@Sun.COM * hermon_cq_resize()
5739517SBill.Taylor@Sun.COM * Context: Can be called only from user or kernel context.
5749517SBill.Taylor@Sun.COM */
5759517SBill.Taylor@Sun.COM int
hermon_cq_resize(hermon_state_t * state,hermon_cqhdl_t cq,uint_t req_size,uint_t * actual_size,uint_t sleepflag)5769517SBill.Taylor@Sun.COM hermon_cq_resize(hermon_state_t *state, hermon_cqhdl_t cq, uint_t req_size,
5779517SBill.Taylor@Sun.COM uint_t *actual_size, uint_t sleepflag)
5789517SBill.Taylor@Sun.COM {
5799517SBill.Taylor@Sun.COM hermon_hw_cqc_t cqc_entry;
5809517SBill.Taylor@Sun.COM hermon_cqhdl_t resize_hdl;
5819517SBill.Taylor@Sun.COM hermon_qalloc_info_t new_cqinfo;
5829517SBill.Taylor@Sun.COM ibt_mr_attr_t mr_attr;
5839517SBill.Taylor@Sun.COM hermon_mr_options_t op;
5849517SBill.Taylor@Sun.COM hermon_pdhdl_t pd;
5859517SBill.Taylor@Sun.COM hermon_mrhdl_t mr;
5869517SBill.Taylor@Sun.COM hermon_hw_cqe_t *buf;
5879517SBill.Taylor@Sun.COM uint32_t new_prod_indx;
5889517SBill.Taylor@Sun.COM uint_t log_cq_size;
5899517SBill.Taylor@Sun.COM int status, flag;
5909517SBill.Taylor@Sun.COM
5919517SBill.Taylor@Sun.COM if (cq->cq_resize_hdl != 0) { /* already in process */
5929517SBill.Taylor@Sun.COM status = IBT_CQ_BUSY;
5939517SBill.Taylor@Sun.COM goto cqresize_fail;
5949517SBill.Taylor@Sun.COM }
5959517SBill.Taylor@Sun.COM
5969517SBill.Taylor@Sun.COM
5979517SBill.Taylor@Sun.COM /* Use the internal protection domain (PD) for CQs */
5989517SBill.Taylor@Sun.COM pd = state->hs_pdhdl_internal;
5999517SBill.Taylor@Sun.COM
6009517SBill.Taylor@Sun.COM /*
6019517SBill.Taylor@Sun.COM * Calculate the appropriate size for the new resized completion queue.
6029517SBill.Taylor@Sun.COM * Note: All Hermon CQs must be a power-of-2 minus 1 in size. Also
6039517SBill.Taylor@Sun.COM * they may not be any smaller than HERMON_CQ_MIN_SIZE. This step is
6049517SBill.Taylor@Sun.COM * to round the requested size up to the next highest power-of-2
6059517SBill.Taylor@Sun.COM */
6069517SBill.Taylor@Sun.COM req_size = max(req_size, HERMON_CQ_MIN_SIZE);
6079517SBill.Taylor@Sun.COM log_cq_size = highbit(req_size);
6089517SBill.Taylor@Sun.COM
6099517SBill.Taylor@Sun.COM /*
6109517SBill.Taylor@Sun.COM * Next we verify that the rounded-up size is valid (i.e. consistent
6119517SBill.Taylor@Sun.COM * with the device limits and/or software-configured limits)
6129517SBill.Taylor@Sun.COM */
6139517SBill.Taylor@Sun.COM if (log_cq_size > state->hs_cfg_profile->cp_log_max_cq_sz) {
6149517SBill.Taylor@Sun.COM status = IBT_HCA_CQ_EXCEEDED;
6159517SBill.Taylor@Sun.COM goto cqresize_fail;
6169517SBill.Taylor@Sun.COM }
6179517SBill.Taylor@Sun.COM
6189517SBill.Taylor@Sun.COM /*
6199517SBill.Taylor@Sun.COM * Allocate the memory for newly resized Completion Queue.
6209517SBill.Taylor@Sun.COM *
6219517SBill.Taylor@Sun.COM * Note: Although we use the common queue allocation routine, we
6229517SBill.Taylor@Sun.COM * always specify HERMON_QUEUE_LOCATION_NORMAL (i.e. CQ located in
6239517SBill.Taylor@Sun.COM * kernel system memory) for kernel CQs because it would be
6249517SBill.Taylor@Sun.COM * inefficient to have CQs located in DDR memory. This is the same
6259517SBill.Taylor@Sun.COM * as we do when we first allocate completion queues primarily
6269517SBill.Taylor@Sun.COM * because CQs are read from (by software) more than they are written
6279517SBill.Taylor@Sun.COM * to. (We always specify HERMON_QUEUE_LOCATION_USERLAND for all
6289517SBill.Taylor@Sun.COM * user-mappable CQs for a similar reason.)
6299517SBill.Taylor@Sun.COM * It is also worth noting that, unlike Hermon QP work queues,
6309517SBill.Taylor@Sun.COM * completion queues do not have the same strict alignment
6319517SBill.Taylor@Sun.COM * requirements. It is sufficient for the CQ memory to be both
6329517SBill.Taylor@Sun.COM * aligned to and bound to addresses which are a multiple of CQE size.
6339517SBill.Taylor@Sun.COM */
6349517SBill.Taylor@Sun.COM
6359517SBill.Taylor@Sun.COM /* first, alloc the resize_handle */
6369517SBill.Taylor@Sun.COM resize_hdl = kmem_zalloc(sizeof (struct hermon_sw_cq_s), KM_SLEEP);
6379517SBill.Taylor@Sun.COM
6389517SBill.Taylor@Sun.COM new_cqinfo.qa_size = (1 << log_cq_size) * sizeof (hermon_hw_cqe_t);
6399517SBill.Taylor@Sun.COM new_cqinfo.qa_alloc_align = PAGESIZE;
6409517SBill.Taylor@Sun.COM new_cqinfo.qa_bind_align = PAGESIZE;
6419517SBill.Taylor@Sun.COM if (cq->cq_is_umap) {
6429517SBill.Taylor@Sun.COM new_cqinfo.qa_location = HERMON_QUEUE_LOCATION_USERLAND;
6439517SBill.Taylor@Sun.COM } else {
6449517SBill.Taylor@Sun.COM new_cqinfo.qa_location = HERMON_QUEUE_LOCATION_NORMAL;
6459517SBill.Taylor@Sun.COM }
6469517SBill.Taylor@Sun.COM status = hermon_queue_alloc(state, &new_cqinfo, sleepflag);
6479517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
6489517SBill.Taylor@Sun.COM /* free the resize handle */
6499517SBill.Taylor@Sun.COM kmem_free(resize_hdl, sizeof (struct hermon_sw_cq_s));
6509517SBill.Taylor@Sun.COM status = IBT_INSUFF_RESOURCE;
6519517SBill.Taylor@Sun.COM goto cqresize_fail;
6529517SBill.Taylor@Sun.COM }
6539517SBill.Taylor@Sun.COM buf = (hermon_hw_cqe_t *)new_cqinfo.qa_buf_aligned;
6549517SBill.Taylor@Sun.COM _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*buf))
6559517SBill.Taylor@Sun.COM
6569517SBill.Taylor@Sun.COM /*
6579517SBill.Taylor@Sun.COM * No initialization of the cq is needed - the command will do it
6589517SBill.Taylor@Sun.COM */
6599517SBill.Taylor@Sun.COM
6609517SBill.Taylor@Sun.COM /*
6619517SBill.Taylor@Sun.COM * Register the memory for the CQ. The memory for the CQ must
6629517SBill.Taylor@Sun.COM * be registered in the Hermon TPT tables. This gives us the LKey
6639517SBill.Taylor@Sun.COM * to specify in the CQ context below.
6649517SBill.Taylor@Sun.COM */
6659517SBill.Taylor@Sun.COM flag = (sleepflag == HERMON_SLEEP) ? IBT_MR_SLEEP : IBT_MR_NOSLEEP;
6669517SBill.Taylor@Sun.COM mr_attr.mr_vaddr = (uint64_t)(uintptr_t)buf;
6679517SBill.Taylor@Sun.COM mr_attr.mr_len = new_cqinfo.qa_size;
6689517SBill.Taylor@Sun.COM mr_attr.mr_as = NULL;
6699517SBill.Taylor@Sun.COM mr_attr.mr_flags = flag | IBT_MR_ENABLE_LOCAL_WRITE;
6709517SBill.Taylor@Sun.COM op.mro_bind_type = state->hs_cfg_profile->cp_iommu_bypass;
6719517SBill.Taylor@Sun.COM op.mro_bind_dmahdl = new_cqinfo.qa_dmahdl;
6729517SBill.Taylor@Sun.COM op.mro_bind_override_addr = 0;
6739517SBill.Taylor@Sun.COM status = hermon_mr_register(state, pd, &mr_attr, &mr, &op,
6749517SBill.Taylor@Sun.COM HERMON_CQ_CMPT);
6759517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
6769517SBill.Taylor@Sun.COM hermon_queue_free(&new_cqinfo);
6779517SBill.Taylor@Sun.COM /* free the resize handle */
6789517SBill.Taylor@Sun.COM kmem_free(resize_hdl, sizeof (struct hermon_sw_cq_s));
6799517SBill.Taylor@Sun.COM status = IBT_INSUFF_RESOURCE;
6809517SBill.Taylor@Sun.COM goto cqresize_fail;
6819517SBill.Taylor@Sun.COM }
6829517SBill.Taylor@Sun.COM _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
6839517SBill.Taylor@Sun.COM
6849517SBill.Taylor@Sun.COM /*
6859517SBill.Taylor@Sun.COM * Now we grab the CQ lock. Since we will be updating the actual
6869517SBill.Taylor@Sun.COM * CQ location and the producer/consumer indexes, we should hold
6879517SBill.Taylor@Sun.COM * the lock.
6889517SBill.Taylor@Sun.COM *
6899517SBill.Taylor@Sun.COM * We do a ARBEL_NOSLEEP here (and below), though, because we are
6909517SBill.Taylor@Sun.COM * holding the "cq_lock" and if we got raised to interrupt level
6919517SBill.Taylor@Sun.COM * by priority inversion, we would not want to block in this routine
6929517SBill.Taylor@Sun.COM * waiting for success.
6939517SBill.Taylor@Sun.COM */
6949517SBill.Taylor@Sun.COM mutex_enter(&cq->cq_lock);
6959517SBill.Taylor@Sun.COM
6969517SBill.Taylor@Sun.COM /*
6979517SBill.Taylor@Sun.COM * Fill in the CQC entry. For the resize operation this is the
6989517SBill.Taylor@Sun.COM * final step before attempting the resize operation on the CQC entry.
6999517SBill.Taylor@Sun.COM * We use all of the information collected/calculated above to fill
7009517SBill.Taylor@Sun.COM * in the requisite portions of the CQC.
7019517SBill.Taylor@Sun.COM */
7029517SBill.Taylor@Sun.COM bzero(&cqc_entry, sizeof (hermon_hw_cqc_t));
7039517SBill.Taylor@Sun.COM cqc_entry.log_cq_sz = log_cq_size;
7049517SBill.Taylor@Sun.COM cqc_entry.pg_offs = new_cqinfo.qa_pgoffs >> 5;
7059517SBill.Taylor@Sun.COM cqc_entry.log2_pgsz = mr->mr_log2_pgsz;
7069517SBill.Taylor@Sun.COM cqc_entry.mtt_base_addh = (uint32_t)((mr->mr_mttaddr >> 32) & 0xFF);
7079517SBill.Taylor@Sun.COM cqc_entry.mtt_base_addl = mr->mr_mttaddr >> 3;
7089517SBill.Taylor@Sun.COM
7099517SBill.Taylor@Sun.COM /*
7109517SBill.Taylor@Sun.COM * Write the CQC entry to hardware. Lastly, we pass ownership of
7119517SBill.Taylor@Sun.COM * the entry to the hardware (using the Hermon RESIZE_CQ firmware
7129517SBill.Taylor@Sun.COM * command). Note: In general, this operation shouldn't fail. But
7139517SBill.Taylor@Sun.COM * if it does, we have to undo everything we've done above before
7149517SBill.Taylor@Sun.COM * returning error. Also note that the status returned may indicate
7159517SBill.Taylor@Sun.COM * the code to return to the IBTF.
7169517SBill.Taylor@Sun.COM */
7179517SBill.Taylor@Sun.COM status = hermon_resize_cq_cmd_post(state, &cqc_entry, cq->cq_cqnum,
7189517SBill.Taylor@Sun.COM &new_prod_indx, HERMON_CMD_NOSLEEP_SPIN);
7199517SBill.Taylor@Sun.COM if (status != HERMON_CMD_SUCCESS) {
7209517SBill.Taylor@Sun.COM /* Resize attempt has failed, drop CQ lock and cleanup */
7219517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
7229517SBill.Taylor@Sun.COM if (hermon_mr_deregister(state, &mr, HERMON_MR_DEREG_ALL,
7239517SBill.Taylor@Sun.COM sleepflag) != DDI_SUCCESS) {
7249517SBill.Taylor@Sun.COM HERMON_WARNING(state, "failed to deregister CQ memory");
7259517SBill.Taylor@Sun.COM }
7269517SBill.Taylor@Sun.COM kmem_free(resize_hdl, sizeof (struct hermon_sw_cq_s));
7279517SBill.Taylor@Sun.COM hermon_queue_free(&new_cqinfo);
7289517SBill.Taylor@Sun.COM if (status == HERMON_CMD_BAD_SIZE) {
7299517SBill.Taylor@Sun.COM return (IBT_CQ_SZ_INSUFFICIENT);
7309517SBill.Taylor@Sun.COM } else {
7319517SBill.Taylor@Sun.COM cmn_err(CE_CONT, "Hermon: RESIZE_CQ command failed: "
7329517SBill.Taylor@Sun.COM "%08x\n", status);
7339517SBill.Taylor@Sun.COM if (status == HERMON_CMD_INVALID_STATUS) {
7349517SBill.Taylor@Sun.COM hermon_fm_ereport(state, HCA_SYS_ERR,
7359517SBill.Taylor@Sun.COM HCA_ERR_SRV_LOST);
7369517SBill.Taylor@Sun.COM }
7379517SBill.Taylor@Sun.COM return (ibc_get_ci_failure(0));
7389517SBill.Taylor@Sun.COM }
7399517SBill.Taylor@Sun.COM }
7409517SBill.Taylor@Sun.COM
7419517SBill.Taylor@Sun.COM /*
7429517SBill.Taylor@Sun.COM * For Hermon, we've alloc'd another handle structure and save off the
7439517SBill.Taylor@Sun.COM * important things in it. Then, in polling we check to see if there's
7449517SBill.Taylor@Sun.COM * a "resizing handle" and if so we look for the "special CQE", opcode
7459517SBill.Taylor@Sun.COM * 0x16, that indicates the transition to the new buffer.
7469517SBill.Taylor@Sun.COM *
7479517SBill.Taylor@Sun.COM * At that point, we'll adjust everything - including dereg and
7489517SBill.Taylor@Sun.COM * freeing of the original buffer, updating all the necessary fields
7499517SBill.Taylor@Sun.COM * in the cq_hdl, and setting up for the next cqe polling
7509517SBill.Taylor@Sun.COM */
7519517SBill.Taylor@Sun.COM
7529517SBill.Taylor@Sun.COM resize_hdl->cq_buf = buf;
7539517SBill.Taylor@Sun.COM resize_hdl->cq_bufsz = (1 << log_cq_size);
7549517SBill.Taylor@Sun.COM resize_hdl->cq_mrhdl = mr;
7559517SBill.Taylor@Sun.COM resize_hdl->cq_log_cqsz = log_cq_size;
7569517SBill.Taylor@Sun.COM
7579517SBill.Taylor@Sun.COM bcopy(&new_cqinfo, &(resize_hdl->cq_cqinfo),
7589517SBill.Taylor@Sun.COM sizeof (struct hermon_qalloc_info_s));
7599517SBill.Taylor@Sun.COM
7609517SBill.Taylor@Sun.COM /* now, save the address in the cq_handle */
7619517SBill.Taylor@Sun.COM cq->cq_resize_hdl = resize_hdl;
7629517SBill.Taylor@Sun.COM
7639517SBill.Taylor@Sun.COM /*
7649517SBill.Taylor@Sun.COM * Drop the CQ lock now.
7659517SBill.Taylor@Sun.COM */
7669517SBill.Taylor@Sun.COM
7679517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
7689517SBill.Taylor@Sun.COM /*
7699517SBill.Taylor@Sun.COM * Fill in the return arguments (if necessary). This includes the
7709517SBill.Taylor@Sun.COM * real new completion queue size.
7719517SBill.Taylor@Sun.COM */
7729517SBill.Taylor@Sun.COM if (actual_size != NULL) {
7739517SBill.Taylor@Sun.COM *actual_size = (1 << log_cq_size) - 1;
7749517SBill.Taylor@Sun.COM }
7759517SBill.Taylor@Sun.COM
7769517SBill.Taylor@Sun.COM return (DDI_SUCCESS);
7779517SBill.Taylor@Sun.COM
7789517SBill.Taylor@Sun.COM cqresize_fail:
7799517SBill.Taylor@Sun.COM return (status);
7809517SBill.Taylor@Sun.COM }
7819517SBill.Taylor@Sun.COM
7829517SBill.Taylor@Sun.COM
7839517SBill.Taylor@Sun.COM /*
7849517SBill.Taylor@Sun.COM * hermon_cq_modify()
7859517SBill.Taylor@Sun.COM * Context: Can be called base context.
7869517SBill.Taylor@Sun.COM */
7879517SBill.Taylor@Sun.COM /* ARGSUSED */
7889517SBill.Taylor@Sun.COM int
hermon_cq_modify(hermon_state_t * state,hermon_cqhdl_t cq,uint_t count,uint_t usec,ibt_cq_handler_id_t hid,uint_t sleepflag)7899517SBill.Taylor@Sun.COM hermon_cq_modify(hermon_state_t *state, hermon_cqhdl_t cq,
7909517SBill.Taylor@Sun.COM uint_t count, uint_t usec, ibt_cq_handler_id_t hid, uint_t sleepflag)
7919517SBill.Taylor@Sun.COM {
7929517SBill.Taylor@Sun.COM int status;
7939517SBill.Taylor@Sun.COM hermon_hw_cqc_t cqc_entry;
7949517SBill.Taylor@Sun.COM
7959517SBill.Taylor@Sun.COM mutex_enter(&cq->cq_lock);
7969517SBill.Taylor@Sun.COM if (count != cq->cq_intmod_count ||
7979517SBill.Taylor@Sun.COM usec != cq->cq_intmod_usec) {
7989517SBill.Taylor@Sun.COM bzero(&cqc_entry, sizeof (hermon_hw_cqc_t));
7999517SBill.Taylor@Sun.COM cqc_entry.cq_max_cnt = count;
8009517SBill.Taylor@Sun.COM cqc_entry.cq_period = usec;
8019517SBill.Taylor@Sun.COM status = hermon_modify_cq_cmd_post(state, &cqc_entry,
8029517SBill.Taylor@Sun.COM cq->cq_cqnum, MODIFY_MODERATION_CQ, sleepflag);
8039517SBill.Taylor@Sun.COM if (status != HERMON_CMD_SUCCESS) {
8049517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
805*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "Hermon: MODIFY_MODERATION_CQ "
806*12965SWilliam.Taylor@Oracle.COM "command failed: %08x\n", status);
8079517SBill.Taylor@Sun.COM if (status == HERMON_CMD_INVALID_STATUS) {
8089517SBill.Taylor@Sun.COM hermon_fm_ereport(state, HCA_SYS_ERR,
8099517SBill.Taylor@Sun.COM HCA_ERR_SRV_LOST);
8109517SBill.Taylor@Sun.COM }
8119517SBill.Taylor@Sun.COM return (ibc_get_ci_failure(0));
8129517SBill.Taylor@Sun.COM }
8139517SBill.Taylor@Sun.COM cq->cq_intmod_count = count;
8149517SBill.Taylor@Sun.COM cq->cq_intmod_usec = usec;
8159517SBill.Taylor@Sun.COM }
816*12965SWilliam.Taylor@Oracle.COM if (hid && (hid - 1 != cq->cq_eqnum)) {
817*12965SWilliam.Taylor@Oracle.COM bzero(&cqc_entry, sizeof (hermon_hw_cqc_t));
818*12965SWilliam.Taylor@Oracle.COM cqc_entry.c_eqn = HERMON_HID_TO_EQNUM(state, hid);
819*12965SWilliam.Taylor@Oracle.COM status = hermon_modify_cq_cmd_post(state, &cqc_entry,
820*12965SWilliam.Taylor@Oracle.COM cq->cq_cqnum, MODIFY_EQN, sleepflag);
821*12965SWilliam.Taylor@Oracle.COM if (status != HERMON_CMD_SUCCESS) {
822*12965SWilliam.Taylor@Oracle.COM mutex_exit(&cq->cq_lock);
823*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "Hermon: MODIFY_EQN command failed: "
824*12965SWilliam.Taylor@Oracle.COM "%08x\n", status);
825*12965SWilliam.Taylor@Oracle.COM if (status == HERMON_CMD_INVALID_STATUS) {
826*12965SWilliam.Taylor@Oracle.COM hermon_fm_ereport(state, HCA_SYS_ERR,
827*12965SWilliam.Taylor@Oracle.COM HCA_ERR_SRV_LOST);
828*12965SWilliam.Taylor@Oracle.COM }
829*12965SWilliam.Taylor@Oracle.COM return (ibc_get_ci_failure(0));
830*12965SWilliam.Taylor@Oracle.COM }
831*12965SWilliam.Taylor@Oracle.COM cq->cq_eqnum = hid - 1;
832*12965SWilliam.Taylor@Oracle.COM }
8339517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
8349517SBill.Taylor@Sun.COM return (DDI_SUCCESS);
8359517SBill.Taylor@Sun.COM }
8369517SBill.Taylor@Sun.COM
8379517SBill.Taylor@Sun.COM /*
8389517SBill.Taylor@Sun.COM * hermon_cq_notify()
8399517SBill.Taylor@Sun.COM * Context: Can be called from interrupt or base context.
8409517SBill.Taylor@Sun.COM */
8419517SBill.Taylor@Sun.COM int
hermon_cq_notify(hermon_state_t * state,hermon_cqhdl_t cq,ibt_cq_notify_flags_t flags)8429517SBill.Taylor@Sun.COM hermon_cq_notify(hermon_state_t *state, hermon_cqhdl_t cq,
8439517SBill.Taylor@Sun.COM ibt_cq_notify_flags_t flags)
8449517SBill.Taylor@Sun.COM {
8459517SBill.Taylor@Sun.COM uint_t cmd;
8469517SBill.Taylor@Sun.COM ibt_status_t status;
8479517SBill.Taylor@Sun.COM
8489517SBill.Taylor@Sun.COM /* Validate IBT flags and call doorbell routine. */
8499517SBill.Taylor@Sun.COM if (flags == IBT_NEXT_COMPLETION) {
8509517SBill.Taylor@Sun.COM cmd = HERMON_CQDB_NOTIFY_CQ;
8519517SBill.Taylor@Sun.COM } else if (flags == IBT_NEXT_SOLICITED) {
8529517SBill.Taylor@Sun.COM cmd = HERMON_CQDB_NOTIFY_CQ_SOLICIT;
8539517SBill.Taylor@Sun.COM } else {
8549517SBill.Taylor@Sun.COM return (IBT_CQ_NOTIFY_TYPE_INVALID);
8559517SBill.Taylor@Sun.COM }
8569517SBill.Taylor@Sun.COM
8579517SBill.Taylor@Sun.COM status = hermon_cq_arm_doorbell(state, cq, cmd);
8589517SBill.Taylor@Sun.COM return (status);
8599517SBill.Taylor@Sun.COM }
8609517SBill.Taylor@Sun.COM
8619517SBill.Taylor@Sun.COM
8629517SBill.Taylor@Sun.COM /*
8639517SBill.Taylor@Sun.COM * hermon_cq_poll()
8649517SBill.Taylor@Sun.COM * Context: Can be called from interrupt or base context.
8659517SBill.Taylor@Sun.COM */
8669517SBill.Taylor@Sun.COM int
hermon_cq_poll(hermon_state_t * state,hermon_cqhdl_t cq,ibt_wc_t * wc_p,uint_t num_wc,uint_t * num_polled)8679517SBill.Taylor@Sun.COM hermon_cq_poll(hermon_state_t *state, hermon_cqhdl_t cq, ibt_wc_t *wc_p,
8689517SBill.Taylor@Sun.COM uint_t num_wc, uint_t *num_polled)
8699517SBill.Taylor@Sun.COM {
8709517SBill.Taylor@Sun.COM hermon_hw_cqe_t *cqe;
8719517SBill.Taylor@Sun.COM uint_t opcode;
872*12965SWilliam.Taylor@Oracle.COM uint32_t cons_indx, wrap_around_mask, shift, mask;
8739517SBill.Taylor@Sun.COM uint32_t polled_cnt, spec_op = 0;
8749517SBill.Taylor@Sun.COM int status;
8759517SBill.Taylor@Sun.COM
8769517SBill.Taylor@Sun.COM /*
8779517SBill.Taylor@Sun.COM * Check for user-mappable CQ memory. Note: We do not allow kernel
8789517SBill.Taylor@Sun.COM * clients to poll CQ memory that is accessible directly by the user.
8799517SBill.Taylor@Sun.COM * If the CQ memory is user accessible, then return an error.
8809517SBill.Taylor@Sun.COM */
8819517SBill.Taylor@Sun.COM if (cq->cq_is_umap) {
8829517SBill.Taylor@Sun.COM return (IBT_CQ_HDL_INVALID);
8839517SBill.Taylor@Sun.COM }
8849517SBill.Taylor@Sun.COM
8859517SBill.Taylor@Sun.COM mutex_enter(&cq->cq_lock);
8869517SBill.Taylor@Sun.COM
8879517SBill.Taylor@Sun.COM /* Get the consumer index */
8889517SBill.Taylor@Sun.COM cons_indx = cq->cq_consindx;
889*12965SWilliam.Taylor@Oracle.COM shift = cq->cq_log_cqsz;
890*12965SWilliam.Taylor@Oracle.COM mask = cq->cq_bufsz;
8919517SBill.Taylor@Sun.COM
8929517SBill.Taylor@Sun.COM /*
8939517SBill.Taylor@Sun.COM * Calculate the wrap around mask. Note: This operation only works
8949517SBill.Taylor@Sun.COM * because all Hermon completion queues have power-of-2 sizes
8959517SBill.Taylor@Sun.COM */
8969517SBill.Taylor@Sun.COM wrap_around_mask = (cq->cq_bufsz - 1);
8979517SBill.Taylor@Sun.COM
8989517SBill.Taylor@Sun.COM /* Calculate the pointer to the first CQ entry */
8999517SBill.Taylor@Sun.COM cqe = &cq->cq_buf[cons_indx & wrap_around_mask];
9009517SBill.Taylor@Sun.COM
9019517SBill.Taylor@Sun.COM /*
9029517SBill.Taylor@Sun.COM * Keep pulling entries from the CQ until we find an entry owned by
9039517SBill.Taylor@Sun.COM * the hardware. As long as there the CQE's owned by SW, process
9049517SBill.Taylor@Sun.COM * each entry by calling hermon_cq_cqe_consume() and updating the CQ
9059517SBill.Taylor@Sun.COM * consumer index. Note: We only update the consumer index if
9069517SBill.Taylor@Sun.COM * hermon_cq_cqe_consume() returns HERMON_CQ_SYNC_AND_DB. Otherwise,
9079517SBill.Taylor@Sun.COM * it indicates that we are going to "recycle" the CQE (probably
9089517SBill.Taylor@Sun.COM * because it is a error CQE and corresponds to more than one
9099517SBill.Taylor@Sun.COM * completion).
9109517SBill.Taylor@Sun.COM */
9119517SBill.Taylor@Sun.COM polled_cnt = 0;
912*12965SWilliam.Taylor@Oracle.COM while (HERMON_CQE_OWNER_IS_SW(cq, cqe, cons_indx, shift, mask)) {
9139517SBill.Taylor@Sun.COM if (cq->cq_resize_hdl != 0) { /* in midst of resize */
9149517SBill.Taylor@Sun.COM /* peek at the opcode */
9159517SBill.Taylor@Sun.COM opcode = HERMON_CQE_OPCODE_GET(cq, cqe);
9169517SBill.Taylor@Sun.COM if (opcode == HERMON_CQE_RCV_RESIZE_CODE) {
9179517SBill.Taylor@Sun.COM hermon_cq_resize_helper(state, cq);
9189517SBill.Taylor@Sun.COM
9199517SBill.Taylor@Sun.COM /* Increment the consumer index */
9209517SBill.Taylor@Sun.COM cons_indx = (cons_indx + 1);
9219517SBill.Taylor@Sun.COM spec_op = 1; /* plus one for the limiting CQE */
9229517SBill.Taylor@Sun.COM
9239517SBill.Taylor@Sun.COM wrap_around_mask = (cq->cq_bufsz - 1);
9249517SBill.Taylor@Sun.COM
9259517SBill.Taylor@Sun.COM /* Update the pointer to the next CQ entry */
9269517SBill.Taylor@Sun.COM cqe = &cq->cq_buf[cons_indx & wrap_around_mask];
9279517SBill.Taylor@Sun.COM
9289517SBill.Taylor@Sun.COM continue;
9299517SBill.Taylor@Sun.COM }
9309517SBill.Taylor@Sun.COM } /* in resizing CQ */
9319517SBill.Taylor@Sun.COM
9329517SBill.Taylor@Sun.COM /*
9339517SBill.Taylor@Sun.COM * either resizing and not the special opcode, or
9349517SBill.Taylor@Sun.COM * not resizing at all
9359517SBill.Taylor@Sun.COM */
9369517SBill.Taylor@Sun.COM hermon_cq_cqe_consume(state, cq, cqe, &wc_p[polled_cnt++]);
9379517SBill.Taylor@Sun.COM
9389517SBill.Taylor@Sun.COM /* Increment the consumer index */
9399517SBill.Taylor@Sun.COM cons_indx = (cons_indx + 1);
9409517SBill.Taylor@Sun.COM
9419517SBill.Taylor@Sun.COM /* Update the pointer to the next CQ entry */
9429517SBill.Taylor@Sun.COM cqe = &cq->cq_buf[cons_indx & wrap_around_mask];
9439517SBill.Taylor@Sun.COM
9449517SBill.Taylor@Sun.COM /*
9459517SBill.Taylor@Sun.COM * If we have run out of space to store work completions,
9469517SBill.Taylor@Sun.COM * then stop and return the ones we have pulled of the CQ.
9479517SBill.Taylor@Sun.COM */
9489517SBill.Taylor@Sun.COM if (polled_cnt >= num_wc) {
9499517SBill.Taylor@Sun.COM break;
9509517SBill.Taylor@Sun.COM }
9519517SBill.Taylor@Sun.COM }
9529517SBill.Taylor@Sun.COM
9539517SBill.Taylor@Sun.COM /*
9549517SBill.Taylor@Sun.COM * Now we only ring the doorbell (to update the consumer index) if
955*12965SWilliam.Taylor@Oracle.COM * we've actually consumed a CQ entry.
9569517SBill.Taylor@Sun.COM */
9579517SBill.Taylor@Sun.COM if ((polled_cnt != 0) && (cq->cq_consindx != cons_indx)) {
9589517SBill.Taylor@Sun.COM /*
9599517SBill.Taylor@Sun.COM * Update the consumer index in both the CQ handle and the
9609517SBill.Taylor@Sun.COM * doorbell record.
9619517SBill.Taylor@Sun.COM */
9629517SBill.Taylor@Sun.COM cq->cq_consindx = cons_indx;
9639517SBill.Taylor@Sun.COM hermon_cq_update_ci_doorbell(cq);
9649517SBill.Taylor@Sun.COM
9659517SBill.Taylor@Sun.COM } else if (polled_cnt == 0) {
9669517SBill.Taylor@Sun.COM if (spec_op != 0) {
9679517SBill.Taylor@Sun.COM /* if we got the special opcode, update the consindx */
9689517SBill.Taylor@Sun.COM cq->cq_consindx = cons_indx;
9699517SBill.Taylor@Sun.COM hermon_cq_update_ci_doorbell(cq);
9709517SBill.Taylor@Sun.COM }
9719517SBill.Taylor@Sun.COM }
9729517SBill.Taylor@Sun.COM
9739517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
9749517SBill.Taylor@Sun.COM
9759517SBill.Taylor@Sun.COM /* Set "num_polled" (if necessary) */
9769517SBill.Taylor@Sun.COM if (num_polled != NULL) {
9779517SBill.Taylor@Sun.COM *num_polled = polled_cnt;
9789517SBill.Taylor@Sun.COM }
9799517SBill.Taylor@Sun.COM
9809517SBill.Taylor@Sun.COM /* Set CQ_EMPTY condition if needed, otherwise return success */
9819517SBill.Taylor@Sun.COM if (polled_cnt == 0) {
9829517SBill.Taylor@Sun.COM status = IBT_CQ_EMPTY;
9839517SBill.Taylor@Sun.COM } else {
9849517SBill.Taylor@Sun.COM status = DDI_SUCCESS;
9859517SBill.Taylor@Sun.COM }
9869517SBill.Taylor@Sun.COM
9879517SBill.Taylor@Sun.COM /*
9889517SBill.Taylor@Sun.COM * Check if the system is currently panicking. If it is, then call
9899517SBill.Taylor@Sun.COM * the Hermon interrupt service routine. This step is necessary here
9909517SBill.Taylor@Sun.COM * because we might be in a polled I/O mode and without the call to
9919517SBill.Taylor@Sun.COM * hermon_isr() - and its subsequent calls to poll and rearm each
9929517SBill.Taylor@Sun.COM * event queue - we might overflow our EQs and render the system
9939517SBill.Taylor@Sun.COM * unable to sync/dump.
9949517SBill.Taylor@Sun.COM */
9959517SBill.Taylor@Sun.COM if (ddi_in_panic() != 0) {
9969517SBill.Taylor@Sun.COM (void) hermon_isr((caddr_t)state, (caddr_t)NULL);
9979517SBill.Taylor@Sun.COM }
9989517SBill.Taylor@Sun.COM return (status);
9999517SBill.Taylor@Sun.COM }
10009517SBill.Taylor@Sun.COM
10019517SBill.Taylor@Sun.COM /*
10029517SBill.Taylor@Sun.COM * cmd_sn must be initialized to 1 to enable proper reenabling
10039517SBill.Taylor@Sun.COM * by hermon_arm_cq_dbr_update().
10049517SBill.Taylor@Sun.COM */
10059517SBill.Taylor@Sun.COM static void
hermon_arm_cq_dbr_init(hermon_dbr_t * cq_arm_dbr)10069517SBill.Taylor@Sun.COM hermon_arm_cq_dbr_init(hermon_dbr_t *cq_arm_dbr)
10079517SBill.Taylor@Sun.COM {
10089517SBill.Taylor@Sun.COM uint32_t *target;
10099517SBill.Taylor@Sun.COM
10109517SBill.Taylor@Sun.COM target = (uint32_t *)cq_arm_dbr + 1;
10119517SBill.Taylor@Sun.COM *target = htonl(1 << HERMON_CQDB_CMDSN_SHIFT);
10129517SBill.Taylor@Sun.COM }
10139517SBill.Taylor@Sun.COM
10149517SBill.Taylor@Sun.COM
10159517SBill.Taylor@Sun.COM /*
10169517SBill.Taylor@Sun.COM * User cmd_sn needs help from this kernel function to know
10179517SBill.Taylor@Sun.COM * when it should be incremented (modulo 4). We do an atomic
10189517SBill.Taylor@Sun.COM * update of the arm_cq dbr to communicate this fact. We retry
10199517SBill.Taylor@Sun.COM * in the case that user library is racing with us. We zero
10209517SBill.Taylor@Sun.COM * out the cmd field so that the user library can use the cmd
10219517SBill.Taylor@Sun.COM * field to track the last command it issued (solicited verses any).
10229517SBill.Taylor@Sun.COM */
10239517SBill.Taylor@Sun.COM static void
hermon_arm_cq_dbr_update(hermon_dbr_t * cq_arm_dbr)10249517SBill.Taylor@Sun.COM hermon_arm_cq_dbr_update(hermon_dbr_t *cq_arm_dbr)
10259517SBill.Taylor@Sun.COM {
10269517SBill.Taylor@Sun.COM uint32_t tmp, cmp, new;
10279517SBill.Taylor@Sun.COM uint32_t old_cmd_sn, new_cmd_sn;
10289517SBill.Taylor@Sun.COM uint32_t *target;
10299517SBill.Taylor@Sun.COM int retries = 0;
10309517SBill.Taylor@Sun.COM
10319517SBill.Taylor@Sun.COM target = (uint32_t *)cq_arm_dbr + 1;
10329517SBill.Taylor@Sun.COM retry:
10339517SBill.Taylor@Sun.COM cmp = *target;
10349517SBill.Taylor@Sun.COM tmp = htonl(cmp);
10359517SBill.Taylor@Sun.COM old_cmd_sn = tmp & (0x3 << HERMON_CQDB_CMDSN_SHIFT);
10369517SBill.Taylor@Sun.COM new_cmd_sn = (old_cmd_sn + (0x1 << HERMON_CQDB_CMDSN_SHIFT)) &
10379517SBill.Taylor@Sun.COM (0x3 << HERMON_CQDB_CMDSN_SHIFT);
10389517SBill.Taylor@Sun.COM new = htonl((tmp & ~(0x37 << HERMON_CQDB_CMD_SHIFT)) | new_cmd_sn);
10399517SBill.Taylor@Sun.COM tmp = atomic_cas_32(target, cmp, new);
10409517SBill.Taylor@Sun.COM if (tmp != cmp) { /* cas failed, so need to retry */
10419517SBill.Taylor@Sun.COM drv_usecwait(retries & 0xff); /* avoid race */
10429517SBill.Taylor@Sun.COM if (++retries > 100000) {
10439517SBill.Taylor@Sun.COM cmn_err(CE_CONT, "cas failed in hermon\n");
10449517SBill.Taylor@Sun.COM retries = 0;
10459517SBill.Taylor@Sun.COM }
10469517SBill.Taylor@Sun.COM goto retry;
10479517SBill.Taylor@Sun.COM }
10489517SBill.Taylor@Sun.COM }
10499517SBill.Taylor@Sun.COM
10509517SBill.Taylor@Sun.COM
10519517SBill.Taylor@Sun.COM /*
10529517SBill.Taylor@Sun.COM * hermon_cq_handler()
10539517SBill.Taylor@Sun.COM * Context: Only called from interrupt context
10549517SBill.Taylor@Sun.COM */
1055*12965SWilliam.Taylor@Oracle.COM /* ARGSUSED */
10569517SBill.Taylor@Sun.COM int
hermon_cq_handler(hermon_state_t * state,hermon_eqhdl_t eq,hermon_hw_eqe_t * eqe)10579517SBill.Taylor@Sun.COM hermon_cq_handler(hermon_state_t *state, hermon_eqhdl_t eq,
10589517SBill.Taylor@Sun.COM hermon_hw_eqe_t *eqe)
10599517SBill.Taylor@Sun.COM {
10609517SBill.Taylor@Sun.COM hermon_cqhdl_t cq;
10619517SBill.Taylor@Sun.COM uint_t cqnum;
10629517SBill.Taylor@Sun.COM
10639517SBill.Taylor@Sun.COM /* Get the CQ handle from CQ number in event descriptor */
10649517SBill.Taylor@Sun.COM cqnum = HERMON_EQE_CQNUM_GET(eq, eqe);
10659517SBill.Taylor@Sun.COM cq = hermon_cqhdl_from_cqnum(state, cqnum);
10669517SBill.Taylor@Sun.COM
10679517SBill.Taylor@Sun.COM /*
10689517SBill.Taylor@Sun.COM * If the CQ handle is NULL, this is probably an indication
10699517SBill.Taylor@Sun.COM * that the CQ has been freed already. In which case, we
10709517SBill.Taylor@Sun.COM * should not deliver this event.
10719517SBill.Taylor@Sun.COM *
10729517SBill.Taylor@Sun.COM * We also check that the CQ number in the handle is the
10739517SBill.Taylor@Sun.COM * same as the CQ number in the event queue entry. This
10749517SBill.Taylor@Sun.COM * extra check allows us to handle the case where a CQ was
10759517SBill.Taylor@Sun.COM * freed and then allocated again in the time it took to
10769517SBill.Taylor@Sun.COM * handle the event queue processing. By constantly incrementing
10779517SBill.Taylor@Sun.COM * the non-constrained portion of the CQ number every time
10789517SBill.Taylor@Sun.COM * a new CQ is allocated, we mitigate (somewhat) the chance
10799517SBill.Taylor@Sun.COM * that a stale event could be passed to the client's CQ
10809517SBill.Taylor@Sun.COM * handler.
10819517SBill.Taylor@Sun.COM *
10829517SBill.Taylor@Sun.COM * Lastly, we check if "hs_ibtfpriv" is NULL. If it is then it
10839517SBill.Taylor@Sun.COM * means that we've have either received this event before we
10849517SBill.Taylor@Sun.COM * finished attaching to the IBTF or we've received it while we
10859517SBill.Taylor@Sun.COM * are in the process of detaching.
10869517SBill.Taylor@Sun.COM */
10879517SBill.Taylor@Sun.COM if ((cq != NULL) && (cq->cq_cqnum == cqnum) &&
10889517SBill.Taylor@Sun.COM (state->hs_ibtfpriv != NULL)) {
10899517SBill.Taylor@Sun.COM hermon_arm_cq_dbr_update(cq->cq_arm_ci_vdbr);
10909517SBill.Taylor@Sun.COM HERMON_DO_IBTF_CQ_CALLB(state, cq);
10919517SBill.Taylor@Sun.COM }
10929517SBill.Taylor@Sun.COM
10939517SBill.Taylor@Sun.COM return (DDI_SUCCESS);
10949517SBill.Taylor@Sun.COM }
10959517SBill.Taylor@Sun.COM
10969517SBill.Taylor@Sun.COM
10979517SBill.Taylor@Sun.COM /*
10989517SBill.Taylor@Sun.COM * hermon_cq_err_handler()
10999517SBill.Taylor@Sun.COM * Context: Only called from interrupt context
11009517SBill.Taylor@Sun.COM */
1101*12965SWilliam.Taylor@Oracle.COM /* ARGSUSED */
11029517SBill.Taylor@Sun.COM int
hermon_cq_err_handler(hermon_state_t * state,hermon_eqhdl_t eq,hermon_hw_eqe_t * eqe)11039517SBill.Taylor@Sun.COM hermon_cq_err_handler(hermon_state_t *state, hermon_eqhdl_t eq,
11049517SBill.Taylor@Sun.COM hermon_hw_eqe_t *eqe)
11059517SBill.Taylor@Sun.COM {
11069517SBill.Taylor@Sun.COM hermon_cqhdl_t cq;
11079517SBill.Taylor@Sun.COM uint_t cqnum;
11089517SBill.Taylor@Sun.COM ibc_async_event_t event;
11099517SBill.Taylor@Sun.COM ibt_async_code_t type;
11109517SBill.Taylor@Sun.COM
11119517SBill.Taylor@Sun.COM HERMON_FMANOTE(state, HERMON_FMA_OVERRUN);
11129517SBill.Taylor@Sun.COM /* Get the CQ handle from CQ number in event descriptor */
11139517SBill.Taylor@Sun.COM cqnum = HERMON_EQE_CQNUM_GET(eq, eqe);
11149517SBill.Taylor@Sun.COM cq = hermon_cqhdl_from_cqnum(state, cqnum);
11159517SBill.Taylor@Sun.COM
11169517SBill.Taylor@Sun.COM /*
11179517SBill.Taylor@Sun.COM * If the CQ handle is NULL, this is probably an indication
11189517SBill.Taylor@Sun.COM * that the CQ has been freed already. In which case, we
11199517SBill.Taylor@Sun.COM * should not deliver this event.
11209517SBill.Taylor@Sun.COM *
11219517SBill.Taylor@Sun.COM * We also check that the CQ number in the handle is the
11229517SBill.Taylor@Sun.COM * same as the CQ number in the event queue entry. This
11239517SBill.Taylor@Sun.COM * extra check allows us to handle the case where a CQ was
11249517SBill.Taylor@Sun.COM * freed and then allocated again in the time it took to
11259517SBill.Taylor@Sun.COM * handle the event queue processing. By constantly incrementing
11269517SBill.Taylor@Sun.COM * the non-constrained portion of the CQ number every time
11279517SBill.Taylor@Sun.COM * a new CQ is allocated, we mitigate (somewhat) the chance
11289517SBill.Taylor@Sun.COM * that a stale event could be passed to the client's CQ
11299517SBill.Taylor@Sun.COM * handler.
11309517SBill.Taylor@Sun.COM *
11319517SBill.Taylor@Sun.COM * And then we check if "hs_ibtfpriv" is NULL. If it is then it
11329517SBill.Taylor@Sun.COM * means that we've have either received this event before we
11339517SBill.Taylor@Sun.COM * finished attaching to the IBTF or we've received it while we
11349517SBill.Taylor@Sun.COM * are in the process of detaching.
11359517SBill.Taylor@Sun.COM */
11369517SBill.Taylor@Sun.COM if ((cq != NULL) && (cq->cq_cqnum == cqnum) &&
11379517SBill.Taylor@Sun.COM (state->hs_ibtfpriv != NULL)) {
11389517SBill.Taylor@Sun.COM event.ev_cq_hdl = (ibt_cq_hdl_t)cq->cq_hdlrarg;
11399517SBill.Taylor@Sun.COM type = IBT_ERROR_CQ;
11409517SBill.Taylor@Sun.COM HERMON_DO_IBTF_ASYNC_CALLB(state, type, &event);
11419517SBill.Taylor@Sun.COM }
11429517SBill.Taylor@Sun.COM
11439517SBill.Taylor@Sun.COM return (DDI_SUCCESS);
11449517SBill.Taylor@Sun.COM }
11459517SBill.Taylor@Sun.COM
11469517SBill.Taylor@Sun.COM
11479517SBill.Taylor@Sun.COM /*
11489517SBill.Taylor@Sun.COM * hermon_cq_refcnt_inc()
11499517SBill.Taylor@Sun.COM * Context: Can be called from interrupt or base context.
11509517SBill.Taylor@Sun.COM */
11519517SBill.Taylor@Sun.COM int
hermon_cq_refcnt_inc(hermon_cqhdl_t cq,uint_t is_special)11529517SBill.Taylor@Sun.COM hermon_cq_refcnt_inc(hermon_cqhdl_t cq, uint_t is_special)
11539517SBill.Taylor@Sun.COM {
11549517SBill.Taylor@Sun.COM /*
11559517SBill.Taylor@Sun.COM * Increment the completion queue's reference count. Note: In order
11569517SBill.Taylor@Sun.COM * to ensure compliance with IBA C11-15, we must ensure that a given
11579517SBill.Taylor@Sun.COM * CQ is not used for both special (SMI/GSI) QP and non-special QP.
11589517SBill.Taylor@Sun.COM * This is accomplished here by keeping track of how the referenced
11599517SBill.Taylor@Sun.COM * CQ is being used.
11609517SBill.Taylor@Sun.COM */
11619517SBill.Taylor@Sun.COM mutex_enter(&cq->cq_lock);
11629517SBill.Taylor@Sun.COM if (cq->cq_refcnt == 0) {
11639517SBill.Taylor@Sun.COM cq->cq_is_special = is_special;
11649517SBill.Taylor@Sun.COM } else {
11659517SBill.Taylor@Sun.COM if (cq->cq_is_special != is_special) {
11669517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
11679517SBill.Taylor@Sun.COM return (DDI_FAILURE);
11689517SBill.Taylor@Sun.COM }
11699517SBill.Taylor@Sun.COM }
11709517SBill.Taylor@Sun.COM cq->cq_refcnt++;
11719517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
11729517SBill.Taylor@Sun.COM return (DDI_SUCCESS);
11739517SBill.Taylor@Sun.COM }
11749517SBill.Taylor@Sun.COM
11759517SBill.Taylor@Sun.COM
11769517SBill.Taylor@Sun.COM /*
11779517SBill.Taylor@Sun.COM * hermon_cq_refcnt_dec()
11789517SBill.Taylor@Sun.COM * Context: Can be called from interrupt or base context.
11799517SBill.Taylor@Sun.COM */
11809517SBill.Taylor@Sun.COM void
hermon_cq_refcnt_dec(hermon_cqhdl_t cq)11819517SBill.Taylor@Sun.COM hermon_cq_refcnt_dec(hermon_cqhdl_t cq)
11829517SBill.Taylor@Sun.COM {
11839517SBill.Taylor@Sun.COM /* Decrement the completion queue's reference count */
11849517SBill.Taylor@Sun.COM mutex_enter(&cq->cq_lock);
11859517SBill.Taylor@Sun.COM cq->cq_refcnt--;
11869517SBill.Taylor@Sun.COM mutex_exit(&cq->cq_lock);
11879517SBill.Taylor@Sun.COM }
11889517SBill.Taylor@Sun.COM
11899517SBill.Taylor@Sun.COM
11909517SBill.Taylor@Sun.COM /*
11919517SBill.Taylor@Sun.COM * hermon_cq_arm_doorbell()
11929517SBill.Taylor@Sun.COM * Context: Can be called from interrupt or base context.
11939517SBill.Taylor@Sun.COM */
11949517SBill.Taylor@Sun.COM static int
hermon_cq_arm_doorbell(hermon_state_t * state,hermon_cqhdl_t cq,uint_t cq_cmd)11959517SBill.Taylor@Sun.COM hermon_cq_arm_doorbell(hermon_state_t *state, hermon_cqhdl_t cq, uint_t cq_cmd)
11969517SBill.Taylor@Sun.COM {
11979517SBill.Taylor@Sun.COM uint32_t cq_num;
11989517SBill.Taylor@Sun.COM uint32_t *target;
11999517SBill.Taylor@Sun.COM uint32_t old_cmd, cmp, new, tmp, cmd_sn;
12009517SBill.Taylor@Sun.COM ddi_acc_handle_t uarhdl = hermon_get_uarhdl(state);
12019517SBill.Taylor@Sun.COM
12029517SBill.Taylor@Sun.COM /* initialize the FMA retry loop */
12039517SBill.Taylor@Sun.COM hermon_pio_init(fm_loop_cnt, fm_status, fm_test_num);
12049517SBill.Taylor@Sun.COM
12059517SBill.Taylor@Sun.COM cq_num = cq->cq_cqnum;
12069517SBill.Taylor@Sun.COM target = (uint32_t *)cq->cq_arm_ci_vdbr + 1;
12079517SBill.Taylor@Sun.COM
12089517SBill.Taylor@Sun.COM /* the FMA retry loop starts for Hermon doorbell register. */
12099517SBill.Taylor@Sun.COM hermon_pio_start(state, uarhdl, pio_error, fm_loop_cnt, fm_status,
12109517SBill.Taylor@Sun.COM fm_test_num);
12119517SBill.Taylor@Sun.COM retry:
12129517SBill.Taylor@Sun.COM cmp = *target;
12139517SBill.Taylor@Sun.COM tmp = htonl(cmp);
12149517SBill.Taylor@Sun.COM old_cmd = tmp & (0x7 << HERMON_CQDB_CMD_SHIFT);
12159517SBill.Taylor@Sun.COM cmd_sn = tmp & (0x3 << HERMON_CQDB_CMDSN_SHIFT);
12169517SBill.Taylor@Sun.COM if (cq_cmd == HERMON_CQDB_NOTIFY_CQ) {
12179517SBill.Taylor@Sun.COM if (old_cmd != HERMON_CQDB_NOTIFY_CQ) {
12189517SBill.Taylor@Sun.COM cmd_sn |= (HERMON_CQDB_NOTIFY_CQ <<
12199517SBill.Taylor@Sun.COM HERMON_CQDB_CMD_SHIFT);
12209517SBill.Taylor@Sun.COM new = htonl(cmd_sn | (cq->cq_consindx & 0xFFFFFF));
12219517SBill.Taylor@Sun.COM tmp = atomic_cas_32(target, cmp, new);
12229517SBill.Taylor@Sun.COM if (tmp != cmp)
12239517SBill.Taylor@Sun.COM goto retry;
12249517SBill.Taylor@Sun.COM HERMON_UAR_DOORBELL(state, uarhdl, (uint64_t *)(void *)
12259517SBill.Taylor@Sun.COM &state->hs_uar->cq, (((uint64_t)cmd_sn | cq_num) <<
12269517SBill.Taylor@Sun.COM 32) | (cq->cq_consindx & 0xFFFFFF));
12279517SBill.Taylor@Sun.COM } /* else it's already armed */
12289517SBill.Taylor@Sun.COM } else {
12299517SBill.Taylor@Sun.COM ASSERT(cq_cmd == HERMON_CQDB_NOTIFY_CQ_SOLICIT);
12309517SBill.Taylor@Sun.COM if (old_cmd != HERMON_CQDB_NOTIFY_CQ &&
12319517SBill.Taylor@Sun.COM old_cmd != HERMON_CQDB_NOTIFY_CQ_SOLICIT) {
12329517SBill.Taylor@Sun.COM cmd_sn |= (HERMON_CQDB_NOTIFY_CQ_SOLICIT <<
12339517SBill.Taylor@Sun.COM HERMON_CQDB_CMD_SHIFT);
12349517SBill.Taylor@Sun.COM new = htonl(cmd_sn | (cq->cq_consindx & 0xFFFFFF));
12359517SBill.Taylor@Sun.COM tmp = atomic_cas_32(target, cmp, new);
12369517SBill.Taylor@Sun.COM if (tmp != cmp)
12379517SBill.Taylor@Sun.COM goto retry;
12389517SBill.Taylor@Sun.COM HERMON_UAR_DOORBELL(state, uarhdl, (uint64_t *)(void *)
12399517SBill.Taylor@Sun.COM &state->hs_uar->cq, (((uint64_t)cmd_sn | cq_num) <<
12409517SBill.Taylor@Sun.COM 32) | (cq->cq_consindx & 0xFFFFFF));
12419517SBill.Taylor@Sun.COM } /* else it's already armed */
12429517SBill.Taylor@Sun.COM }
12439517SBill.Taylor@Sun.COM
12449517SBill.Taylor@Sun.COM /* the FMA retry loop ends. */
12459517SBill.Taylor@Sun.COM hermon_pio_end(state, uarhdl, pio_error, fm_loop_cnt, fm_status,
12469517SBill.Taylor@Sun.COM fm_test_num);
12479517SBill.Taylor@Sun.COM
12489517SBill.Taylor@Sun.COM return (IBT_SUCCESS);
12499517SBill.Taylor@Sun.COM
12509517SBill.Taylor@Sun.COM pio_error:
12519517SBill.Taylor@Sun.COM hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
12529517SBill.Taylor@Sun.COM return (ibc_get_ci_failure(0));
12539517SBill.Taylor@Sun.COM }
12549517SBill.Taylor@Sun.COM
12559517SBill.Taylor@Sun.COM
12569517SBill.Taylor@Sun.COM /*
12579517SBill.Taylor@Sun.COM * hermon_cqhdl_from_cqnum()
12589517SBill.Taylor@Sun.COM * Context: Can be called from interrupt or base context.
12599517SBill.Taylor@Sun.COM *
12609517SBill.Taylor@Sun.COM * This routine is important because changing the unconstrained
12619517SBill.Taylor@Sun.COM * portion of the CQ number is critical to the detection of a
12629517SBill.Taylor@Sun.COM * potential race condition in the CQ handler code (i.e. the case
12639517SBill.Taylor@Sun.COM * where a CQ is freed and alloc'd again before an event for the
12649517SBill.Taylor@Sun.COM * "old" CQ can be handled).
12659517SBill.Taylor@Sun.COM *
12669517SBill.Taylor@Sun.COM * While this is not a perfect solution (not sure that one exists)
12679517SBill.Taylor@Sun.COM * it does help to mitigate the chance that this race condition will
12689517SBill.Taylor@Sun.COM * cause us to deliver a "stale" event to the new CQ owner. Note:
12699517SBill.Taylor@Sun.COM * this solution does not scale well because the number of constrained
12709517SBill.Taylor@Sun.COM * bits increases (and, hence, the number of unconstrained bits
12719517SBill.Taylor@Sun.COM * decreases) as the number of supported CQs grows. For small and
12729517SBill.Taylor@Sun.COM * intermediate values, it should hopefully provide sufficient
12739517SBill.Taylor@Sun.COM * protection.
12749517SBill.Taylor@Sun.COM */
12759517SBill.Taylor@Sun.COM hermon_cqhdl_t
hermon_cqhdl_from_cqnum(hermon_state_t * state,uint_t cqnum)12769517SBill.Taylor@Sun.COM hermon_cqhdl_from_cqnum(hermon_state_t *state, uint_t cqnum)
12779517SBill.Taylor@Sun.COM {
12789517SBill.Taylor@Sun.COM uint_t cqindx, cqmask;
12799517SBill.Taylor@Sun.COM
12809517SBill.Taylor@Sun.COM /* Calculate the CQ table index from the cqnum */
12819517SBill.Taylor@Sun.COM cqmask = (1 << state->hs_cfg_profile->cp_log_num_cq) - 1;
12829517SBill.Taylor@Sun.COM cqindx = cqnum & cqmask;
1283*12965SWilliam.Taylor@Oracle.COM return (hermon_icm_num_to_hdl(state, HERMON_CQC, cqindx));
12849517SBill.Taylor@Sun.COM }
12859517SBill.Taylor@Sun.COM
12869517SBill.Taylor@Sun.COM /*
12879517SBill.Taylor@Sun.COM * hermon_cq_cqe_consume()
12889517SBill.Taylor@Sun.COM * Context: Can be called from interrupt or base context.
12899517SBill.Taylor@Sun.COM */
12909517SBill.Taylor@Sun.COM static void
hermon_cq_cqe_consume(hermon_state_t * state,hermon_cqhdl_t cq,hermon_hw_cqe_t * cqe,ibt_wc_t * wc)12919517SBill.Taylor@Sun.COM hermon_cq_cqe_consume(hermon_state_t *state, hermon_cqhdl_t cq,
12929517SBill.Taylor@Sun.COM hermon_hw_cqe_t *cqe, ibt_wc_t *wc)
12939517SBill.Taylor@Sun.COM {
12949517SBill.Taylor@Sun.COM uint_t opcode, qpnum, qp1_indx;
12959517SBill.Taylor@Sun.COM ibt_wc_flags_t flags;
12969517SBill.Taylor@Sun.COM ibt_wrc_opcode_t type;
12979517SBill.Taylor@Sun.COM
12989517SBill.Taylor@Sun.COM /*
12999517SBill.Taylor@Sun.COM * Determine if this is an "error" CQE by examining "opcode". If it
13009517SBill.Taylor@Sun.COM * is an error CQE, then call hermon_cq_errcqe_consume() and return
13019517SBill.Taylor@Sun.COM * whatever status it returns. Otherwise, this is a successful
13029517SBill.Taylor@Sun.COM * completion.
13039517SBill.Taylor@Sun.COM */
13049517SBill.Taylor@Sun.COM opcode = HERMON_CQE_OPCODE_GET(cq, cqe);
13059517SBill.Taylor@Sun.COM if ((opcode == HERMON_CQE_SEND_ERR_OPCODE) ||
13069517SBill.Taylor@Sun.COM (opcode == HERMON_CQE_RECV_ERR_OPCODE)) {
13079517SBill.Taylor@Sun.COM hermon_cq_errcqe_consume(state, cq, cqe, wc);
13089517SBill.Taylor@Sun.COM return;
13099517SBill.Taylor@Sun.COM }
13109517SBill.Taylor@Sun.COM
13119517SBill.Taylor@Sun.COM /*
13129517SBill.Taylor@Sun.COM * Fetch the Work Request ID using the information in the CQE.
13139517SBill.Taylor@Sun.COM * See hermon_wr.c for more details.
13149517SBill.Taylor@Sun.COM */
13159517SBill.Taylor@Sun.COM wc->wc_id = hermon_wrid_get_entry(cq, cqe);
13169517SBill.Taylor@Sun.COM
13179517SBill.Taylor@Sun.COM /*
13189517SBill.Taylor@Sun.COM * Parse the CQE opcode to determine completion type. This will set
13199517SBill.Taylor@Sun.COM * not only the type of the completion, but also any flags that might
13209517SBill.Taylor@Sun.COM * be associated with it (e.g. whether immediate data is present).
13219517SBill.Taylor@Sun.COM */
13229517SBill.Taylor@Sun.COM flags = IBT_WC_NO_FLAGS;
1323*12965SWilliam.Taylor@Oracle.COM _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(state->hs_fcoib_may_be_running))
13249517SBill.Taylor@Sun.COM if (HERMON_CQE_SENDRECV_GET(cq, cqe) != HERMON_COMPLETION_RECV) {
13259517SBill.Taylor@Sun.COM
13269517SBill.Taylor@Sun.COM /* Send CQE */
13279517SBill.Taylor@Sun.COM switch (opcode) {
13289517SBill.Taylor@Sun.COM case HERMON_CQE_SND_RDMAWR_IMM:
13299517SBill.Taylor@Sun.COM case HERMON_CQE_SND_RDMAWR:
13309517SBill.Taylor@Sun.COM type = IBT_WRC_RDMAW;
13319517SBill.Taylor@Sun.COM break;
13329517SBill.Taylor@Sun.COM
1333*12965SWilliam.Taylor@Oracle.COM case HERMON_CQE_SND_SEND_INV:
13349517SBill.Taylor@Sun.COM case HERMON_CQE_SND_SEND_IMM:
13359517SBill.Taylor@Sun.COM case HERMON_CQE_SND_SEND:
13369517SBill.Taylor@Sun.COM type = IBT_WRC_SEND;
13379517SBill.Taylor@Sun.COM break;
13389517SBill.Taylor@Sun.COM
13399517SBill.Taylor@Sun.COM case HERMON_CQE_SND_LSO:
13409517SBill.Taylor@Sun.COM type = IBT_WRC_SEND_LSO;
13419517SBill.Taylor@Sun.COM break;
13429517SBill.Taylor@Sun.COM
13439517SBill.Taylor@Sun.COM case HERMON_CQE_SND_RDMARD:
13449517SBill.Taylor@Sun.COM type = IBT_WRC_RDMAR;
13459517SBill.Taylor@Sun.COM break;
13469517SBill.Taylor@Sun.COM
13479517SBill.Taylor@Sun.COM case HERMON_CQE_SND_ATOMIC_CS:
13489517SBill.Taylor@Sun.COM type = IBT_WRC_CSWAP;
13499517SBill.Taylor@Sun.COM break;
13509517SBill.Taylor@Sun.COM
13519517SBill.Taylor@Sun.COM case HERMON_CQE_SND_ATOMIC_FA:
13529517SBill.Taylor@Sun.COM type = IBT_WRC_FADD;
13539517SBill.Taylor@Sun.COM break;
13549517SBill.Taylor@Sun.COM
13559517SBill.Taylor@Sun.COM case HERMON_CQE_SND_BIND_MW:
13569517SBill.Taylor@Sun.COM type = IBT_WRC_BIND;
13579517SBill.Taylor@Sun.COM break;
13589517SBill.Taylor@Sun.COM
1359*12965SWilliam.Taylor@Oracle.COM case HERMON_CQE_SND_FRWR:
1360*12965SWilliam.Taylor@Oracle.COM type = IBT_WRC_FAST_REG_PMR;
1361*12965SWilliam.Taylor@Oracle.COM break;
1362*12965SWilliam.Taylor@Oracle.COM
1363*12965SWilliam.Taylor@Oracle.COM case HERMON_CQE_SND_LCL_INV:
1364*12965SWilliam.Taylor@Oracle.COM type = IBT_WRC_LOCAL_INVALIDATE;
1365*12965SWilliam.Taylor@Oracle.COM break;
1366*12965SWilliam.Taylor@Oracle.COM
13679517SBill.Taylor@Sun.COM default:
13689517SBill.Taylor@Sun.COM HERMON_WARNING(state, "unknown send CQE type");
13699517SBill.Taylor@Sun.COM wc->wc_status = IBT_WC_LOCAL_QP_OP_ERR;
13709517SBill.Taylor@Sun.COM return;
13719517SBill.Taylor@Sun.COM }
1372*12965SWilliam.Taylor@Oracle.COM } else if ((state->hs_fcoib_may_be_running == B_TRUE) &&
1373*12965SWilliam.Taylor@Oracle.COM hermon_fcoib_is_fexch_qpn(state, HERMON_CQE_QPNUM_GET(cq, cqe))) {
1374*12965SWilliam.Taylor@Oracle.COM type = IBT_WRC_RECV;
1375*12965SWilliam.Taylor@Oracle.COM if (HERMON_CQE_FEXCH_DIFE(cq, cqe))
1376*12965SWilliam.Taylor@Oracle.COM flags |= IBT_WC_DIF_ERROR;
1377*12965SWilliam.Taylor@Oracle.COM wc->wc_bytes_xfer = HERMON_CQE_BYTECNT_GET(cq, cqe);
1378*12965SWilliam.Taylor@Oracle.COM wc->wc_fexch_seq_cnt = HERMON_CQE_FEXCH_SEQ_CNT(cq, cqe);
1379*12965SWilliam.Taylor@Oracle.COM wc->wc_fexch_tx_bytes_xfer = HERMON_CQE_FEXCH_TX_BYTES(cq, cqe);
1380*12965SWilliam.Taylor@Oracle.COM wc->wc_fexch_rx_bytes_xfer = HERMON_CQE_FEXCH_RX_BYTES(cq, cqe);
1381*12965SWilliam.Taylor@Oracle.COM wc->wc_fexch_seq_id = HERMON_CQE_FEXCH_SEQ_ID(cq, cqe);
1382*12965SWilliam.Taylor@Oracle.COM wc->wc_detail = HERMON_CQE_FEXCH_DETAIL(cq, cqe) &
1383*12965SWilliam.Taylor@Oracle.COM IBT_WC_DETAIL_FC_MATCH_MASK;
1384*12965SWilliam.Taylor@Oracle.COM wc->wc_rkey = HERMON_CQE_IMM_ETH_PKEY_CRED_GET(cq, cqe);
1385*12965SWilliam.Taylor@Oracle.COM flags |= IBT_WC_FEXCH_FMT | IBT_WC_RKEY_INVALIDATED;
13869517SBill.Taylor@Sun.COM } else {
1387*12965SWilliam.Taylor@Oracle.COM /*
1388*12965SWilliam.Taylor@Oracle.COM * Parse the remaining contents of the CQE into the work
1389*12965SWilliam.Taylor@Oracle.COM * completion. This means filling in SL, QP number, SLID,
1390*12965SWilliam.Taylor@Oracle.COM * immediate data, etc.
1391*12965SWilliam.Taylor@Oracle.COM *
1392*12965SWilliam.Taylor@Oracle.COM * Note: Not all of these fields are valid in a given
1393*12965SWilliam.Taylor@Oracle.COM * completion. Many of them depend on the actual type of
1394*12965SWilliam.Taylor@Oracle.COM * completion. So we fill in all of the fields and leave
1395*12965SWilliam.Taylor@Oracle.COM * it up to the IBTF and consumer to sort out which are
1396*12965SWilliam.Taylor@Oracle.COM * valid based on their context.
1397*12965SWilliam.Taylor@Oracle.COM */
1398*12965SWilliam.Taylor@Oracle.COM wc->wc_sl = HERMON_CQE_SL_GET(cq, cqe);
1399*12965SWilliam.Taylor@Oracle.COM wc->wc_qpn = HERMON_CQE_DQPN_GET(cq, cqe);
1400*12965SWilliam.Taylor@Oracle.COM wc->wc_slid = HERMON_CQE_DLID_GET(cq, cqe);
1401*12965SWilliam.Taylor@Oracle.COM wc->wc_immed_data =
1402*12965SWilliam.Taylor@Oracle.COM HERMON_CQE_IMM_ETH_PKEY_CRED_GET(cq, cqe);
1403*12965SWilliam.Taylor@Oracle.COM wc->wc_ethertype = (wc->wc_immed_data & 0xFFFF);
1404*12965SWilliam.Taylor@Oracle.COM wc->wc_pkey_ix = (wc->wc_immed_data &
1405*12965SWilliam.Taylor@Oracle.COM ((1 << state->hs_queryport.log_max_pkey) - 1));
1406*12965SWilliam.Taylor@Oracle.COM /*
1407*12965SWilliam.Taylor@Oracle.COM * Fill in "bytes transferred" as appropriate. Also,
1408*12965SWilliam.Taylor@Oracle.COM * if necessary, fill in the "path bits" field.
1409*12965SWilliam.Taylor@Oracle.COM */
1410*12965SWilliam.Taylor@Oracle.COM wc->wc_path_bits = HERMON_CQE_PATHBITS_GET(cq, cqe);
1411*12965SWilliam.Taylor@Oracle.COM wc->wc_bytes_xfer = HERMON_CQE_BYTECNT_GET(cq, cqe);
1412*12965SWilliam.Taylor@Oracle.COM
1413*12965SWilliam.Taylor@Oracle.COM /*
1414*12965SWilliam.Taylor@Oracle.COM * Check for GRH, update the flags, then fill in "wc_flags"
1415*12965SWilliam.Taylor@Oracle.COM * field in the work completion
1416*12965SWilliam.Taylor@Oracle.COM */
1417*12965SWilliam.Taylor@Oracle.COM if (HERMON_CQE_GRH_GET(cq, cqe) != 0) {
1418*12965SWilliam.Taylor@Oracle.COM flags |= IBT_WC_GRH_PRESENT;
1419*12965SWilliam.Taylor@Oracle.COM }
14209517SBill.Taylor@Sun.COM
14219517SBill.Taylor@Sun.COM /* Receive CQE */
1422*12965SWilliam.Taylor@Oracle.COM switch (opcode) {
14239517SBill.Taylor@Sun.COM case HERMON_CQE_RCV_SEND_IMM:
14249517SBill.Taylor@Sun.COM /*
14259517SBill.Taylor@Sun.COM * Note: According to the PRM, all QP1 recv
14269517SBill.Taylor@Sun.COM * completions look like the result of a Send with
14279517SBill.Taylor@Sun.COM * Immediate. They are not, however, (MADs are Send
14289517SBill.Taylor@Sun.COM * Only) so we need to check the QP number and set
14299517SBill.Taylor@Sun.COM * the flag only if it is non-QP1.
14309517SBill.Taylor@Sun.COM */
14319517SBill.Taylor@Sun.COM qpnum = HERMON_CQE_QPNUM_GET(cq, cqe);
14329517SBill.Taylor@Sun.COM qp1_indx = state->hs_spec_qp1->hr_indx;
14339517SBill.Taylor@Sun.COM if ((qpnum < qp1_indx) || (qpnum > qp1_indx + 1)) {
14349517SBill.Taylor@Sun.COM flags |= IBT_WC_IMMED_DATA_PRESENT;
14359517SBill.Taylor@Sun.COM }
14369517SBill.Taylor@Sun.COM /* FALLTHROUGH */
1437*12965SWilliam.Taylor@Oracle.COM
14389517SBill.Taylor@Sun.COM case HERMON_CQE_RCV_SEND:
14399517SBill.Taylor@Sun.COM type = IBT_WRC_RECV;
14409517SBill.Taylor@Sun.COM if (HERMON_CQE_IS_IPOK(cq, cqe)) {
14419517SBill.Taylor@Sun.COM wc->wc_cksum = HERMON_CQE_CKSUM(cq, cqe);
14429517SBill.Taylor@Sun.COM flags |= IBT_WC_CKSUM_OK;
14439517SBill.Taylor@Sun.COM wc->wc_detail = IBT_WC_DETAIL_ALL_FLAGS_MASK &
14449517SBill.Taylor@Sun.COM HERMON_CQE_IPOIB_STATUS(cq, cqe);
14459517SBill.Taylor@Sun.COM }
14469517SBill.Taylor@Sun.COM break;
1447*12965SWilliam.Taylor@Oracle.COM
1448*12965SWilliam.Taylor@Oracle.COM case HERMON_CQE_RCV_SEND_INV:
1449*12965SWilliam.Taylor@Oracle.COM type = IBT_WRC_RECV;
1450*12965SWilliam.Taylor@Oracle.COM flags |= IBT_WC_RKEY_INVALIDATED;
1451*12965SWilliam.Taylor@Oracle.COM wc->wc_rkey = wc->wc_immed_data; /* same field in cqe */
1452*12965SWilliam.Taylor@Oracle.COM break;
1453*12965SWilliam.Taylor@Oracle.COM
14549517SBill.Taylor@Sun.COM case HERMON_CQE_RCV_RDMAWR_IMM:
14559517SBill.Taylor@Sun.COM flags |= IBT_WC_IMMED_DATA_PRESENT;
14569517SBill.Taylor@Sun.COM type = IBT_WRC_RECV_RDMAWI;
14579517SBill.Taylor@Sun.COM break;
14589517SBill.Taylor@Sun.COM
14599517SBill.Taylor@Sun.COM default:
14609517SBill.Taylor@Sun.COM
14619517SBill.Taylor@Sun.COM HERMON_WARNING(state, "unknown recv CQE type");
14629517SBill.Taylor@Sun.COM wc->wc_status = IBT_WC_LOCAL_QP_OP_ERR;
14639517SBill.Taylor@Sun.COM return;
14649517SBill.Taylor@Sun.COM }
14659517SBill.Taylor@Sun.COM }
14669517SBill.Taylor@Sun.COM wc->wc_type = type;
14679517SBill.Taylor@Sun.COM wc->wc_flags = flags;
14689517SBill.Taylor@Sun.COM wc->wc_status = IBT_WC_SUCCESS;
14699517SBill.Taylor@Sun.COM }
14709517SBill.Taylor@Sun.COM
14719517SBill.Taylor@Sun.COM /*
14729517SBill.Taylor@Sun.COM * hermon_cq_errcqe_consume()
14739517SBill.Taylor@Sun.COM * Context: Can be called from interrupt or base context.
14749517SBill.Taylor@Sun.COM */
14759517SBill.Taylor@Sun.COM static void
hermon_cq_errcqe_consume(hermon_state_t * state,hermon_cqhdl_t cq,hermon_hw_cqe_t * cqe,ibt_wc_t * wc)14769517SBill.Taylor@Sun.COM hermon_cq_errcqe_consume(hermon_state_t *state, hermon_cqhdl_t cq,
14779517SBill.Taylor@Sun.COM hermon_hw_cqe_t *cqe, ibt_wc_t *wc)
14789517SBill.Taylor@Sun.COM {
14799517SBill.Taylor@Sun.COM uint32_t imm_eth_pkey_cred;
14809517SBill.Taylor@Sun.COM uint_t status;
14819517SBill.Taylor@Sun.COM ibt_wc_status_t ibt_status;
14829517SBill.Taylor@Sun.COM
14839517SBill.Taylor@Sun.COM /*
14849517SBill.Taylor@Sun.COM * Fetch the Work Request ID using the information in the CQE.
14859517SBill.Taylor@Sun.COM * See hermon_wr.c for more details.
14869517SBill.Taylor@Sun.COM */
14879517SBill.Taylor@Sun.COM wc->wc_id = hermon_wrid_get_entry(cq, cqe);
14889517SBill.Taylor@Sun.COM
14899517SBill.Taylor@Sun.COM /*
14909517SBill.Taylor@Sun.COM * Parse the CQE opcode to determine completion type. We know that
14919517SBill.Taylor@Sun.COM * the CQE is an error completion, so we extract only the completion
14929517SBill.Taylor@Sun.COM * status/syndrome here.
14939517SBill.Taylor@Sun.COM */
14949517SBill.Taylor@Sun.COM imm_eth_pkey_cred = HERMON_CQE_ERROR_SYNDROME_GET(cq, cqe);
14959517SBill.Taylor@Sun.COM status = imm_eth_pkey_cred;
149611972SBill.Taylor@Sun.COM if (status != HERMON_CQE_WR_FLUSHED_ERR)
1497*12965SWilliam.Taylor@Oracle.COM IBTF_DPRINTF_L2("CQE ERR", "cqe %p QPN %x indx %x status 0x%x "
1498*12965SWilliam.Taylor@Oracle.COM "vendor syndrome %x", cqe, HERMON_CQE_QPNUM_GET(cq, cqe),
1499*12965SWilliam.Taylor@Oracle.COM HERMON_CQE_WQECNTR_GET(cq, cqe), status,
1500*12965SWilliam.Taylor@Oracle.COM HERMON_CQE_ERROR_VENDOR_SYNDROME_GET(cq, cqe));
15019517SBill.Taylor@Sun.COM switch (status) {
15029517SBill.Taylor@Sun.COM case HERMON_CQE_LOC_LEN_ERR:
1503*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, HERMON_FMA_LOCLEN);
15049517SBill.Taylor@Sun.COM ibt_status = IBT_WC_LOCAL_LEN_ERR;
15059517SBill.Taylor@Sun.COM break;
15069517SBill.Taylor@Sun.COM
15079517SBill.Taylor@Sun.COM case HERMON_CQE_LOC_OP_ERR:
1508*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, HERMON_FMA_LOCQPOP);
15099517SBill.Taylor@Sun.COM ibt_status = IBT_WC_LOCAL_QP_OP_ERR;
15109517SBill.Taylor@Sun.COM break;
15119517SBill.Taylor@Sun.COM
15129517SBill.Taylor@Sun.COM case HERMON_CQE_LOC_PROT_ERR:
1513*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, HERMON_FMA_LOCPROT);
15149517SBill.Taylor@Sun.COM ibt_status = IBT_WC_LOCAL_PROTECT_ERR;
1515*12965SWilliam.Taylor@Oracle.COM IBTF_DPRINTF_L2("ERRCQE", "is at %p", cqe);
15169517SBill.Taylor@Sun.COM if (hermon_should_panic) {
15179517SBill.Taylor@Sun.COM cmn_err(CE_PANIC, "Hermon intentional PANIC - "
15189517SBill.Taylor@Sun.COM "Local Protection Error\n");
15199517SBill.Taylor@Sun.COM }
15209517SBill.Taylor@Sun.COM break;
15219517SBill.Taylor@Sun.COM
15229517SBill.Taylor@Sun.COM case HERMON_CQE_WR_FLUSHED_ERR:
15239517SBill.Taylor@Sun.COM ibt_status = IBT_WC_WR_FLUSHED_ERR;
15249517SBill.Taylor@Sun.COM break;
15259517SBill.Taylor@Sun.COM
15269517SBill.Taylor@Sun.COM case HERMON_CQE_MW_BIND_ERR:
1527*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, HERMON_FMA_MWBIND);
15289517SBill.Taylor@Sun.COM ibt_status = IBT_WC_MEM_WIN_BIND_ERR;
15299517SBill.Taylor@Sun.COM break;
15309517SBill.Taylor@Sun.COM
15319517SBill.Taylor@Sun.COM case HERMON_CQE_BAD_RESPONSE_ERR:
1532*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, HERMON_FMA_RESP);
15339517SBill.Taylor@Sun.COM ibt_status = IBT_WC_BAD_RESPONSE_ERR;
15349517SBill.Taylor@Sun.COM break;
15359517SBill.Taylor@Sun.COM
15369517SBill.Taylor@Sun.COM case HERMON_CQE_LOCAL_ACCESS_ERR:
1537*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, HERMON_FMA_LOCACC);
15389517SBill.Taylor@Sun.COM ibt_status = IBT_WC_LOCAL_ACCESS_ERR;
15399517SBill.Taylor@Sun.COM break;
15409517SBill.Taylor@Sun.COM
15419517SBill.Taylor@Sun.COM case HERMON_CQE_REM_INV_REQ_ERR:
1542*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, HERMON_FMA_REMREQ);
15439517SBill.Taylor@Sun.COM ibt_status = IBT_WC_REMOTE_INVALID_REQ_ERR;
15449517SBill.Taylor@Sun.COM break;
15459517SBill.Taylor@Sun.COM
15469517SBill.Taylor@Sun.COM case HERMON_CQE_REM_ACC_ERR:
1547*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, HERMON_FMA_REMACC);
15489517SBill.Taylor@Sun.COM ibt_status = IBT_WC_REMOTE_ACCESS_ERR;
15499517SBill.Taylor@Sun.COM break;
15509517SBill.Taylor@Sun.COM
15519517SBill.Taylor@Sun.COM case HERMON_CQE_REM_OP_ERR:
1552*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, HERMON_FMA_REMOP);
15539517SBill.Taylor@Sun.COM ibt_status = IBT_WC_REMOTE_OP_ERR;
15549517SBill.Taylor@Sun.COM break;
15559517SBill.Taylor@Sun.COM
15569517SBill.Taylor@Sun.COM case HERMON_CQE_TRANS_TO_ERR:
1557*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, HERMON_FMA_XPORTCNT);
15589517SBill.Taylor@Sun.COM ibt_status = IBT_WC_TRANS_TIMEOUT_ERR;
15599517SBill.Taylor@Sun.COM break;
15609517SBill.Taylor@Sun.COM
15619517SBill.Taylor@Sun.COM case HERMON_CQE_RNRNAK_TO_ERR:
1562*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, HERMON_FMA_RNRCNT);
15639517SBill.Taylor@Sun.COM ibt_status = IBT_WC_RNR_NAK_TIMEOUT_ERR;
15649517SBill.Taylor@Sun.COM break;
15659517SBill.Taylor@Sun.COM
15669517SBill.Taylor@Sun.COM /*
15679517SBill.Taylor@Sun.COM * The following error codes are not supported in the Hermon driver
15689517SBill.Taylor@Sun.COM * as they relate only to Reliable Datagram completion statuses:
15699517SBill.Taylor@Sun.COM * case HERMON_CQE_LOCAL_RDD_VIO_ERR:
15709517SBill.Taylor@Sun.COM * case HERMON_CQE_REM_INV_RD_REQ_ERR:
15719517SBill.Taylor@Sun.COM * case HERMON_CQE_EEC_REM_ABORTED_ERR:
15729517SBill.Taylor@Sun.COM * case HERMON_CQE_INV_EEC_NUM_ERR:
15739517SBill.Taylor@Sun.COM * case HERMON_CQE_INV_EEC_STATE_ERR:
15749517SBill.Taylor@Sun.COM * case HERMON_CQE_LOC_EEC_ERR:
15759517SBill.Taylor@Sun.COM */
15769517SBill.Taylor@Sun.COM
15779517SBill.Taylor@Sun.COM default:
15789517SBill.Taylor@Sun.COM HERMON_WARNING(state, "unknown error CQE status");
15799517SBill.Taylor@Sun.COM HERMON_FMANOTE(state, HERMON_FMA_UNKN);
15809517SBill.Taylor@Sun.COM ibt_status = IBT_WC_LOCAL_QP_OP_ERR;
15819517SBill.Taylor@Sun.COM break;
15829517SBill.Taylor@Sun.COM }
15839517SBill.Taylor@Sun.COM
15849517SBill.Taylor@Sun.COM wc->wc_status = ibt_status;
15859517SBill.Taylor@Sun.COM }
15869517SBill.Taylor@Sun.COM
15879517SBill.Taylor@Sun.COM
15889517SBill.Taylor@Sun.COM /*
15899517SBill.Taylor@Sun.COM * hermon_cq_resize_helper()
15909517SBill.Taylor@Sun.COM * Context: Can be called only from user or kernel context.
15919517SBill.Taylor@Sun.COM */
15929517SBill.Taylor@Sun.COM void
hermon_cq_resize_helper(hermon_state_t * state,hermon_cqhdl_t cq)15939517SBill.Taylor@Sun.COM hermon_cq_resize_helper(hermon_state_t *state, hermon_cqhdl_t cq)
15949517SBill.Taylor@Sun.COM {
15959517SBill.Taylor@Sun.COM hermon_cqhdl_t resize_hdl;
15969517SBill.Taylor@Sun.COM int status;
15979517SBill.Taylor@Sun.COM
15989517SBill.Taylor@Sun.COM /*
15999517SBill.Taylor@Sun.COM * we're here because we found the special cqe opcode, so we have
16009517SBill.Taylor@Sun.COM * to update the cq_handle, release the old resources, clear the
16019517SBill.Taylor@Sun.COM * flag in the cq_hdl, and release the resize_hdl. When we return
16029517SBill.Taylor@Sun.COM * above, it will take care of the rest
16039517SBill.Taylor@Sun.COM */
16049517SBill.Taylor@Sun.COM ASSERT(MUTEX_HELD(&cq->cq_lock));
16059517SBill.Taylor@Sun.COM
16069517SBill.Taylor@Sun.COM resize_hdl = cq->cq_resize_hdl;
16079517SBill.Taylor@Sun.COM
16089517SBill.Taylor@Sun.COM /*
16099517SBill.Taylor@Sun.COM * Deregister the memory for the old Completion Queue. Note: We
16109517SBill.Taylor@Sun.COM * really can't return error here because we have no good way to
16119517SBill.Taylor@Sun.COM * cleanup. Plus, the deregistration really shouldn't ever happen.
16129517SBill.Taylor@Sun.COM * So, if it does, it is an indication that something has gone
16139517SBill.Taylor@Sun.COM * seriously wrong. So we print a warning message and return error
16149517SBill.Taylor@Sun.COM * (knowing, of course, that the "old" CQ memory will be leaked)
16159517SBill.Taylor@Sun.COM */
16169517SBill.Taylor@Sun.COM status = hermon_mr_deregister(state, &cq->cq_mrhdl, HERMON_MR_DEREG_ALL,
16179517SBill.Taylor@Sun.COM HERMON_SLEEP);
16189517SBill.Taylor@Sun.COM if (status != DDI_SUCCESS) {
16199517SBill.Taylor@Sun.COM HERMON_WARNING(state, "failed to deregister old CQ memory");
16209517SBill.Taylor@Sun.COM }
16219517SBill.Taylor@Sun.COM
16229517SBill.Taylor@Sun.COM /* Next, free the memory from the old CQ buffer */
16239517SBill.Taylor@Sun.COM hermon_queue_free(&cq->cq_cqinfo);
16249517SBill.Taylor@Sun.COM
16259517SBill.Taylor@Sun.COM /* now we can update the cq_hdl with the new things saved */
16269517SBill.Taylor@Sun.COM
16279517SBill.Taylor@Sun.COM cq->cq_buf = resize_hdl->cq_buf;
16289517SBill.Taylor@Sun.COM cq->cq_mrhdl = resize_hdl->cq_mrhdl;
16299517SBill.Taylor@Sun.COM cq->cq_bufsz = resize_hdl->cq_bufsz;
16309517SBill.Taylor@Sun.COM cq->cq_log_cqsz = resize_hdl->cq_log_cqsz;
16319517SBill.Taylor@Sun.COM cq->cq_umap_dhp = cq->cq_resize_hdl->cq_umap_dhp;
16329517SBill.Taylor@Sun.COM cq->cq_resize_hdl = 0;
16339517SBill.Taylor@Sun.COM bcopy(&resize_hdl->cq_cqinfo, &cq->cq_cqinfo,
16349517SBill.Taylor@Sun.COM sizeof (struct hermon_qalloc_info_s));
16359517SBill.Taylor@Sun.COM
16369517SBill.Taylor@Sun.COM /* finally, release the resizing handle */
16379517SBill.Taylor@Sun.COM kmem_free(resize_hdl, sizeof (struct hermon_sw_cq_s));
16389517SBill.Taylor@Sun.COM }
16399517SBill.Taylor@Sun.COM
16409517SBill.Taylor@Sun.COM
16419517SBill.Taylor@Sun.COM /*
16429517SBill.Taylor@Sun.COM * hermon_cq_entries_flush()
16439517SBill.Taylor@Sun.COM * Context: Can be called from interrupt or base context.
16449517SBill.Taylor@Sun.COM */
16459517SBill.Taylor@Sun.COM /* ARGSUSED */
16469517SBill.Taylor@Sun.COM void
hermon_cq_entries_flush(hermon_state_t * state,hermon_qphdl_t qp)16479517SBill.Taylor@Sun.COM hermon_cq_entries_flush(hermon_state_t *state, hermon_qphdl_t qp)
16489517SBill.Taylor@Sun.COM {
16499517SBill.Taylor@Sun.COM hermon_cqhdl_t cq;
16509517SBill.Taylor@Sun.COM hermon_hw_cqe_t *cqe, *next_cqe;
16519517SBill.Taylor@Sun.COM hermon_srqhdl_t srq;
16529517SBill.Taylor@Sun.COM hermon_workq_hdr_t *wq;
16539517SBill.Taylor@Sun.COM uint32_t cons_indx, tail_cons_indx, wrap_around_mask;
16549517SBill.Taylor@Sun.COM uint32_t new_indx, check_indx, qpnum;
1655*12965SWilliam.Taylor@Oracle.COM uint32_t shift, mask;
16569517SBill.Taylor@Sun.COM int outstanding_cqes;
16579517SBill.Taylor@Sun.COM
16589517SBill.Taylor@Sun.COM qpnum = qp->qp_qpnum;
16599517SBill.Taylor@Sun.COM if ((srq = qp->qp_srqhdl) != NULL)
16609517SBill.Taylor@Sun.COM wq = qp->qp_srqhdl->srq_wq_wqhdr;
16619517SBill.Taylor@Sun.COM else
16629517SBill.Taylor@Sun.COM wq = NULL;
16639517SBill.Taylor@Sun.COM cq = qp->qp_rq_cqhdl;
16649517SBill.Taylor@Sun.COM
1665*12965SWilliam.Taylor@Oracle.COM if (cq == NULL) {
1666*12965SWilliam.Taylor@Oracle.COM cq = qp->qp_sq_cqhdl;
1667*12965SWilliam.Taylor@Oracle.COM }
1668*12965SWilliam.Taylor@Oracle.COM
16699517SBill.Taylor@Sun.COM do_send_cq: /* loop back to here if send_cq is not the same as recv_cq */
1670*12965SWilliam.Taylor@Oracle.COM if (cq == NULL)
1671*12965SWilliam.Taylor@Oracle.COM return;
16729517SBill.Taylor@Sun.COM
16739517SBill.Taylor@Sun.COM cons_indx = cq->cq_consindx;
1674*12965SWilliam.Taylor@Oracle.COM shift = cq->cq_log_cqsz;
1675*12965SWilliam.Taylor@Oracle.COM mask = cq->cq_bufsz;
1676*12965SWilliam.Taylor@Oracle.COM wrap_around_mask = mask - 1;
16779517SBill.Taylor@Sun.COM
16789517SBill.Taylor@Sun.COM /* Calculate the pointer to the first CQ entry */
16799517SBill.Taylor@Sun.COM cqe = &cq->cq_buf[cons_indx & wrap_around_mask];
16809517SBill.Taylor@Sun.COM
16819517SBill.Taylor@Sun.COM /*
16829517SBill.Taylor@Sun.COM * Loop through the CQ looking for entries owned by software. If an
16839517SBill.Taylor@Sun.COM * entry is owned by software then we increment an 'outstanding_cqes'
16849517SBill.Taylor@Sun.COM * count to know how many entries total we have on our CQ. We use this
16859517SBill.Taylor@Sun.COM * value further down to know how many entries to loop through looking
16869517SBill.Taylor@Sun.COM * for our same QP number.
16879517SBill.Taylor@Sun.COM */
16889517SBill.Taylor@Sun.COM outstanding_cqes = 0;
16899517SBill.Taylor@Sun.COM tail_cons_indx = cons_indx;
1690*12965SWilliam.Taylor@Oracle.COM while (HERMON_CQE_OWNER_IS_SW(cq, cqe, tail_cons_indx, shift, mask)) {
16919517SBill.Taylor@Sun.COM /* increment total cqes count */
16929517SBill.Taylor@Sun.COM outstanding_cqes++;
16939517SBill.Taylor@Sun.COM
16949517SBill.Taylor@Sun.COM /* increment the consumer index */
16959517SBill.Taylor@Sun.COM tail_cons_indx++;
16969517SBill.Taylor@Sun.COM
16979517SBill.Taylor@Sun.COM /* update the pointer to the next cq entry */
16989517SBill.Taylor@Sun.COM cqe = &cq->cq_buf[tail_cons_indx & wrap_around_mask];
16999517SBill.Taylor@Sun.COM }
17009517SBill.Taylor@Sun.COM
17019517SBill.Taylor@Sun.COM /*
17029517SBill.Taylor@Sun.COM * Using the 'tail_cons_indx' that was just set, we now know how many
17039517SBill.Taylor@Sun.COM * total CQEs possible there are. Set the 'check_indx' and the
17049517SBill.Taylor@Sun.COM * 'new_indx' to the last entry identified by 'tail_cons_indx'
17059517SBill.Taylor@Sun.COM */
17069517SBill.Taylor@Sun.COM check_indx = new_indx = (tail_cons_indx - 1);
17079517SBill.Taylor@Sun.COM
17089517SBill.Taylor@Sun.COM while (--outstanding_cqes >= 0) {
17099517SBill.Taylor@Sun.COM cqe = &cq->cq_buf[check_indx & wrap_around_mask];
17109517SBill.Taylor@Sun.COM
17119517SBill.Taylor@Sun.COM /*
17129517SBill.Taylor@Sun.COM * If the QP number is the same in the CQE as the QP, then
17139517SBill.Taylor@Sun.COM * we must "consume" it. If it is for an SRQ wqe, then we
17149517SBill.Taylor@Sun.COM * also must free the wqe back onto the free list of the SRQ.
17159517SBill.Taylor@Sun.COM */
17169517SBill.Taylor@Sun.COM if (qpnum == HERMON_CQE_QPNUM_GET(cq, cqe)) {
17179517SBill.Taylor@Sun.COM if (srq && (HERMON_CQE_SENDRECV_GET(cq, cqe) ==
17189517SBill.Taylor@Sun.COM HERMON_COMPLETION_RECV)) {
17199517SBill.Taylor@Sun.COM uint64_t *desc;
17209517SBill.Taylor@Sun.COM int indx;
17219517SBill.Taylor@Sun.COM
17229517SBill.Taylor@Sun.COM /* Add wqe back to SRQ free list */
17239517SBill.Taylor@Sun.COM indx = HERMON_CQE_WQEADDRSZ_GET(cq, cqe) &
17249517SBill.Taylor@Sun.COM wq->wq_mask;
17259517SBill.Taylor@Sun.COM desc = HERMON_SRQ_WQE_ADDR(srq, wq->wq_tail);
17269517SBill.Taylor@Sun.COM ((uint16_t *)desc)[1] = htons(indx);
17279517SBill.Taylor@Sun.COM wq->wq_tail = indx;
17289517SBill.Taylor@Sun.COM }
17299517SBill.Taylor@Sun.COM } else { /* CQEs for other QPNs need to remain */
17309517SBill.Taylor@Sun.COM if (check_indx != new_indx) {
17319517SBill.Taylor@Sun.COM next_cqe =
17329517SBill.Taylor@Sun.COM &cq->cq_buf[new_indx & wrap_around_mask];
17339517SBill.Taylor@Sun.COM /* Copy the CQE into the "next_cqe" pointer. */
17349517SBill.Taylor@Sun.COM bcopy(cqe, next_cqe, sizeof (hermon_hw_cqe_t));
17359517SBill.Taylor@Sun.COM }
17369517SBill.Taylor@Sun.COM new_indx--; /* move index to next CQE to fill */
17379517SBill.Taylor@Sun.COM }
17389517SBill.Taylor@Sun.COM check_indx--; /* move index to next CQE to check */
17399517SBill.Taylor@Sun.COM }
17409517SBill.Taylor@Sun.COM
17419517SBill.Taylor@Sun.COM /*
17429517SBill.Taylor@Sun.COM * Update consumer index to be the 'new_indx'. This moves it past all
17439517SBill.Taylor@Sun.COM * removed entries. Because 'new_indx' is pointing to the last
17449517SBill.Taylor@Sun.COM * previously valid SW owned entry, we add 1 to point the cons_indx to
17459517SBill.Taylor@Sun.COM * the first HW owned entry.
17469517SBill.Taylor@Sun.COM */
17479517SBill.Taylor@Sun.COM cons_indx = (new_indx + 1);
17489517SBill.Taylor@Sun.COM
17499517SBill.Taylor@Sun.COM /*
17509517SBill.Taylor@Sun.COM * Now we only ring the doorbell (to update the consumer index) if
17519517SBill.Taylor@Sun.COM * we've actually consumed a CQ entry. If we found no QP number
17529517SBill.Taylor@Sun.COM * matches above, then we would not have removed anything. So only if
17539517SBill.Taylor@Sun.COM * something was removed do we ring the doorbell.
17549517SBill.Taylor@Sun.COM */
17559517SBill.Taylor@Sun.COM if (cq->cq_consindx != cons_indx) {
17569517SBill.Taylor@Sun.COM /*
17579517SBill.Taylor@Sun.COM * Update the consumer index in both the CQ handle and the
17589517SBill.Taylor@Sun.COM * doorbell record.
17599517SBill.Taylor@Sun.COM */
17609517SBill.Taylor@Sun.COM cq->cq_consindx = cons_indx;
17619517SBill.Taylor@Sun.COM
17629517SBill.Taylor@Sun.COM hermon_cq_update_ci_doorbell(cq);
17639517SBill.Taylor@Sun.COM
17649517SBill.Taylor@Sun.COM }
17659517SBill.Taylor@Sun.COM if (cq != qp->qp_sq_cqhdl) {
17669517SBill.Taylor@Sun.COM cq = qp->qp_sq_cqhdl;
17679517SBill.Taylor@Sun.COM goto do_send_cq;
17689517SBill.Taylor@Sun.COM }
17699517SBill.Taylor@Sun.COM }
1770*12965SWilliam.Taylor@Oracle.COM
1771*12965SWilliam.Taylor@Oracle.COM /*
1772*12965SWilliam.Taylor@Oracle.COM * hermon_get_cq_sched_list()
1773*12965SWilliam.Taylor@Oracle.COM * Context: Only called from attach() path context
1774*12965SWilliam.Taylor@Oracle.COM *
1775*12965SWilliam.Taylor@Oracle.COM * Read properties, creating entries in hs_cq_sched_list with
1776*12965SWilliam.Taylor@Oracle.COM * information about the requested "expected" and "minimum"
1777*12965SWilliam.Taylor@Oracle.COM * number of MSI-X interrupt vectors per list entry.
1778*12965SWilliam.Taylor@Oracle.COM */
1779*12965SWilliam.Taylor@Oracle.COM static int
hermon_get_cq_sched_list(hermon_state_t * state)1780*12965SWilliam.Taylor@Oracle.COM hermon_get_cq_sched_list(hermon_state_t *state)
1781*12965SWilliam.Taylor@Oracle.COM {
1782*12965SWilliam.Taylor@Oracle.COM char **listp, ulp_prop[HERMON_CQH_MAX + 4];
1783*12965SWilliam.Taylor@Oracle.COM uint_t nlist, i, j, ndata;
1784*12965SWilliam.Taylor@Oracle.COM int *data;
1785*12965SWilliam.Taylor@Oracle.COM size_t len;
1786*12965SWilliam.Taylor@Oracle.COM hermon_cq_sched_t *cq_schedp;
1787*12965SWilliam.Taylor@Oracle.COM
1788*12965SWilliam.Taylor@Oracle.COM if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, state->hs_dip,
1789*12965SWilliam.Taylor@Oracle.COM DDI_PROP_DONTPASS, "cqh-group-list", &listp, &nlist) !=
1790*12965SWilliam.Taylor@Oracle.COM DDI_PROP_SUCCESS)
1791*12965SWilliam.Taylor@Oracle.COM return (0);
1792*12965SWilliam.Taylor@Oracle.COM
1793*12965SWilliam.Taylor@Oracle.COM state->hs_cq_sched_array_size = nlist;
1794*12965SWilliam.Taylor@Oracle.COM state->hs_cq_sched_array = cq_schedp = kmem_zalloc(nlist *
1795*12965SWilliam.Taylor@Oracle.COM sizeof (hermon_cq_sched_t), KM_SLEEP);
1796*12965SWilliam.Taylor@Oracle.COM for (i = 0; i < nlist; i++) {
1797*12965SWilliam.Taylor@Oracle.COM if ((len = strlen(listp[i])) >= HERMON_CQH_MAX) {
1798*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "'cqh' property name too long\n");
1799*12965SWilliam.Taylor@Oracle.COM goto game_over;
1800*12965SWilliam.Taylor@Oracle.COM }
1801*12965SWilliam.Taylor@Oracle.COM for (j = 0; j < i; j++) {
1802*12965SWilliam.Taylor@Oracle.COM if (strcmp(listp[j], listp[i]) == 0) {
1803*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "Duplicate 'cqh' property\n");
1804*12965SWilliam.Taylor@Oracle.COM goto game_over;
1805*12965SWilliam.Taylor@Oracle.COM }
1806*12965SWilliam.Taylor@Oracle.COM }
1807*12965SWilliam.Taylor@Oracle.COM (void) strncpy(cq_schedp[i].cqs_name, listp[i], HERMON_CQH_MAX);
1808*12965SWilliam.Taylor@Oracle.COM ulp_prop[0] = 'c';
1809*12965SWilliam.Taylor@Oracle.COM ulp_prop[1] = 'q';
1810*12965SWilliam.Taylor@Oracle.COM ulp_prop[2] = 'h';
1811*12965SWilliam.Taylor@Oracle.COM ulp_prop[3] = '-';
1812*12965SWilliam.Taylor@Oracle.COM (void) strncpy(ulp_prop + 4, listp[i], len + 1);
1813*12965SWilliam.Taylor@Oracle.COM if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, state->hs_dip,
1814*12965SWilliam.Taylor@Oracle.COM DDI_PROP_DONTPASS, ulp_prop, &data, &ndata) !=
1815*12965SWilliam.Taylor@Oracle.COM DDI_PROP_SUCCESS) {
1816*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "property '%s' not found\n", ulp_prop);
1817*12965SWilliam.Taylor@Oracle.COM goto game_over;
1818*12965SWilliam.Taylor@Oracle.COM }
1819*12965SWilliam.Taylor@Oracle.COM if (ndata != 2) {
1820*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "property '%s' does not "
1821*12965SWilliam.Taylor@Oracle.COM "have 2 integers\n", ulp_prop);
1822*12965SWilliam.Taylor@Oracle.COM goto game_over_free_data;
1823*12965SWilliam.Taylor@Oracle.COM }
1824*12965SWilliam.Taylor@Oracle.COM cq_schedp[i].cqs_desired = data[0];
1825*12965SWilliam.Taylor@Oracle.COM cq_schedp[i].cqs_minimum = data[1];
1826*12965SWilliam.Taylor@Oracle.COM cq_schedp[i].cqs_refcnt = 0;
1827*12965SWilliam.Taylor@Oracle.COM ddi_prop_free(data);
1828*12965SWilliam.Taylor@Oracle.COM }
1829*12965SWilliam.Taylor@Oracle.COM if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, state->hs_dip,
1830*12965SWilliam.Taylor@Oracle.COM DDI_PROP_DONTPASS, "cqh-default", &data, &ndata) !=
1831*12965SWilliam.Taylor@Oracle.COM DDI_PROP_SUCCESS) {
1832*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "property 'cqh-default' not found\n");
1833*12965SWilliam.Taylor@Oracle.COM goto game_over;
1834*12965SWilliam.Taylor@Oracle.COM }
1835*12965SWilliam.Taylor@Oracle.COM if (ndata != 2) {
1836*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "property 'cqh-default' does not "
1837*12965SWilliam.Taylor@Oracle.COM "have 2 integers\n");
1838*12965SWilliam.Taylor@Oracle.COM goto game_over_free_data;
1839*12965SWilliam.Taylor@Oracle.COM }
1840*12965SWilliam.Taylor@Oracle.COM cq_schedp = &state->hs_cq_sched_default;
1841*12965SWilliam.Taylor@Oracle.COM cq_schedp->cqs_desired = data[0];
1842*12965SWilliam.Taylor@Oracle.COM cq_schedp->cqs_minimum = data[1];
1843*12965SWilliam.Taylor@Oracle.COM cq_schedp->cqs_refcnt = 0;
1844*12965SWilliam.Taylor@Oracle.COM ddi_prop_free(data);
1845*12965SWilliam.Taylor@Oracle.COM ddi_prop_free(listp);
1846*12965SWilliam.Taylor@Oracle.COM return (1); /* game on */
1847*12965SWilliam.Taylor@Oracle.COM
1848*12965SWilliam.Taylor@Oracle.COM game_over_free_data:
1849*12965SWilliam.Taylor@Oracle.COM ddi_prop_free(data);
1850*12965SWilliam.Taylor@Oracle.COM game_over:
1851*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "Error in 'cqh' properties in hermon.conf\n");
1852*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "completion handler groups not being used\n");
1853*12965SWilliam.Taylor@Oracle.COM kmem_free(cq_schedp, nlist * sizeof (hermon_cq_sched_t));
1854*12965SWilliam.Taylor@Oracle.COM state->hs_cq_sched_array_size = 0;
1855*12965SWilliam.Taylor@Oracle.COM ddi_prop_free(listp);
1856*12965SWilliam.Taylor@Oracle.COM return (0);
1857*12965SWilliam.Taylor@Oracle.COM }
1858*12965SWilliam.Taylor@Oracle.COM
1859*12965SWilliam.Taylor@Oracle.COM /*
1860*12965SWilliam.Taylor@Oracle.COM * hermon_cq_sched_init()
1861*12965SWilliam.Taylor@Oracle.COM * Context: Only called from attach() path context
1862*12965SWilliam.Taylor@Oracle.COM *
1863*12965SWilliam.Taylor@Oracle.COM * Read the hermon.conf properties looking for cq_sched info,
1864*12965SWilliam.Taylor@Oracle.COM * creating reserved pools of MSI-X interrupt ranges for the
1865*12965SWilliam.Taylor@Oracle.COM * specified ULPs.
1866*12965SWilliam.Taylor@Oracle.COM */
1867*12965SWilliam.Taylor@Oracle.COM int
hermon_cq_sched_init(hermon_state_t * state)1868*12965SWilliam.Taylor@Oracle.COM hermon_cq_sched_init(hermon_state_t *state)
1869*12965SWilliam.Taylor@Oracle.COM {
1870*12965SWilliam.Taylor@Oracle.COM hermon_cq_sched_t *cq_schedp, *defp;
1871*12965SWilliam.Taylor@Oracle.COM int i, desired, array_size;
1872*12965SWilliam.Taylor@Oracle.COM
1873*12965SWilliam.Taylor@Oracle.COM mutex_init(&state->hs_cq_sched_lock, NULL, MUTEX_DRIVER,
1874*12965SWilliam.Taylor@Oracle.COM DDI_INTR_PRI(state->hs_intrmsi_pri));
1875*12965SWilliam.Taylor@Oracle.COM
1876*12965SWilliam.Taylor@Oracle.COM mutex_enter(&state->hs_cq_sched_lock);
1877*12965SWilliam.Taylor@Oracle.COM state->hs_cq_sched_array = NULL;
1878*12965SWilliam.Taylor@Oracle.COM
1879*12965SWilliam.Taylor@Oracle.COM /* initialize cq_sched_default */
1880*12965SWilliam.Taylor@Oracle.COM defp = &state->hs_cq_sched_default;
1881*12965SWilliam.Taylor@Oracle.COM defp->cqs_start_hid = 1;
1882*12965SWilliam.Taylor@Oracle.COM defp->cqs_len = state->hs_intrmsi_allocd;
1883*12965SWilliam.Taylor@Oracle.COM defp->cqs_next_alloc = defp->cqs_len - 1;
1884*12965SWilliam.Taylor@Oracle.COM (void) strncpy(defp->cqs_name, "default", 8);
1885*12965SWilliam.Taylor@Oracle.COM
1886*12965SWilliam.Taylor@Oracle.COM /* Read properties to determine which ULPs use cq_sched */
1887*12965SWilliam.Taylor@Oracle.COM if (hermon_get_cq_sched_list(state) == 0)
1888*12965SWilliam.Taylor@Oracle.COM goto done;
1889*12965SWilliam.Taylor@Oracle.COM
1890*12965SWilliam.Taylor@Oracle.COM /* Determine if we have enough vectors, or if we have to scale down */
1891*12965SWilliam.Taylor@Oracle.COM desired = defp->cqs_desired; /* default desired (from hermon.conf) */
1892*12965SWilliam.Taylor@Oracle.COM if (desired <= 0)
1893*12965SWilliam.Taylor@Oracle.COM goto done; /* all interrupts in the default pool */
1894*12965SWilliam.Taylor@Oracle.COM cq_schedp = state->hs_cq_sched_array;
1895*12965SWilliam.Taylor@Oracle.COM array_size = state->hs_cq_sched_array_size;
1896*12965SWilliam.Taylor@Oracle.COM for (i = 0; i < array_size; i++)
1897*12965SWilliam.Taylor@Oracle.COM desired += cq_schedp[i].cqs_desired;
1898*12965SWilliam.Taylor@Oracle.COM if (desired > state->hs_intrmsi_allocd) {
1899*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "#interrupts allocated (%d) is less than "
1900*12965SWilliam.Taylor@Oracle.COM "the #interrupts desired (%d)\n",
1901*12965SWilliam.Taylor@Oracle.COM state->hs_intrmsi_allocd, desired);
1902*12965SWilliam.Taylor@Oracle.COM cmn_err(CE_CONT, "completion handler groups not being used\n");
1903*12965SWilliam.Taylor@Oracle.COM goto done; /* all interrupts in the default pool */
1904*12965SWilliam.Taylor@Oracle.COM }
1905*12965SWilliam.Taylor@Oracle.COM /* Game on. For each cq_sched group, reserve the MSI-X range */
1906*12965SWilliam.Taylor@Oracle.COM for (i = 0; i < array_size; i++) {
1907*12965SWilliam.Taylor@Oracle.COM desired = cq_schedp[i].cqs_desired;
1908*12965SWilliam.Taylor@Oracle.COM cq_schedp[i].cqs_start_hid = defp->cqs_start_hid;
1909*12965SWilliam.Taylor@Oracle.COM cq_schedp[i].cqs_len = desired;
1910*12965SWilliam.Taylor@Oracle.COM cq_schedp[i].cqs_next_alloc = desired - 1;
1911*12965SWilliam.Taylor@Oracle.COM defp->cqs_len -= desired;
1912*12965SWilliam.Taylor@Oracle.COM defp->cqs_start_hid += desired;
1913*12965SWilliam.Taylor@Oracle.COM }
1914*12965SWilliam.Taylor@Oracle.COM /* reset default's start allocation seed */
1915*12965SWilliam.Taylor@Oracle.COM state->hs_cq_sched_default.cqs_next_alloc =
1916*12965SWilliam.Taylor@Oracle.COM state->hs_cq_sched_default.cqs_len - 1;
1917*12965SWilliam.Taylor@Oracle.COM
1918*12965SWilliam.Taylor@Oracle.COM done:
1919*12965SWilliam.Taylor@Oracle.COM mutex_exit(&state->hs_cq_sched_lock);
1920*12965SWilliam.Taylor@Oracle.COM return (IBT_SUCCESS);
1921*12965SWilliam.Taylor@Oracle.COM }
1922*12965SWilliam.Taylor@Oracle.COM
1923*12965SWilliam.Taylor@Oracle.COM void
hermon_cq_sched_fini(hermon_state_t * state)1924*12965SWilliam.Taylor@Oracle.COM hermon_cq_sched_fini(hermon_state_t *state)
1925*12965SWilliam.Taylor@Oracle.COM {
1926*12965SWilliam.Taylor@Oracle.COM mutex_enter(&state->hs_cq_sched_lock);
1927*12965SWilliam.Taylor@Oracle.COM if (state->hs_cq_sched_array_size) {
1928*12965SWilliam.Taylor@Oracle.COM kmem_free(state->hs_cq_sched_array, sizeof (hermon_cq_sched_t) *
1929*12965SWilliam.Taylor@Oracle.COM state->hs_cq_sched_array_size);
1930*12965SWilliam.Taylor@Oracle.COM state->hs_cq_sched_array_size = 0;
1931*12965SWilliam.Taylor@Oracle.COM state->hs_cq_sched_array = NULL;
1932*12965SWilliam.Taylor@Oracle.COM }
1933*12965SWilliam.Taylor@Oracle.COM mutex_exit(&state->hs_cq_sched_lock);
1934*12965SWilliam.Taylor@Oracle.COM mutex_destroy(&state->hs_cq_sched_lock);
1935*12965SWilliam.Taylor@Oracle.COM }
1936*12965SWilliam.Taylor@Oracle.COM
1937*12965SWilliam.Taylor@Oracle.COM int
hermon_cq_sched_alloc(hermon_state_t * state,ibt_cq_sched_attr_t * attr,hermon_cq_sched_t ** cq_sched_pp)1938*12965SWilliam.Taylor@Oracle.COM hermon_cq_sched_alloc(hermon_state_t *state, ibt_cq_sched_attr_t *attr,
1939*12965SWilliam.Taylor@Oracle.COM hermon_cq_sched_t **cq_sched_pp)
1940*12965SWilliam.Taylor@Oracle.COM {
1941*12965SWilliam.Taylor@Oracle.COM hermon_cq_sched_t *cq_schedp;
1942*12965SWilliam.Taylor@Oracle.COM int i;
1943*12965SWilliam.Taylor@Oracle.COM char *name;
1944*12965SWilliam.Taylor@Oracle.COM ibt_cq_sched_flags_t flags;
1945*12965SWilliam.Taylor@Oracle.COM
1946*12965SWilliam.Taylor@Oracle.COM flags = attr->cqs_flags;
1947*12965SWilliam.Taylor@Oracle.COM if ((flags & (IBT_CQS_SCHED_GROUP | IBT_CQS_EXACT_SCHED_GROUP)) == 0) {
1948*12965SWilliam.Taylor@Oracle.COM *cq_sched_pp = NULL;
1949*12965SWilliam.Taylor@Oracle.COM return (IBT_SUCCESS);
1950*12965SWilliam.Taylor@Oracle.COM }
1951*12965SWilliam.Taylor@Oracle.COM name = attr->cqs_pool_name;
1952*12965SWilliam.Taylor@Oracle.COM
1953*12965SWilliam.Taylor@Oracle.COM mutex_enter(&state->hs_cq_sched_lock);
1954*12965SWilliam.Taylor@Oracle.COM cq_schedp = state->hs_cq_sched_array;
1955*12965SWilliam.Taylor@Oracle.COM for (i = 0; i < state->hs_cq_sched_array_size; i++, cq_schedp++) {
1956*12965SWilliam.Taylor@Oracle.COM if (strcmp(name, cq_schedp->cqs_name) == 0) {
1957*12965SWilliam.Taylor@Oracle.COM if (cq_schedp->cqs_len != 0)
1958*12965SWilliam.Taylor@Oracle.COM cq_schedp->cqs_refcnt++;
1959*12965SWilliam.Taylor@Oracle.COM break; /* found it */
1960*12965SWilliam.Taylor@Oracle.COM }
1961*12965SWilliam.Taylor@Oracle.COM }
1962*12965SWilliam.Taylor@Oracle.COM if ((i == state->hs_cq_sched_array_size) || /* not found, or */
1963*12965SWilliam.Taylor@Oracle.COM (cq_schedp->cqs_len == 0)) /* defined, but no dedicated intr's */
1964*12965SWilliam.Taylor@Oracle.COM cq_schedp = NULL;
1965*12965SWilliam.Taylor@Oracle.COM mutex_exit(&state->hs_cq_sched_lock);
1966*12965SWilliam.Taylor@Oracle.COM
1967*12965SWilliam.Taylor@Oracle.COM *cq_sched_pp = cq_schedp; /* set to valid hdl, or to NULL */
1968*12965SWilliam.Taylor@Oracle.COM if ((cq_schedp == NULL) &&
1969*12965SWilliam.Taylor@Oracle.COM (attr->cqs_flags & IBT_CQS_EXACT_SCHED_GROUP))
1970*12965SWilliam.Taylor@Oracle.COM return (IBT_CQ_NO_SCHED_GROUP);
1971*12965SWilliam.Taylor@Oracle.COM else
1972*12965SWilliam.Taylor@Oracle.COM return (IBT_SUCCESS);
1973*12965SWilliam.Taylor@Oracle.COM }
1974*12965SWilliam.Taylor@Oracle.COM
1975*12965SWilliam.Taylor@Oracle.COM int
hermon_cq_sched_free(hermon_state_t * state,hermon_cq_sched_t * cq_schedp)1976*12965SWilliam.Taylor@Oracle.COM hermon_cq_sched_free(hermon_state_t *state, hermon_cq_sched_t *cq_schedp)
1977*12965SWilliam.Taylor@Oracle.COM {
1978*12965SWilliam.Taylor@Oracle.COM if (cq_schedp != NULL) {
1979*12965SWilliam.Taylor@Oracle.COM /* Just decrement refcnt */
1980*12965SWilliam.Taylor@Oracle.COM mutex_enter(&state->hs_cq_sched_lock);
1981*12965SWilliam.Taylor@Oracle.COM if (cq_schedp->cqs_refcnt == 0)
1982*12965SWilliam.Taylor@Oracle.COM HERMON_WARNING(state, "cq_sched free underflow\n");
1983*12965SWilliam.Taylor@Oracle.COM else
1984*12965SWilliam.Taylor@Oracle.COM cq_schedp->cqs_refcnt--;
1985*12965SWilliam.Taylor@Oracle.COM mutex_exit(&state->hs_cq_sched_lock);
1986*12965SWilliam.Taylor@Oracle.COM }
1987*12965SWilliam.Taylor@Oracle.COM return (IBT_SUCCESS);
1988*12965SWilliam.Taylor@Oracle.COM }
1989