Heiko J Schick | fab9722 | 2006-09-22 15:22:22 -0700 | [diff] [blame] | 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 | |
| 44 | void *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 | |
| 59 | void *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 | |
Hoang-Nam Nguyen | 2771e9e | 2006-11-20 23:54:12 +0100 | [diff] [blame] | 73 | int 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 | |
Heiko J Schick | fab9722 | 2006-09-22 15:22:22 -0700 | [diff] [blame] | 86 | int 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 | |
| 146 | int 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 | } |