diff options
author | Heiko J Schick <schickhj.ibm.com> | 2006-09-22 18:22:22 -0400 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2006-09-22 18:22:22 -0400 |
commit | fab97220c9e409a98b1956ba677ddd2dd43b0b95 (patch) | |
tree | f4fd0456b758a651eb7e774c550641e177a9b333 /drivers/infiniband/hw/ehca/ehca_av.c | |
parent | ded7f1a16d50527359be02f8b04f9ba56bc923e6 (diff) |
IB/ehca: Add driver for IBM eHCA InfiniBand adapters
Add a driver for IBM GX bus InfiniBand adapters, which are usable with
some pSeries/System p systems.
Signed-off-by: Heiko J Schick <schickhj.ibm.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
Diffstat (limited to 'drivers/infiniband/hw/ehca/ehca_av.c')
-rw-r--r-- | drivers/infiniband/hw/ehca/ehca_av.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/drivers/infiniband/hw/ehca/ehca_av.c b/drivers/infiniband/hw/ehca/ehca_av.c new file mode 100644 index 000000000000..3bac197f9014 --- /dev/null +++ b/drivers/infiniband/hw/ehca/ehca_av.c | |||
@@ -0,0 +1,271 @@ | |||
1 | /* | ||
2 | * IBM eServer eHCA Infiniband device driver for Linux on POWER | ||
3 | * | ||
4 | * adress vector functions | ||
5 | * | ||
6 | * Authors: Hoang-Nam Nguyen <hnguyen@de.ibm.com> | ||
7 | * Khadija Souissi <souissik@de.ibm.com> | ||
8 | * Reinhard Ernst <rernst@de.ibm.com> | ||
9 | * Christoph Raisch <raisch@de.ibm.com> | ||
10 | * | ||
11 | * Copyright (c) 2005 IBM Corporation | ||
12 | * | ||
13 | * All rights reserved. | ||
14 | * | ||
15 | * This source code is distributed under a dual license of GPL v2.0 and OpenIB | ||
16 | * BSD. | ||
17 | * | ||
18 | * OpenIB BSD License | ||
19 | * | ||
20 | * Redistribution and use in source and binary forms, with or without | ||
21 | * modification, are permitted provided that the following conditions are met: | ||
22 | * | ||
23 | * Redistributions of source code must retain the above copyright notice, this | ||
24 | * list of conditions and the following disclaimer. | ||
25 | * | ||
26 | * Redistributions in binary form must reproduce the above copyright notice, | ||
27 | * this list of conditions and the following disclaimer in the documentation | ||
28 | * and/or other materials | ||
29 | * provided with the distribution. | ||
30 | * | ||
31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
32 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
33 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
34 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
35 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
36 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
37 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
38 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
39 | * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
40 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
41 | * POSSIBILITY OF SUCH DAMAGE. | ||
42 | */ | ||
43 | |||
44 | |||
45 | #include <asm/current.h> | ||
46 | |||
47 | #include "ehca_tools.h" | ||
48 | #include "ehca_iverbs.h" | ||
49 | #include "hcp_if.h" | ||
50 | |||
51 | static struct kmem_cache *av_cache; | ||
52 | |||
53 | struct ib_ah *ehca_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr) | ||
54 | { | ||
55 | int ret; | ||
56 | struct ehca_av *av; | ||
57 | struct ehca_shca *shca = container_of(pd->device, struct ehca_shca, | ||
58 | ib_device); | ||
59 | |||
60 | av = kmem_cache_alloc(av_cache, SLAB_KERNEL); | ||
61 | if (!av) { | ||
62 | ehca_err(pd->device, "Out of memory pd=%p ah_attr=%p", | ||
63 | pd, ah_attr); | ||
64 | return ERR_PTR(-ENOMEM); | ||
65 | } | ||
66 | |||
67 | av->av.sl = ah_attr->sl; | ||
68 | av->av.dlid = ah_attr->dlid; | ||
69 | av->av.slid_path_bits = ah_attr->src_path_bits; | ||
70 | |||
71 | if (ehca_static_rate < 0) { | ||
72 | int ah_mult = ib_rate_to_mult(ah_attr->static_rate); | ||
73 | int ehca_mult = | ||
74 | ib_rate_to_mult(shca->sport[ah_attr->port_num].rate ); | ||
75 | |||
76 | if (ah_mult >= ehca_mult) | ||
77 | av->av.ipd = 0; | ||
78 | else | ||
79 | av->av.ipd = (ah_mult > 0) ? | ||
80 | ((ehca_mult - 1) / ah_mult) : 0; | ||
81 | } else | ||
82 | av->av.ipd = ehca_static_rate; | ||
83 | |||
84 | av->av.lnh = ah_attr->ah_flags; | ||
85 | av->av.grh.word_0 = EHCA_BMASK_SET(GRH_IPVERSION_MASK, 6); | ||
86 | av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_TCLASS_MASK, | ||
87 | ah_attr->grh.traffic_class); | ||
88 | av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_FLOWLABEL_MASK, | ||
89 | ah_attr->grh.flow_label); | ||
90 | av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_HOPLIMIT_MASK, | ||
91 | ah_attr->grh.hop_limit); | ||
92 | av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_NEXTHEADER_MASK, 0x1B); | ||
93 | /* set sgid in grh.word_1 */ | ||
94 | if (ah_attr->ah_flags & IB_AH_GRH) { | ||
95 | int rc; | ||
96 | struct ib_port_attr port_attr; | ||
97 | union ib_gid gid; | ||
98 | memset(&port_attr, 0, sizeof(port_attr)); | ||
99 | rc = ehca_query_port(pd->device, ah_attr->port_num, | ||
100 | &port_attr); | ||
101 | if (rc) { /* invalid port number */ | ||
102 | ret = -EINVAL; | ||
103 | ehca_err(pd->device, "Invalid port number " | ||
104 | "ehca_query_port() returned %x " | ||
105 | "pd=%p ah_attr=%p", rc, pd, ah_attr); | ||
106 | goto create_ah_exit1; | ||
107 | } | ||
108 | memset(&gid, 0, sizeof(gid)); | ||
109 | rc = ehca_query_gid(pd->device, | ||
110 | ah_attr->port_num, | ||
111 | ah_attr->grh.sgid_index, &gid); | ||
112 | if (rc) { | ||
113 | ret = -EINVAL; | ||
114 | ehca_err(pd->device, "Failed to retrieve sgid " | ||
115 | "ehca_query_gid() returned %x " | ||
116 | "pd=%p ah_attr=%p", rc, pd, ah_attr); | ||
117 | goto create_ah_exit1; | ||
118 | } | ||
119 | memcpy(&av->av.grh.word_1, &gid, sizeof(gid)); | ||
120 | } | ||
121 | /* for the time being we use a hard coded PMTU of 2048 Bytes */ | ||
122 | av->av.pmtu = 4; | ||
123 | |||
124 | /* dgid comes in grh.word_3 */ | ||
125 | memcpy(&av->av.grh.word_3, &ah_attr->grh.dgid, | ||
126 | sizeof(ah_attr->grh.dgid)); | ||
127 | |||
128 | return &av->ib_ah; | ||
129 | |||
130 | create_ah_exit1: | ||
131 | kmem_cache_free(av_cache, av); | ||
132 | |||
133 | return ERR_PTR(ret); | ||
134 | } | ||
135 | |||
136 | int ehca_modify_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr) | ||
137 | { | ||
138 | struct ehca_av *av; | ||
139 | struct ehca_ud_av new_ehca_av; | ||
140 | struct ehca_pd *my_pd = container_of(ah->pd, struct ehca_pd, ib_pd); | ||
141 | u32 cur_pid = current->tgid; | ||
142 | |||
143 | if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context && | ||
144 | my_pd->ownpid != cur_pid) { | ||
145 | ehca_err(ah->device, "Invalid caller pid=%x ownpid=%x", | ||
146 | cur_pid, my_pd->ownpid); | ||
147 | return -EINVAL; | ||
148 | } | ||
149 | |||
150 | memset(&new_ehca_av, 0, sizeof(new_ehca_av)); | ||
151 | new_ehca_av.sl = ah_attr->sl; | ||
152 | new_ehca_av.dlid = ah_attr->dlid; | ||
153 | new_ehca_av.slid_path_bits = ah_attr->src_path_bits; | ||
154 | new_ehca_av.ipd = ah_attr->static_rate; | ||
155 | new_ehca_av.lnh = EHCA_BMASK_SET(GRH_FLAG_MASK, | ||
156 | (ah_attr->ah_flags & IB_AH_GRH) > 0); | ||
157 | new_ehca_av.grh.word_0 = EHCA_BMASK_SET(GRH_TCLASS_MASK, | ||
158 | ah_attr->grh.traffic_class); | ||
159 | new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_FLOWLABEL_MASK, | ||
160 | ah_attr->grh.flow_label); | ||
161 | new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_HOPLIMIT_MASK, | ||
162 | ah_attr->grh.hop_limit); | ||
163 | new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_NEXTHEADER_MASK, 0x1b); | ||
164 | |||
165 | /* set sgid in grh.word_1 */ | ||
166 | if (ah_attr->ah_flags & IB_AH_GRH) { | ||
167 | int rc; | ||
168 | struct ib_port_attr port_attr; | ||
169 | union ib_gid gid; | ||
170 | memset(&port_attr, 0, sizeof(port_attr)); | ||
171 | rc = ehca_query_port(ah->device, ah_attr->port_num, | ||
172 | &port_attr); | ||
173 | if (rc) { /* invalid port number */ | ||
174 | ehca_err(ah->device, "Invalid port number " | ||
175 | "ehca_query_port() returned %x " | ||
176 | "ah=%p ah_attr=%p port_num=%x", | ||
177 | rc, ah, ah_attr, ah_attr->port_num); | ||
178 | return -EINVAL; | ||
179 | } | ||
180 | memset(&gid, 0, sizeof(gid)); | ||
181 | rc = ehca_query_gid(ah->device, | ||
182 | ah_attr->port_num, | ||
183 | ah_attr->grh.sgid_index, &gid); | ||
184 | if (rc) { | ||
185 | ehca_err(ah->device, "Failed to retrieve sgid " | ||
186 | "ehca_query_gid() returned %x " | ||
187 | "ah=%p ah_attr=%p port_num=%x " | ||
188 | "sgid_index=%x", | ||
189 | rc, ah, ah_attr, ah_attr->port_num, | ||
190 | ah_attr->grh.sgid_index); | ||
191 | return -EINVAL; | ||
192 | } | ||
193 | memcpy(&new_ehca_av.grh.word_1, &gid, sizeof(gid)); | ||
194 | } | ||
195 | |||
196 | new_ehca_av.pmtu = 4; /* see also comment in create_ah() */ | ||
197 | |||
198 | memcpy(&new_ehca_av.grh.word_3, &ah_attr->grh.dgid, | ||
199 | sizeof(ah_attr->grh.dgid)); | ||
200 | |||
201 | av = container_of(ah, struct ehca_av, ib_ah); | ||
202 | av->av = new_ehca_av; | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | int ehca_query_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr) | ||
208 | { | ||
209 | struct ehca_av *av = container_of(ah, struct ehca_av, ib_ah); | ||
210 | struct ehca_pd *my_pd = container_of(ah->pd, struct ehca_pd, ib_pd); | ||
211 | u32 cur_pid = current->tgid; | ||
212 | |||
213 | if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context && | ||
214 | my_pd->ownpid != cur_pid) { | ||
215 | ehca_err(ah->device, "Invalid caller pid=%x ownpid=%x", | ||
216 | cur_pid, my_pd->ownpid); | ||
217 | return -EINVAL; | ||
218 | } | ||
219 | |||
220 | memcpy(&ah_attr->grh.dgid, &av->av.grh.word_3, | ||
221 | sizeof(ah_attr->grh.dgid)); | ||
222 | ah_attr->sl = av->av.sl; | ||
223 | |||
224 | ah_attr->dlid = av->av.dlid; | ||
225 | |||
226 | ah_attr->src_path_bits = av->av.slid_path_bits; | ||
227 | ah_attr->static_rate = av->av.ipd; | ||
228 | ah_attr->ah_flags = EHCA_BMASK_GET(GRH_FLAG_MASK, av->av.lnh); | ||
229 | ah_attr->grh.traffic_class = EHCA_BMASK_GET(GRH_TCLASS_MASK, | ||
230 | av->av.grh.word_0); | ||
231 | ah_attr->grh.hop_limit = EHCA_BMASK_GET(GRH_HOPLIMIT_MASK, | ||
232 | av->av.grh.word_0); | ||
233 | ah_attr->grh.flow_label = EHCA_BMASK_GET(GRH_FLOWLABEL_MASK, | ||
234 | av->av.grh.word_0); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | int ehca_destroy_ah(struct ib_ah *ah) | ||
240 | { | ||
241 | struct ehca_pd *my_pd = container_of(ah->pd, struct ehca_pd, ib_pd); | ||
242 | u32 cur_pid = current->tgid; | ||
243 | |||
244 | if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context && | ||
245 | my_pd->ownpid != cur_pid) { | ||
246 | ehca_err(ah->device, "Invalid caller pid=%x ownpid=%x", | ||
247 | cur_pid, my_pd->ownpid); | ||
248 | return -EINVAL; | ||
249 | } | ||
250 | |||
251 | kmem_cache_free(av_cache, container_of(ah, struct ehca_av, ib_ah)); | ||
252 | |||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | int ehca_init_av_cache(void) | ||
257 | { | ||
258 | av_cache = kmem_cache_create("ehca_cache_av", | ||
259 | sizeof(struct ehca_av), 0, | ||
260 | SLAB_HWCACHE_ALIGN, | ||
261 | NULL, NULL); | ||
262 | if (!av_cache) | ||
263 | return -ENOMEM; | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | void ehca_cleanup_av_cache(void) | ||
268 | { | ||
269 | if (av_cache) | ||
270 | kmem_cache_destroy(av_cache); | ||
271 | } | ||