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 * All rights reserved. 13 * 14 * This source code is distributed under a dual license of GPL v2.0 and OpenIB 15 * BSD. 16 * 17 * OpenIB BSD License 18 * 19 * Redistribution and use in source and binary forms, with or without 20 * modification, are permitted provided that the following conditions are met: 21 * 22 * Redistributions of source code must retain the above copyright notice, this 23 * list of conditions and the following disclaimer. 24 * 25 * Redistributions in binary form must reproduce the above copyright notice, 26 * this list of conditions and the following disclaimer in the documentation 27 * and/or other materials 28 * provided with the distribution. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 31 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 34 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 37 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 38 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 40 * POSSIBILITY OF SUCH DAMAGE. 41 */ 42 43#ifndef __IPZ_PT_FN_H__ 44#define __IPZ_PT_FN_H__ 45 46#define EHCA_PAGESHIFT 12 47#define EHCA_PAGESIZE 4096UL 48#define EHCA_PAGEMASK (~(EHCA_PAGESIZE-1)) 49#define EHCA_PT_ENTRIES 512UL 50 51#include "ehca_tools.h" 52#include "ehca_qes.h" 53 54struct ehca_pd; 55struct ipz_small_queue_page; 56 57extern struct kmem_cache *small_qp_cache; 58 59/* struct generic ehca page */ 60struct ipz_page { 61 u8 entries[EHCA_PAGESIZE]; 62}; 63 64#define IPZ_SPAGE_PER_KPAGE (PAGE_SIZE / 512) 65 66struct ipz_small_queue_page { 67 unsigned long page; 68 unsigned long bitmap[IPZ_SPAGE_PER_KPAGE / BITS_PER_LONG]; 69 int fill; 70 void *mapped_addr; 71 u32 mmap_count; 72 struct list_head list; 73}; 74 75/* struct generic queue in linux kernel virtual memory (kv) */ 76struct ipz_queue { 77 u64 current_q_offset; /* current queue entry */ 78 79 struct ipz_page **queue_pages; /* array of pages belonging to queue */ 80 u32 qe_size; /* queue entry size */ 81 u32 act_nr_of_sg; 82 u32 queue_length; /* queue length allocated in bytes */ 83 u32 pagesize; 84 u32 toggle_state; /* toggle flag - per page */ 85 u32 offset; /* save offset within page for small_qp */ 86 struct ipz_small_queue_page *small_page; 87}; 88 89/* 90 * return current Queue Entry for a certain q_offset 91 * returns address (kv) of Queue Entry 92 */ 93static inline void *ipz_qeit_calc(struct ipz_queue *queue, u64 q_offset) 94{ 95 struct ipz_page *current_page; 96 if (q_offset >= queue->queue_length) 97 return NULL; 98 current_page = (queue->queue_pages)[q_offset >> EHCA_PAGESHIFT]; 99 return ¤t_page->entries[q_offset & (EHCA_PAGESIZE - 1)]; 100} 101 102/* 103 * return current Queue Entry 104 * returns address (kv) of Queue Entry 105 */ 106static inline void *ipz_qeit_get(struct ipz_queue *queue) 107{ 108 return ipz_qeit_calc(queue, queue->current_q_offset); 109} 110 111/* 112 * return current Queue Page , increment Queue Page iterator from 113 * page to page in struct ipz_queue, last increment will return 0! and 114 * NOT wrap 115 * returns address (kv) of Queue Page 116 * warning don't use in parallel with ipz_QE_get_inc() 117 */ 118void *ipz_qpageit_get_inc(struct ipz_queue *queue); 119 120/* 121 * return current Queue Entry, increment Queue Entry iterator by one 122 * step in struct ipz_queue, will wrap in ringbuffer 123 * returns address (kv) of Queue Entry BEFORE increment 124 * warning don't use in parallel with ipz_qpageit_get_inc() 125 */ 126static inline void *ipz_qeit_get_inc(struct ipz_queue *queue) 127{ 128 void *ret = ipz_qeit_get(queue); 129 queue->current_q_offset += queue->qe_size; 130 if (queue->current_q_offset >= queue->queue_length) { 131 queue->current_q_offset = 0; 132 /* toggle the valid flag */ 133 queue->toggle_state = (~queue->toggle_state) & 1; 134 } 135 136 return ret; 137} 138 139/* 140 * return a bool indicating whether current Queue Entry is valid 141 */ 142static inline int ipz_qeit_is_valid(struct ipz_queue *queue) 143{ 144 struct ehca_cqe *cqe = ipz_qeit_get(queue); 145 return ((cqe->cqe_flags >> 7) == (queue->toggle_state & 1)); 146} 147 148/* 149 * return current Queue Entry, increment Queue Entry iterator by one 150 * step in struct ipz_queue, will wrap in ringbuffer 151 * returns address (kv) of Queue Entry BEFORE increment 152 * returns 0 and does not increment, if wrong valid state 153 * warning don't use in parallel with ipz_qpageit_get_inc() 154 */ 155static inline void *ipz_qeit_get_inc_valid(struct ipz_queue *queue) 156{ 157 return ipz_qeit_is_valid(queue) ? ipz_qeit_get_inc(queue) : NULL; 158} 159 160/* 161 * returns and resets Queue Entry iterator 162 * returns address (kv) of first Queue Entry 163 */ 164static inline void *ipz_qeit_reset(struct ipz_queue *queue) 165{ 166 queue->current_q_offset = 0; 167 return ipz_qeit_get(queue); 168} 169 170/* 171 * return the q_offset corresponding to an absolute address 172 */ 173int ipz_queue_abs_to_offset(struct ipz_queue *queue, u64 addr, u64 *q_offset); 174 175/* 176 * return the next queue offset. don't modify the queue. 177 */ 178static inline u64 ipz_queue_advance_offset(struct ipz_queue *queue, u64 offset) 179{ 180 offset += queue->qe_size; 181 if (offset >= queue->queue_length) offset = 0; 182 return offset; 183} 184 185/* struct generic page table */ 186struct ipz_pt { 187 u64 entries[EHCA_PT_ENTRIES]; 188}; 189 190/* struct page table for a queue, only to be used in pf */ 191struct ipz_qpt { 192 /* queue page tables (kv), use u64 because we know the element length */ 193 u64 *qpts; 194 u32 n_qpts; 195 u32 n_ptes; /* number of page table entries */ 196 u64 *current_pte_addr; 197}; 198 199/* 200 * constructor for a ipz_queue_t, placement new for ipz_queue_t, 201 * new for all dependent datastructors 202 * all QP Tables are the same 203 * flow: 204 * allocate+pin queue 205 * see ipz_qpt_ctor() 206 * returns true if ok, false if out of memory 207 */ 208int ipz_queue_ctor(struct ehca_pd *pd, struct ipz_queue *queue, 209 const u32 nr_of_pages, const u32 pagesize, 210 const u32 qe_size, const u32 nr_of_sg, 211 int is_small); 212 213/* 214 * destructor for a ipz_queue_t 215 * -# free queue 216 * see ipz_queue_ctor() 217 * returns true if ok, false if queue was NULL-ptr of free failed 218 */ 219int ipz_queue_dtor(struct ehca_pd *pd, struct ipz_queue *queue); 220 221/* 222 * constructor for a ipz_qpt_t, 223 * placement new for struct ipz_queue, new for all dependent datastructors 224 * all QP Tables are the same, 225 * flow: 226 * -# allocate+pin queue 227 * -# initialise ptcb 228 * -# allocate+pin PTs 229 * -# link PTs to a ring, according to HCA Arch, set bit62 id needed 230 * -# the ring must have room for exactly nr_of_PTEs 231 * see ipz_qpt_ctor() 232 */ 233void ipz_qpt_ctor(struct ipz_qpt *qpt, 234 const u32 nr_of_qes, 235 const u32 pagesize, 236 const u32 qe_size, 237 const u8 lowbyte, const u8 toggle, 238 u32 * act_nr_of_QEs, u32 * act_nr_of_pages); 239 240/* 241 * return current Queue Entry, increment Queue Entry iterator by one 242 * step in struct ipz_queue, will wrap in ringbuffer 243 * returns address (kv) of Queue Entry BEFORE increment 244 * warning don't use in parallel with ipz_qpageit_get_inc() 245 * warning unpredictable results may occur if steps>act_nr_of_queue_entries 246 * fix EQ page problems 247 */ 248void *ipz_qeit_eq_get_inc(struct ipz_queue *queue); 249 250/* 251 * return current Event Queue Entry, increment Queue Entry iterator 252 * by one step in struct ipz_queue if valid, will wrap in ringbuffer 253 * returns address (kv) of Queue Entry BEFORE increment 254 * returns 0 and does not increment, if wrong valid state 255 * warning don't use in parallel with ipz_queue_QPageit_get_inc() 256 * warning unpredictable results may occur if steps>act_nr_of_queue_entries 257 */ 258static inline void *ipz_eqit_eq_get_inc_valid(struct ipz_queue *queue) 259{ 260 void *ret = ipz_qeit_get(queue); 261 u32 qe = *(u8 *)ret; 262 if ((qe >> 7) != (queue->toggle_state & 1)) 263 return NULL; 264 ipz_qeit_eq_get_inc(queue); /* this is a good one */ 265 return ret; 266} 267 268static inline void *ipz_eqit_eq_peek_valid(struct ipz_queue *queue) 269{ 270 void *ret = ipz_qeit_get(queue); 271 u32 qe = *(u8 *)ret; 272 if ((qe >> 7) != (queue->toggle_state & 1)) 273 return NULL; 274 return ret; 275} 276 277/* returns address (GX) of first queue entry */ 278static inline u64 ipz_qpt_get_firstpage(struct ipz_qpt *qpt) 279{ 280 return be64_to_cpu(qpt->qpts[0]); 281} 282 283/* returns address (kv) of first page of queue page table */ 284static inline void *ipz_qpt_get_qpt(struct ipz_qpt *qpt) 285{ 286 return qpt->qpts; 287} 288 289#endif /* __IPZ_PT_FN_H__ */ 290