IB/ehca: Support small QP queues
[linux-2.6.git] / drivers / infiniband / hw / ehca / ipz_pt_fn.h
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
54 struct ehca_pd;
55 struct ipz_small_queue_page;
56
57 /* struct generic ehca page */
58 struct ipz_page {
59         u8 entries[EHCA_PAGESIZE];
60 };
61
62 #define IPZ_SPAGE_PER_KPAGE (PAGE_SIZE / 512)
63
64 struct ipz_small_queue_page {
65         unsigned long page;
66         unsigned long bitmap[IPZ_SPAGE_PER_KPAGE / BITS_PER_LONG];
67         int fill;
68         void *mapped_addr;
69         u32 mmap_count;
70         struct list_head list;
71 };
72
73 /* struct generic queue in linux kernel virtual memory (kv) */
74 struct ipz_queue {
75         u64 current_q_offset;   /* current queue entry */
76
77         struct ipz_page **queue_pages;  /* array of pages belonging to queue */
78         u32 qe_size;            /* queue entry size */
79         u32 act_nr_of_sg;
80         u32 queue_length;       /* queue length allocated in bytes */
81         u32 pagesize;
82         u32 toggle_state;       /* toggle flag - per page */
83         u32 offset; /* save offset within page for small_qp */
84         struct ipz_small_queue_page *small_page;
85 };
86
87 /*
88  * return current Queue Entry for a certain q_offset
89  * returns address (kv) of Queue Entry
90  */
91 static inline void *ipz_qeit_calc(struct ipz_queue *queue, u64 q_offset)
92 {
93         struct ipz_page *current_page;
94         if (q_offset >= queue->queue_length)
95                 return NULL;
96         current_page = (queue->queue_pages)[q_offset >> EHCA_PAGESHIFT];
97         return &current_page->entries[q_offset & (EHCA_PAGESIZE - 1)];
98 }
99
100 /*
101  * return current Queue Entry
102  * returns address (kv) of Queue Entry
103  */
104 static inline void *ipz_qeit_get(struct ipz_queue *queue)
105 {
106         return ipz_qeit_calc(queue, queue->current_q_offset);
107 }
108
109 /*
110  * return current Queue Page , increment Queue Page iterator from
111  * page to page in struct ipz_queue, last increment will return 0! and
112  * NOT wrap
113  * returns address (kv) of Queue Page
114  * warning don't use in parallel with ipz_QE_get_inc()
115  */
116 void *ipz_qpageit_get_inc(struct ipz_queue *queue);
117
118 /*
119  * return current Queue Entry, increment Queue Entry iterator by one
120  * step in struct ipz_queue, will wrap in ringbuffer
121  * returns address (kv) of Queue Entry BEFORE increment
122  * warning don't use in parallel with ipz_qpageit_get_inc()
123  */
124 static inline void *ipz_qeit_get_inc(struct ipz_queue *queue)
125 {
126         void *ret = ipz_qeit_get(queue);
127         queue->current_q_offset += queue->qe_size;
128         if (queue->current_q_offset >= queue->queue_length) {
129                 queue->current_q_offset = 0;
130                 /* toggle the valid flag */
131                 queue->toggle_state = (~queue->toggle_state) & 1;
132         }
133
134         return ret;
135 }
136
137 /*
138  * return a bool indicating whether current Queue Entry is valid
139  */
140 static inline int ipz_qeit_is_valid(struct ipz_queue *queue)
141 {
142         struct ehca_cqe *cqe = ipz_qeit_get(queue);
143         return ((cqe->cqe_flags >> 7) == (queue->toggle_state & 1));
144 }
145
146 /*
147  * return current Queue Entry, increment Queue Entry iterator by one
148  * step in struct ipz_queue, will wrap in ringbuffer
149  * returns address (kv) of Queue Entry BEFORE increment
150  * returns 0 and does not increment, if wrong valid state
151  * warning don't use in parallel with ipz_qpageit_get_inc()
152  */
153 static inline void *ipz_qeit_get_inc_valid(struct ipz_queue *queue)
154 {
155         return ipz_qeit_is_valid(queue) ? ipz_qeit_get_inc(queue) : NULL;
156 }
157
158 /*
159  * returns and resets Queue Entry iterator
160  * returns address (kv) of first Queue Entry
161  */
162 static inline void *ipz_qeit_reset(struct ipz_queue *queue)
163 {
164         queue->current_q_offset = 0;
165         return ipz_qeit_get(queue);
166 }
167
168 /*
169  * return the q_offset corresponding to an absolute address
170  */
171 int ipz_queue_abs_to_offset(struct ipz_queue *queue, u64 addr, u64 *q_offset);
172
173 /*
174  * return the next queue offset. don't modify the queue.
175  */
176 static inline u64 ipz_queue_advance_offset(struct ipz_queue *queue, u64 offset)
177 {
178         offset += queue->qe_size;
179         if (offset >= queue->queue_length) offset = 0;
180         return offset;
181 }
182
183 /* struct generic page table */
184 struct ipz_pt {
185         u64 entries[EHCA_PT_ENTRIES];
186 };
187
188 /* struct page table for a queue, only to be used in pf */
189 struct ipz_qpt {
190         /* queue page tables (kv), use u64 because we know the element length */
191         u64 *qpts;
192         u32 n_qpts;
193         u32 n_ptes;       /*  number of page table entries */
194         u64 *current_pte_addr;
195 };
196
197 /*
198  * constructor for a ipz_queue_t, placement new for ipz_queue_t,
199  * new for all dependent datastructors
200  * all QP Tables are the same
201  * flow:
202  *    allocate+pin queue
203  * see ipz_qpt_ctor()
204  * returns true if ok, false if out of memory
205  */
206 int ipz_queue_ctor(struct ehca_pd *pd, struct ipz_queue *queue,
207                    const u32 nr_of_pages, const u32 pagesize,
208                    const u32 qe_size, const u32 nr_of_sg,
209                    int is_small);
210
211 /*
212  * destructor for a ipz_queue_t
213  *  -# free queue
214  *  see ipz_queue_ctor()
215  *  returns true if ok, false if queue was NULL-ptr of free failed
216  */
217 int ipz_queue_dtor(struct ehca_pd *pd, struct ipz_queue *queue);
218
219 /*
220  * constructor for a ipz_qpt_t,
221  * placement new for struct ipz_queue, new for all dependent datastructors
222  * all QP Tables are the same,
223  * flow:
224  * -# allocate+pin queue
225  * -# initialise ptcb
226  * -# allocate+pin PTs
227  * -# link PTs to a ring, according to HCA Arch, set bit62 id needed
228  * -# the ring must have room for exactly nr_of_PTEs
229  * see ipz_qpt_ctor()
230  */
231 void ipz_qpt_ctor(struct ipz_qpt *qpt,
232                   const u32 nr_of_qes,
233                   const u32 pagesize,
234                   const u32 qe_size,
235                   const u8 lowbyte, const u8 toggle,
236                   u32 * act_nr_of_QEs, u32 * act_nr_of_pages);
237
238 /*
239  * return current Queue Entry, increment Queue Entry iterator by one
240  * step in struct ipz_queue, will wrap in ringbuffer
241  * returns address (kv) of Queue Entry BEFORE increment
242  * warning don't use in parallel with ipz_qpageit_get_inc()
243  * warning unpredictable results may occur if steps>act_nr_of_queue_entries
244  * fix EQ page problems
245  */
246 void *ipz_qeit_eq_get_inc(struct ipz_queue *queue);
247
248 /*
249  * return current Event Queue Entry, increment Queue Entry iterator
250  * by one step in struct ipz_queue if valid, will wrap in ringbuffer
251  * returns address (kv) of Queue Entry BEFORE increment
252  * returns 0 and does not increment, if wrong valid state
253  * warning don't use in parallel with ipz_queue_QPageit_get_inc()
254  * warning unpredictable results may occur if steps>act_nr_of_queue_entries
255  */
256 static inline void *ipz_eqit_eq_get_inc_valid(struct ipz_queue *queue)
257 {
258         void *ret = ipz_qeit_get(queue);
259         u32 qe = *(u8 *)ret;
260         if ((qe >> 7) != (queue->toggle_state & 1))
261                 return NULL;
262         ipz_qeit_eq_get_inc(queue); /* this is a good one */
263         return ret;
264 }
265
266 static inline void *ipz_eqit_eq_peek_valid(struct ipz_queue *queue)
267 {
268         void *ret = ipz_qeit_get(queue);
269         u32 qe = *(u8 *)ret;
270         if ((qe >> 7) != (queue->toggle_state & 1))
271                 return NULL;
272         return ret;
273 }
274
275 /* returns address (GX) of first queue entry */
276 static inline u64 ipz_qpt_get_firstpage(struct ipz_qpt *qpt)
277 {
278         return be64_to_cpu(qpt->qpts[0]);
279 }
280
281 /* returns address (kv) of first page of queue page table */
282 static inline void *ipz_qpt_get_qpt(struct ipz_qpt *qpt)
283 {
284         return qpt->qpts;
285 }
286
287 #endif                          /* __IPZ_PT_FN_H__ */