1/* 2 * IBM eServer eHCA Infiniband device driver for Linux on POWER 3 * 4 * internal queue handling 5 * 6 * Authors: Waleri Fomin <fomin@de.ibm.com> 7 * Reinhard Ernst <rernst@de.ibm.com> 8 * Christoph Raisch <raisch@de.ibm.com> 9 * 10 * Copyright (c) 2005 IBM Corporation 11 * 12 * This source code is distributed under a dual license of GPL v2.0 and OpenIB 13 * BSD. 14 * 15 * OpenIB BSD License 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions are met: 19 * 20 * Redistributions of source code must retain the above copyright notice, this 21 * list of conditions and the following disclaimer. 22 * 23 * Redistributions in binary form must reproduce the above copyright notice, 24 * this list of conditions and the following disclaimer in the documentation 25 * and/or other materials 26 * provided with the distribution. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 29 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 32 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 35 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 36 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 * POSSIBILITY OF SUCH DAMAGE. 39 */ 40 41#include "ehca_tools.h" 42#include "ipz_pt_fn.h" 43 44void *ipz_qpageit_get_inc(struct ipz_queue *queue) 45{ 46 void *ret = ipz_qeit_get(queue); 47 queue->current_q_offset += queue->pagesize; 48 if (queue->current_q_offset > queue->queue_length) { 49 queue->current_q_offset -= queue->pagesize; 50 ret = NULL; 51 } 52 if (((u64)ret) % EHCA_PAGESIZE) { 53 ehca_gen_err("ERROR!! not at PAGE-Boundary"); 54 return NULL; 55 } 56 return ret; 57} 58 59void *ipz_qeit_eq_get_inc(struct ipz_queue *queue) 60{ 61 void *ret = ipz_qeit_get(queue); 62 u64 last_entry_in_q = queue->queue_length - queue->qe_size; 63 64 queue->current_q_offset += queue->qe_size; 65 if (queue->current_q_offset > last_entry_in_q) { 66 queue->current_q_offset = 0; 67 queue->toggle_state = (~queue->toggle_state) & 1; 68 } 69 70 return ret; 71} 72 73int ipz_queue_abs_to_offset(struct ipz_queue *queue, u64 addr, u64 *q_offset) 74{ 75 int i; 76 for (i = 0; i < queue->queue_length / queue->pagesize; i++) { 77 u64 page = (u64)virt_to_abs(queue->queue_pages[i]); 78 if (addr >= page && addr < page + queue->pagesize) { 79 *q_offset = addr - page + i * queue->pagesize; 80 return 0; 81 } 82 } 83 return -EINVAL; 84} 85 86int ipz_queue_ctor(struct ipz_queue *queue, 87 const u32 nr_of_pages, 88 const u32 pagesize, const u32 qe_size, const u32 nr_of_sg) 89{ 90 int pages_per_kpage = PAGE_SIZE >> EHCA_PAGESHIFT; 91 int f; 92 93 if (pagesize > PAGE_SIZE) { 94 ehca_gen_err("FATAL ERROR: pagesize=%x is greater " 95 "than kernel page size", pagesize); 96 return 0; 97 } 98 if (!pages_per_kpage) { 99 ehca_gen_err("FATAL ERROR: invalid kernel page size. " 100 "pages_per_kpage=%x", pages_per_kpage); 101 return 0; 102 } 103 queue->queue_length = nr_of_pages * pagesize; 104 queue->queue_pages = vmalloc(nr_of_pages * sizeof(void *)); 105 if (!queue->queue_pages) { 106 ehca_gen_err("ERROR!! didn't get the memory"); 107 return 0; 108 } 109 memset(queue->queue_pages, 0, nr_of_pages * sizeof(void *)); 110 /* 111 * allocate pages for queue: 112 * outer loop allocates whole kernel pages (page aligned) and 113 * inner loop divides a kernel page into smaller hca queue pages 114 */ 115 f = 0; 116 while (f < nr_of_pages) { 117 u8 *kpage = (u8*)get_zeroed_page(GFP_KERNEL); 118 int k; 119 if (!kpage) 120 goto ipz_queue_ctor_exit0; /*NOMEM*/ 121 for (k = 0; k < pages_per_kpage && f < nr_of_pages; k++) { 122 (queue->queue_pages)[f] = (struct ipz_page *)kpage; 123 kpage += EHCA_PAGESIZE; 124 f++; 125 } 126 } 127 128 queue->current_q_offset = 0; 129 queue->qe_size = qe_size; 130 queue->act_nr_of_sg = nr_of_sg; 131 queue->pagesize = pagesize; 132 queue->toggle_state = 1; 133 return 1; 134 135 ipz_queue_ctor_exit0: 136 ehca_gen_err("Couldn't get alloc pages queue=%p f=%x nr_of_pages=%x", 137 queue, f, nr_of_pages); 138 for (f = 0; f < nr_of_pages; f += pages_per_kpage) { 139 if (!(queue->queue_pages)[f]) 140 break; 141 free_page((unsigned long)(queue->queue_pages)[f]); 142 } 143 return 0; 144} 145 146int ipz_queue_dtor(struct ipz_queue *queue) 147{ 148 int pages_per_kpage = PAGE_SIZE >> EHCA_PAGESHIFT; 149 int g; 150 int nr_pages; 151 152 if (!queue || !queue->queue_pages) { 153 ehca_gen_dbg("queue or queue_pages is NULL"); 154 return 0; 155 } 156 nr_pages = queue->queue_length / queue->pagesize; 157 for (g = 0; g < nr_pages; g += pages_per_kpage) 158 free_page((unsigned long)(queue->queue_pages)[g]); 159 vfree(queue->queue_pages); 160 161 return 1; 162} 163