ehea: queue management
Subsystems (2)
PATCH 3/7 ehea: queue management
drivers/net/ehea/ehea_qmr.c | 643 ++++++++++++++++++++++++++++++++++++++++++++ drivers/net/ehea/ehea_qmr.h | 367 +++++++++++++++++++++++++ 2 files changed, 1010 insertions(+)
drivers/net/ehea/ehea_qmr.c +643 -0
@@ -0,0 +1,643 @@ +/* +* linux/drivers/net/ehea/ehea_qmr.c +* +* eHEA ethernet device driver for IBM eServer System p +* +* (C) Copyright IBM Corp. 2006 +* +* Authors: +* Christoph Raisch <[email protected]> +* Jan-Bernd Themann <[email protected]> +* Thomas Klein <[email protected]> +* +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "ehea.h" +#include "ehea_phyp.h" +#include "ehea_qmr.h" + +static void *hw_qpageit_get_inc (struct hw_queue *queue) +{ +void *retvalue =hw_qeit_get (queue); + + queue->current_q_offset += queue->pagesize; +if (queue->current_q_offset > queue->queue_length) { + queue->current_q_offset -= queue->pagesize; + retvalue =NULL ; + } +else if ((((u64 ) retvalue) & (EHEA_PAGESIZE -1 )) !=0 ) { +ehea_error ("not on pageboundary" ); + retvalue =NULL ; + } +return retvalue; +} + +static int hw_queue_ctor (struct hw_queue *queue,const u32 nr_of_pages, +const u32 pagesize,const u32 qe_size) +{ +int pages_per_kpage =PAGE_SIZE / pagesize; +int i; + +if (pagesize >PAGE_SIZE ) { +ehea_error ("FATAL ERROR: pagesize=%x is greater than " +"kernel page size" , pagesize); +return 0 ; + } +if (!pages_per_kpage) { +ehea_error ("FATAL ERROR: invalid kernel page size. " +"pages_per_kpage=%x" , pages_per_kpage); +return 0 ; + } + queue->queue_length = nr_of_pages * pagesize; + queue->queue_pages =vmalloc (nr_of_pages *sizeof (void *)); +if (!queue->queue_pages) { +ehea_error ("no mem" ); +return 0 ; + } +memset (queue->queue_pages,0 , nr_of_pages *sizeof (void *)); +/* +* allocate pages for queue: +* outer loop allocates whole kernel pages (page aligned) and +* inner loop divides a kernel page into smaller hea queue pages +*/ + i =0 ; +while (i < nr_of_pages) { +int k; +u8 *kpage = (u8 *)get_zeroed_page (GFP_KERNEL ); +if (!kpage) + goto hw_queue_ctor_exit0; +for (k =0 ; k < pages_per_kpage && i < nr_of_pages; k++) { + (queue->queue_pages)[i] = (struct ehea_page *)kpage; + kpage += pagesize; + i++; + } + } + + queue->current_q_offset =0 ; + queue->qe_size = qe_size; + queue->pagesize = pagesize; + queue->toggle_state =1 ; +return 1 ; + +hw_queue_ctor_exit0: +for (i =0 ; i < nr_of_pages; i += pages_per_kpage) { +if (!(queue->queue_pages)[i]) +break ; +free_page ((unsigned long )(queue->queue_pages)[i]); + } +return 0 ; +} + +static void hw_queue_dtor (struct hw_queue *queue) +{ +int pages_per_kpage =PAGE_SIZE / queue->pagesize; +int i; +int nr_pages; + +if (!queue || !queue->queue_pages) +return ; + + nr_pages = queue->queue_length / queue->pagesize; + +for (i =0 ; i < nr_pages; i += pages_per_kpage) +free_page ((unsigned long )(queue->queue_pages)[i]); + +vfree (queue->queue_pages); +} + +struct ehea_cq *ehea_create_cq (struct ehea_adapter *adapter, +int nr_of_cqe,u64 eq_handle,u32 cq_token) +{ +struct ehea_cq *cq =NULL ; +struct h_epa epa; + +u64 *cq_handle_ref; +u32 act_nr_of_entries; +u32 act_pages; +u64 hret =H_HARDWARE ; +int hwret; +u32 counter; +void *vpage =NULL ; +u64 rpage =0 ; + + cq =kzalloc (sizeof (*cq),GFP_KERNEL ); +if (!cq) { +ehea_error ("no mem for cq" ); + goto create_cq_exit0; + } + + cq->attr.max_nr_of_cqes = nr_of_cqe; + cq->attr.cq_token = cq_token; + cq->attr.eq_handle = eq_handle; + + cq->adapter = adapter; + + cq_handle_ref = &cq->fw_handle; + act_nr_of_entries =0 ; + act_pages =0 ; + + hret =ehea_h_alloc_resource_cq (adapter->handle, cq, &cq->attr, + &cq->fw_handle, &cq->epas); +if (hret !=H_SUCCESS ) { +ehea_error ("alloc_resource_cq failed" ); + goto create_cq_exit1; + } + + hwret =hw_queue_ctor (&cq->hw_queue, cq->attr.nr_pages, +EHEA_PAGESIZE ,sizeof (struct ehea_cqe )); +if (!hwret) + goto create_cq_exit2; + +for (counter =0 ; counter < cq->attr.nr_pages; counter++) { + vpage =hw_qpageit_get_inc (&cq->hw_queue); +if (!vpage) { +ehea_error ("hw_qpageit_get_inc failed" ); + goto create_cq_exit3; + } + + rpage =virt_to_abs (vpage); + + hret =ehea_h_register_rpage_cq (adapter->handle, cq->fw_handle, +0 ,EHEA_CQ_REGISTER_ORIG , rpage, +1 , cq->epas.kernel); +if (hret <H_SUCCESS ) { +ehea_error ("register_rpage_cq failed ehea_cq=%p " +"hret=%lx counter=%i act_pages=%i" , + cq, hret, counter, cq->attr.nr_pages); + goto create_cq_exit3; + } + +if (counter == (cq->attr.nr_pages -1 )) { + vpage =hw_qpageit_get_inc (&cq->hw_queue); + +if ((hret !=H_SUCCESS ) || (vpage)) { +ehea_error ("registration of pages not " +"complete hret=%lx\n" , hret); + goto create_cq_exit3; + } + }else { +if ((hret !=H_PAGE_REGISTERED ) || (!vpage)) { +ehea_error ("CQ: registration of page failed " +"hret=%lx\n" , hret); + goto create_cq_exit3; + } + } + } + +hw_qeit_reset (&cq->hw_queue); + epa = cq->epas.kernel; +ehea_reset_cq_ep (cq); +ehea_reset_cq_n1 (cq); + +return cq; + +create_cq_exit3: +hw_queue_dtor (&cq->hw_queue); + +create_cq_exit2: +ehea_h_destroy_cq (adapter->handle, cq, cq->fw_handle, &cq->epas); + +create_cq_exit1: +kfree (cq); + +create_cq_exit0: +return NULL ; +} + +int ehea_destroy_cq (struct ehea_cq *cq) +{ +u64 adapter_handle; +u64 hret =H_HARDWARE ; + + adapter_handle = cq->adapter->handle; + +if (!cq) +return 0 ; + +/* deregister all previous registered pages */ + hret =ehea_h_destroy_cq (adapter_handle, cq, cq->fw_handle, &cq->epas); +if (hret !=H_SUCCESS ) { +ehea_error ("destroy CQ failed" ); +return -EINVAL ; + } + +hw_queue_dtor (&cq->hw_queue); +kfree (cq); + +return 0 ; +} + +struct ehea_eq *ehea_create_eq (struct ehea_adapter *adapter, +const enum ehea_eq_type type, +const u32 max_nr_of_eqes,const u8 eqe_gen) +{ +u64 hret =H_HARDWARE ; +int ret =0 ; +u32 i; +void *vpage =NULL ; +u64 rpage =0 ; +struct ehea_eq *eq; + + eq =kzalloc (sizeof (*eq),GFP_KERNEL ); +if (!eq) { +ehea_error ("no mem for eq" ); +return NULL ; + } + + eq->adapter = adapter; + eq->attr.type = type; + eq->attr.max_nr_of_eqes = max_nr_of_eqes; + eq->attr.eqe_gen = eqe_gen; +spin_lock_init (&eq->spinlock); + + hret =ehea_h_alloc_resource_eq (adapter->handle, + eq, &eq->attr, &eq->fw_handle); + +if (hret !=H_SUCCESS ) { +ehea_error ("alloc_resource_eq failed" ); + goto free_eq_mem; + } + + ret =hw_queue_ctor (&eq->hw_queue, eq->attr.nr_pages,EHEA_PAGESIZE , +sizeof (struct ehea_eqe )); +if (!ret) { +ehea_error ("can't allocate eq pages" ); + goto alloc_pages_failed; + } + +for (i =0 ; i < eq->attr.nr_pages; i++) { + vpage =hw_qpageit_get_inc (&eq->hw_queue); +if (!vpage) { +ehea_error ("hw_qpageit_get_inc failed" ); + hret =H_RESOURCE ; + goto register_page_failed; + } + + rpage =virt_to_abs (vpage); + + hret =ehea_h_register_rpage_eq (adapter->handle, eq->fw_handle, +0 ,EHEA_EQ_REGISTER_ORIG , rpage, +1 ); + +if (i == (eq->attr.nr_pages -1 )) { +/* last page */ + vpage =hw_qpageit_get_inc (&eq->hw_queue); +if ((hret !=H_SUCCESS ) || (vpage)) { + goto register_page_failed; + } + }else { +if ((hret !=H_PAGE_REGISTERED ) || (!vpage)) { + goto register_page_failed; + } + } + } + +hw_qeit_reset (&eq->hw_queue); +return eq; + +register_page_failed: +hw_queue_dtor (&eq->hw_queue); + +alloc_pages_failed: +ehea_h_destroy_eq (adapter->handle, eq, eq->fw_handle, &eq->epas); +free_eq_mem: +kfree (eq); + +return NULL ; +} + +struct ehea_eqe *ehea_poll_eq (struct ehea_eq *eq) +{ +struct ehea_eqe *eqe =NULL ; +unsigned long flags =0 ; + +spin_lock_irqsave (&eq->spinlock, flags); + eqe = (struct ehea_eqe *)hw_eqit_eq_get_inc_valid (&eq->hw_queue); +spin_unlock_irqrestore (&eq->spinlock, flags); + +return eqe; +} + +int ehea_destroy_eq (struct ehea_eq *eq) +{ +unsigned long flags =0 ; +u64 hret =H_HARDWARE ; + +if (!eq) +return 0 ; + +spin_lock_irqsave (&eq->spinlock, flags); + + hret =ehea_h_destroy_eq (eq->adapter->handle, eq, eq->fw_handle, + &eq->epas); +spin_unlock_irqrestore (&eq->spinlock, flags); + +if (hret !=H_SUCCESS ) { +ehea_error ("failed freeing EQ resources" ); +return -EINVAL ; + } + +hw_queue_dtor (&eq->hw_queue); +kfree (eq); + +return 0 ; +} + +/** +* allocates memory for a queue and registers pages in phyp +*/ +int ehea_qp_alloc_register (struct ehea_qp *qp,struct hw_queue *hw_queue, +int nr_pages,int wqe_size,int act_nr_sges, +struct ehea_adapter *adapter,int h_call_q_selector) +{ +u64 hret =H_HARDWARE ; +u64 rpage =0 ; +int ret =0 ; +int cnt =0 ; +void *vpage =NULL ; + + ret =hw_queue_ctor (hw_queue, + nr_pages,EHEA_PAGESIZE , wqe_size); +if (!ret) +return -ENOMEM ; + +for (cnt =0 ; cnt < nr_pages; cnt++) { + vpage =hw_qpageit_get_inc (hw_queue); +if (!vpage) { +ehea_error ("hw_qpageit_get_inc failed" ); + goto qp_alloc_register_exit0; + } + rpage =virt_to_abs (vpage); + + hret =ehea_h_register_rpage_qp (adapter->handle, qp->fw_handle, +0 , h_call_q_selector, rpage,1 , + qp->epas.kernel); +if (hret <H_SUCCESS ) { +ehea_error ("register_rpage_qp failed" ); + goto qp_alloc_register_exit0; + } + } +hw_qeit_reset (hw_queue); + +return 0 ; + +qp_alloc_register_exit0: +hw_queue_dtor (hw_queue); +return -EINVAL ; +} + +static inline u32 map_wqe_size (u8 wqe_enc_size) +{ +return 128 << wqe_enc_size; +} + +struct ehea_qp *ehea_create_qp (struct ehea_adapter *adapter, +u32 pd,struct ehea_qp_init_attr *init_attr) +{ +struct ehea_qp *qp; +u64 hret =H_HARDWARE ; + +u32 wqe_size_in_bytes_sq =0 ; +u32 wqe_size_in_bytes_rq1 =0 ; +u32 wqe_size_in_bytes_rq2 =0 ; +u32 wqe_size_in_bytes_rq3 =0 ; + +int ret =-1 ; + + qp =kzalloc (sizeof (*qp),GFP_KERNEL ); +if (!qp) { +ehea_error ("no mem for qp" ); +return NULL ; + } + + qp->adapter = adapter; + + hret =ehea_h_alloc_resource_qp (adapter->handle, qp, init_attr, pd, + &qp->fw_handle, &qp->epas); +if (hret !=H_SUCCESS ) { +ehea_error ("ehea_h_alloc_resource_qp failed" ); + goto create_qp_exit1; + } + + wqe_size_in_bytes_sq =map_wqe_size (init_attr->act_wqe_size_enc_sq); + wqe_size_in_bytes_rq1 =map_wqe_size (init_attr->act_wqe_size_enc_rq1); + wqe_size_in_bytes_rq2 =map_wqe_size (init_attr->act_wqe_size_enc_rq2); + wqe_size_in_bytes_rq3 =map_wqe_size (init_attr->act_wqe_size_enc_rq3); + + ret =ehea_qp_alloc_register (qp, &qp->hw_squeue, init_attr->nr_sq_pages, + wqe_size_in_bytes_sq, + init_attr->act_wqe_size_enc_sq, adapter, +0 ); +if (ret) { +ehea_error ("can't register for sq ret=%x" , ret); + goto create_qp_exit2; + } + + ret =ehea_qp_alloc_register (qp, &qp->hw_rqueue1, + init_attr->nr_rq1_pages, + wqe_size_in_bytes_rq1, + init_attr->act_wqe_size_enc_rq1, + adapter,1 ); +if (ret) { +ehea_error ("can't register for rq1 ret=%x" , ret); + goto create_qp_exit3; + } + +if (init_attr->rq_count >1 ) { + ret =ehea_qp_alloc_register (qp, &qp->hw_rqueue2, + init_attr->nr_rq2_pages, + wqe_size_in_bytes_rq2, + init_attr->act_wqe_size_enc_rq2, + adapter,2 ); +if (ret) { +ehea_error ("can't register for rq2 ret=%x" , ret); + goto create_qp_exit4; + } + } + +if (init_attr->rq_count >2 ) { + ret =ehea_qp_alloc_register (qp, &qp->hw_rqueue3, + init_attr->nr_rq3_pages, + wqe_size_in_bytes_rq3, + init_attr->act_wqe_size_enc_rq3, + adapter,3 ); +if (ret) { +ehea_error ("can't register for rq3 ret=%x" , ret); + goto create_qp_exit5; + } + } + + qp->init_attr = *init_attr; + +return qp; +create_qp_exit5: +hw_queue_dtor (&qp->hw_rqueue2); + +create_qp_exit4: +hw_queue_dtor (&qp->hw_rqueue1); + +create_qp_exit3: +hw_queue_dtor (&qp->hw_squeue); + +create_qp_exit2: + hret =ehea_h_destroy_qp (adapter->handle, qp, qp->fw_handle, &qp->epas); + +create_qp_exit1: +kfree (qp); +return NULL ; +} + +int ehea_destroy_qp (struct ehea_qp *qp) +{ +int ret =0 ; +u64 hret =H_HARDWARE ; +struct ehea_qp_init_attr *qp_attr = &qp->init_attr; + +if (!qp) +return 0 ; + + hret =ehea_h_destroy_qp (qp->adapter->handle, qp, qp->fw_handle, + &qp->epas); +if (hret !=H_SUCCESS ) { +ehea_error ("destroy QP failed" ); + ret = -EINVAL ; + } + +hw_queue_dtor (&qp->hw_squeue); +hw_queue_dtor (&qp->hw_rqueue1); + +if (qp_attr->rq_count >1 ) +hw_queue_dtor (&qp->hw_rqueue2); +if (qp_attr->rq_count >2 ) +hw_queue_dtor (&qp->hw_rqueue3); +kfree (qp); + +return ret; +} + +int ehea_reg_mr_adapter (struct ehea_adapter *adapter) +{ +int i =0 ; +int k =0 ; +u64 hret =H_HARDWARE ; +u64 start =KERNELBASE ; +u64 end = (u64 ) high_memory; +u64 nr_pages = (end - start) /PAGE_SIZE ; +u32 acc_ctrl =EHEA_MR_ACC_CTRL ; +u64 pt_abs =0 ; +u64 *pt; + + pt =kzalloc (PAGE_SIZE ,GFP_KERNEL ); +if (!pt) { +ehea_error ("no mem" ); +return -ENOMEM ; + } + pt_abs =virt_to_abs (pt); + + hret =ehea_h_alloc_resource_mr (adapter->handle, start, end - start, + acc_ctrl, adapter->pd, + &adapter->mr.handle, &adapter->mr.lkey); +if (hret !=H_SUCCESS ) { +ehea_error ("alloc_mr failed" ); +return -EINVAL ; + } + + adapter->mr.vaddr =KERNELBASE ; + +while (nr_pages >0 ) { +if (nr_pages >1 ) { +u64 num_pages =min (nr_pages, (u64 )512 ); +for (i =0 ; i < num_pages; i++) + pt[i] =virt_to_abs ((void *)(((u64 )start) + + ((k++) * +PAGE_SIZE ))); + + hret =ehea_h_register_rpage_mr (adapter->handle, + adapter->mr.handle,0 , +0 , (u64 )pt_abs, + num_pages); + nr_pages -= num_pages; + }else { +u64 abs_adr =virt_to_abs ((void *)(((u64 )start) + + (k *PAGE_SIZE ))); + hret =ehea_h_register_rpage_mr (adapter->handle, + adapter->mr.handle,0 , +0 , abs_adr,1 ); + nr_pages--; + } + +if ((hret !=H_SUCCESS ) && (hret !=H_PAGE_REGISTERED )) { +ehea_h_free_resource_mr (adapter->handle, + adapter->mr.handle); +ehea_error ("free_resource_mr failed" ); +return -EINVAL ; + } + } + +if (hret !=H_SUCCESS ) { +ehea_h_free_resource_mr (adapter->handle, adapter->mr.handle); +ehea_error ("free_resource_mr failed for last page" ); +return -EINVAL ; + } +return 0 ; +} + +int ehea_reg_mr_pages (struct ehea_adapter *adapter, +struct ehea_mr *mr, +u64 start,u64 *pt,int nr_pages) +{ +u64 hret =H_HARDWARE ; +u32 acc_ctrl =EHEA_MR_ACC_CTRL ; + +u64 pt_abs =virt_to_abs (pt); +u64 first_page = pt[0 ]; + + hret =ehea_h_alloc_resource_mr (adapter->handle, start, +PAGE_SIZE * nr_pages, acc_ctrl, + adapter->pd, &mr->handle, &mr->lkey); +if (hret !=H_SUCCESS ) { +ehea_error ("alloc_resource_mr failed" ); +return -EINVAL ; + } + +if (nr_pages >1 ) + hret =ehea_h_register_rpage_mr (adapter->handle, mr->handle, +0 ,0 , (u64 )pt_abs, nr_pages); +else + hret =ehea_h_register_rpage_mr (adapter->handle, mr->handle, +0 ,0 , first_page,1 ); + +if (hret !=H_SUCCESS ) { +ehea_h_free_resource_mr (adapter->handle, mr->handle); +ehea_error ("free_resource_mr failed for last page" ); +return -EINVAL ; + } + mr->vaddr = start; +return 0 ; +} + +int ehea_dereg_mr_adapter (struct ehea_adapter *adapter) +{ +u64 hret =H_HARDWARE ; + + hret =ehea_h_free_resource_mr (adapter->handle, adapter->mr.handle); +if (hret !=H_SUCCESS ) { +return -EINVAL ; + } + +return 0 ; +}
drivers/net/ehea/ehea_qmr.h +367 -0
367 lines not on this page · raw message
drivers/net/ehea/ehea_qmr.c line 66
Quoted 2 lines
drivers/net/ehea/ehea_qmr.c +2
-0+ queue->queue_length = nr_of_pages * pagesize; + queue->queue_pages =vmalloc (nr_of_pages *sizeof (void *));
wow... is this really so large that it warrants a vmalloc()???
Quoted 5 lines · Arjan van de Ven
drivers/net/ehea/ehea_qmr.c line 66
Quoted 2 lines
drivers/net/ehea/ehea_qmr.c +2
-0+ queue->queue_length = nr_of_pages * pagesize; + queue->queue_pages =vmalloc (nr_of_pages *sizeof (void *));wow... is this really so large that it warrants a
vmalloc()???
Agreed: Replaced with kmalloc()
Regards
Thomas
Quoted 4 lines · Thomas Klein
Quoted 2 lines
wow... is this really so large that it warrants a
vmalloc()???Agreed: Replaced with
kmalloc()
My understanding from the previous discussion was that it actually is a multi-page power of two allocation, so the right choice might be __get_free_pages() instead of kmalloc, but it probably doesn't make much of a difference.
You should really do some measurements to see what the minimal queue sizes are that can get you optimal throughput.
Arnd <><
Quoted 4 lines
You should really do some measurements to see what the minimal queue sizes are that can get you optimal throughput.
Arnd <><
we did.
And as always in performance tuning... one size fits all unfortunately is not the correct answer.
Therefore we'll leave that open to the user as most other new ethernet driver did as well.
Regards . . . Christoph R
Quoted 2 lines · Christoph Raisch
And as always in performance tuning... one size fits all unfortunately is not the correct answer.
Ah, good. What is the maximum sensible value that you came up with?
Quoted 2 lines
Therefore we'll leave that open to the user as most other new ethernet driver did as well.
Sure. The interesting question is whether you want to allow users to set it to a value that is no longer sensible to do with __get_free_pages() and requires vmalloc().
Arnd <><
Quoted 8 lines · Thomas Klein
Quoted 5 lines · Arjan van de Ven
drivers/net/ehea/ehea_qmr.c line 66
Quoted 2 lines
drivers/net/ehea/ehea_qmr.c +2
-0+ queue->queue_length = nr_of_pages * pagesize; + queue->queue_pages =vmalloc (nr_of_pages *sizeof (void *));wow... is this really so large that it warrants a
vmalloc()???Agreed: Replaced with
kmalloc()
kzalloc() and you can remove the memset() as well.
Jörn
Signature · 4 lines
Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming. -- Rob Pike
drivers/net/ehea/ehea_qmr.c line 42
Quoted 6 lines · Jan-Bernd Themann
drivers/net/ehea/ehea_qmr.c +5
-0+if (queue->current_q_offset > queue->queue_length) { + queue->current_q_offset -= queue->pagesize; + retvalue =NULL ; + } +else if ((((u64 ) retvalue) & (EHEA_PAGESIZE -1 )) !=0 ) {
} else if (((u64) retvalue) & (EHEA_PAGESIZE-1)) {Review
Quoted 1 line
(quoted diff) +1
-0+ if (hret < H_SUCCESS) {
Do you have a reason to keep H_SUCCESS?
drivers/net/ehea/ehea_qmr.c line 525
Quoted 3 lines
drivers/net/ehea/ehea_qmr.c +3
-0+if (qp_attr->rq_count >1 ) +hw_queue_dtor (&qp->hw_rqueue2); +if (qp_attr->rq_count >2 )
Small amount of whitespace damage.
Jörn
Signature · 4 lines
Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface. -- Doug MacIlroy