/* * Copyright (C) 2005 - 2008 ServerEngines * All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * * Contact Information: * linux-drivers@serverengines.com * * ServerEngines * 209 N. Fair Oaks Ave * Sunnyvale, CA 94085 */ #include "hwlib.h" #include "bestatus.h" /* * Completion Queue Objects */ /* *============================================================================ * P U B L I C R O U T I N E S *============================================================================ */ /* This routine creates a completion queue based on the client completion queue configuration information. FunctionObject - Handle to a function object CqBaseVa - Base VA for a the CQ ring NumEntries - CEV_CQ_CNT_* values solEventEnable - 0 = All CQEs can generate Events if CQ is eventable 1 = only CQEs with solicited bit set are eventable eventable - Eventable CQ, generates interrupts. nodelay - 1 = Force interrupt, relevent if CQ eventable. Interrupt is asserted immediately after EQE write is confirmed, regardless of EQ Timer or watermark settings. wme - Enable watermark based coalescing wmThresh - High watermark(CQ fullness at which event or interrupt should be asserted). These are the CEV_WATERMARK encoded values. EqObject - EQ Handle to assign to this CQ ppCqObject - Internal CQ Handle returned. Returns BE_SUCCESS if successfull, otherwise a useful error code is returned. IRQL < DISPATCH_LEVEL */ int be_cq_create(struct be_function_object *pfob, struct ring_desc *rd, u32 length, bool solicited_eventable, bool no_delay, u32 wm_thresh, struct be_eq_object *eq_object, struct be_cq_object *cq_object) { int status = BE_SUCCESS; u32 num_entries_encoding; u32 num_entries = length / sizeof(struct MCC_CQ_ENTRY_AMAP); struct FWCMD_COMMON_CQ_CREATE *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; u32 n; unsigned long irql; ASSERT(rd); ASSERT(cq_object); ASSERT(length % sizeof(struct MCC_CQ_ENTRY_AMAP) == 0); switch (num_entries) { case 256: num_entries_encoding = CEV_CQ_CNT_256; break; case 512: num_entries_encoding = CEV_CQ_CNT_512; break; case 1024: num_entries_encoding = CEV_CQ_CNT_1024; break; default: ASSERT(0); return BE_STATUS_INVALID_PARAMETER; } /* * All cq entries all the same size. Use iSCSI version * as a test for the proper rd length. */ memset(cq_object, 0, sizeof(*cq_object)); atomic_set(&cq_object->ref_count, 0); cq_object->parent_function = pfob; cq_object->eq_object = eq_object; cq_object->num_entries = num_entries; /* save for MCC cq processing */ cq_object->va = rd->va; /* map into UT. */ length = num_entries * sizeof(struct MCC_CQ_ENTRY_AMAP); spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { ASSERT(wrb); TRACE(DL_ERR, "No free MCC WRBs in create EQ."); status = BE_STATUS_NO_MCC_WRB; goto Error; } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_CQ_CREATE); fwcmd->params.request.num_pages = PAGES_SPANNED(OFFSET_IN_PAGE(rd->va), length); AMAP_SET_BITS_PTR(CQ_CONTEXT, valid, &fwcmd->params.request.context, 1); n = pfob->pci_function_number; AMAP_SET_BITS_PTR(CQ_CONTEXT, Func, &fwcmd->params.request.context, n); n = (eq_object != NULL); AMAP_SET_BITS_PTR(CQ_CONTEXT, Eventable, &fwcmd->params.request.context, n); AMAP_SET_BITS_PTR(CQ_CONTEXT, Armed, &fwcmd->params.request.context, 1); n = eq_object ? eq_object->eq_id : 0; AMAP_SET_BITS_PTR(CQ_CONTEXT, EQID, &fwcmd->params.request.context, n); AMAP_SET_BITS_PTR(CQ_CONTEXT, Count, &fwcmd->params.request.context, num_entries_encoding); n = 0; /* Protection Domain is always 0 in Linux driver */ AMAP_SET_BITS_PTR(CQ_CONTEXT, PD, &fwcmd->params.request.context, n); AMAP_SET_BITS_PTR(CQ_CONTEXT, NoDelay, &fwcmd->params.request.context, no_delay); AMAP_SET_BITS_PTR(CQ_CONTEXT, SolEvent, &fwcmd->params.request.context, solicited_eventable); n = (wm_thresh != 0xFFFFFFFF); AMAP_SET_BITS_PTR(CQ_CONTEXT, WME, &fwcmd->params.request.context, n); n = (n ? wm_thresh : 0); AMAP_SET_BITS_PTR(CQ_CONTEXT, Watermark, &fwcmd->params.request.context, n); /* Create a page list for the FWCMD. */ be_rd_to_pa_list(rd, fwcmd->params.request.pages, ARRAY_SIZE(fwcmd->params.request.pages)); /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL, NULL, NULL, fwcmd, NULL); if (status != BE_SUCCESS) { TRACE(DL_ERR, "MCC to create CQ failed."); goto Error; } /* Remember the CQ id. */ cq_object->cq_id = fwcmd->params.response.cq_id; /* insert this cq into eq_object reference */ if (eq_object) { atomic_inc(&eq_object->ref_count); list_add_tail(&cq_object->cqlist_for_eq, &eq_object->cq_list_head); } Error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* Deferences the given object. Once the object's reference count drops to zero, the object is destroyed and all resources that are held by this object are released. The on-chip context is also destroyed along with the queue ID, and any mappings made into the UT. cq_object - CQ handle returned from cq_object_create. returns the current reference count on the object IRQL: IRQL < DISPATCH_LEVEL */ int be_cq_destroy(struct be_cq_object *cq_object) { int status = 0; /* Nothing should reference this CQ at this point. */ ASSERT(atomic_read(&cq_object->ref_count) == 0); /* Send fwcmd to destroy the CQ. */ status = be_function_ring_destroy(cq_object->parent_function, cq_object->cq_id, FWCMD_RING_TYPE_CQ, NULL, NULL, NULL, NULL); ASSERT(status == 0); /* Remove reference if this is an eventable CQ. */ if (cq_object->eq_object) { atomic_dec(&cq_object->eq_object->ref_count); list_del(&cq_object->cqlist_for_eq); } return BE_SUCCESS; }