Whamcloud - gitweb
* Landed portals:b_port_step as follows...
[fs/lustre-release.git] / lnet / klnds / viblnd / vibnal_sa.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * Copyright (C) 2004 Cluster File Systems, Inc.
5  *   Author: Frank Zago <fzago@systemfabricworks.com>
6  *
7  *   This file is part of Lustre, http://www.lustre.org.
8  *
9  *   Lustre is free software; you can redistribute it and/or
10  *   modify it under the terms of version 2 of the GNU General Public
11  *   License as published by the Free Software Foundation.
12  *
13  *   Lustre is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with Lustre; if not, write to the Free Software
20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  */
23
24 #include "vibnal.h"
25
26 /*--------------------------------------------------------------------------*/
27
28 struct sa_request *alloc_sa_request(void)
29 {
30         struct sa_request *request;
31         gsi_dtgrm_t *dtgrm;
32         vv_return_t retval;
33
34         PORTAL_ALLOC(request, sizeof(*request));
35         if (request == NULL)
36                 return NULL;
37         
38         retval = gsi_dtgrm_pool_get(kibnal_data.gsi_pool_handle, &dtgrm);
39         if (retval) {
40                 CERROR("cannot get a datagram: %d\n", retval);
41                 PORTAL_FREE(request, sizeof(*request));
42                 return NULL;
43         }
44
45         memset(request, 0, sizeof(*request));
46
47         request->dtgrm_req = dtgrm;
48         request->retry = GSI_RETRY;    /* retry the request up to 10 times */
49
50         return request;
51 }
52
53 void free_sa_request(struct sa_request *request)
54 {
55         if (request) {
56                 if (request->dtgrm_req) {
57                         gsi_dtgrm_pool_put(request->dtgrm_req); 
58                 }
59
60                 if (request->dtgrm_resp) {
61                         gsi_dtgrm_pool_put(request->dtgrm_resp);
62                 }
63
64                 PORTAL_FREE(request, sizeof(*request));
65         }
66 }
67
68 /*--------------------------------------------------------------------------*/
69
70 static void complete_sa_request(struct sa_request *request)
71 {
72         if (request->callback) {
73                 request->callback(request);
74         } else {
75                 complete(&request->signal);
76         }
77 }
78
79 static void
80 sa_request_timeout_handler(unsigned long context)
81 {
82         struct sa_request *request = (struct sa_request *)context;
83         int ret;
84         vv_return_t retval;
85
86         if (request->retry--) {
87                 /* Resend */
88
89                 CDEBUG(D_NET, "timer expired for MAD TID "LPX64" - retrying (%d retry left)\n", request->mad->hdr.transact_id, request->retry);
90                 retval = gsi_post_send_dtgrm(kibnal_data.gsi_handle, request->dtgrm_req);
91                 if (retval) {
92                         CERROR("gsi_post_send_dtgrm failed: %d\n", retval);
93                         ret = -EIO;
94                 } else {
95
96                         /* restart the timer */
97                         request->timer.expires = jiffies + (HZ * GSI_TIMEOUT);
98                         add_timer(&request->timer);
99                         
100                         ret = 0;
101                 }
102         } else {
103                 CDEBUG(D_NET, "timer expired for MAD TID "LPX64" - no more retry\n", request->mad->hdr.transact_id);
104                 ret = ETIMEDOUT;
105         }
106
107         if (ret) {
108                 request->status = ret;
109                 complete_sa_request(request);
110         }
111 }
112
113 /*--------------------------------------------------------------------------*/
114
115 /* Send a SA request */
116 int vibnal_start_sa_request(struct sa_request *request)
117 {
118         int ret;
119         vv_return_t vv_stat;
120         int retval;
121
122         CDEBUG (D_NET, "querying SA\n");
123
124         /* Put the request on the pending list and get a transaction ID. */
125         down(&kibnal_data.gsi_mutex);
126
127         list_add_tail(&request->list, &kibnal_data.gsi_pending);
128
129         up(&kibnal_data.gsi_mutex);
130
131         retval = gsi_post_send_dtgrm(kibnal_data.gsi_handle, request->dtgrm_req);
132         if (retval) {
133                 CERROR("gsi_post_send_dtgrm failed: %d\n", retval);
134                 return -EIO;
135         }
136
137         /* TODO: This might create a race condition if the response has
138          * already been received. */
139         init_timer(&request->timer);
140         request->timer.expires = jiffies + (HZ * GSI_TIMEOUT);
141         request->timer.data = (unsigned long)request;
142         request->timer.function = sa_request_timeout_handler;
143         add_timer(&request->timer);
144
145         CDEBUG(D_NET, "Posted MAD with TID= "LPX64"\n", request->mad->hdr.transact_id);
146         return 0;
147 }
148
149 /* Received a MAD */
150 void
151 vibnal_mad_received_cb(gsi_class_handle_t handle, void *context, gsi_dtgrm_t *dtgrm)
152 {
153         sa_mad_v2_t *mad = (sa_mad_v2_t *) dtgrm->mad;
154         ib_service_record_v2_t *sr = (ib_service_record_v2_t *) mad->payload;
155         struct list_head *this;
156         struct sa_request *request;
157
158         CDEBUG(D_NET, "Received new MAD\n");
159
160         /* Validate the MAD */
161         if (mad->hdr.base_ver != MAD_IB_BASE_VERSION ||
162                 mad->hdr.class != MAD_CLASS_SUBN_ADM ||
163         mad->hdr.class_ver != 2) {
164                 CDEBUG(D_NET, "ignoring MAD (base_ver=%x, class=%x, class_ver=%x)\n",
165                            mad->hdr.base_ver, mad->hdr.class, mad->hdr.class_ver);
166                 return;
167         }
168
169         /* We don't care about queries, only about responses */
170         if (mad->hdr.m.ms.r != 1) {
171                 CDEBUG(D_NET, "ignoring MAD (response=%d)\n", mad->hdr.m.ms.r);
172                 return;
173         }
174
175         /* We only care about service records and path records. */
176         if (mad->hdr.attrib_id != SA_SERVICE_RECORD &&
177                 mad->hdr.attrib_id != SA_PATH_RECORD) {
178                 CDEBUG(D_NET, "ignoring MAD (attrib_id=%x)\n", mad->hdr.attrib_id);
179                 return;
180         }
181
182         /* Find the MAD request in our list */
183         request = NULL;
184
185         down(&kibnal_data.gsi_mutex);
186
187         list_for_each(this, &kibnal_data.gsi_pending) {
188                 struct sa_request *_request = list_entry(this, struct sa_request, list);
189
190                 CDEBUG(D_NET, "Comparing pending MAD TID "LPX64" with incoming MAD TID "LPX64"\n",
191                            _request->mad->hdr.transact_id, mad->hdr.transact_id);
192
193                 if (_request->mad->hdr.transact_id == mad->hdr.transact_id) {
194                         CDEBUG(D_NET, "TIDs match\n");
195                         request = _request;
196                         break;
197                 }
198         }
199
200         if (request == NULL) {
201                 up(&kibnal_data.gsi_mutex);
202                 CDEBUG(D_NET, "ignoring MAD (TID = "LPX64"\n", mad->hdr.transact_id);
203                 return;
204         }
205
206         up(&kibnal_data.gsi_mutex);
207
208         /* Stop the timer and remove the request from the pending list of requests. */
209         del_timer_sync(&request->timer);
210
211         down(&kibnal_data.gsi_mutex);
212
213         list_del(&request->list);
214
215         up(&kibnal_data.gsi_mutex);
216
217         request->dtgrm_resp = dtgrm;
218
219         /* Depending on the response, update the status. This is not exact
220          * because a non-zero status is not always an error, but that
221          * should be good enough right now. */
222         /* TODO: fix. */
223         if (mad->hdr.u.ns.status.raw16) {
224                 CDEBUG(D_NET, "MAD response has bad status: %x\n", mad->hdr.u.ns.status.raw16);
225                 request->status = -EIO;
226         } else {
227                 request->status = 0;
228         }
229
230         CDEBUG(D_NET, "incoming MAD successfully processed (status is %d)\n", request->status);
231
232         complete_sa_request(request);
233 }
234
235 /* MAD send completion */
236 void
237 vibnal_mad_sent_cb(gsi_class_handle_t handle, void *context, gsi_dtgrm_t * dtgrm)
238 {
239         sa_mad_v2_t *mad = (sa_mad_v2_t *) dtgrm->mad;
240         
241         /* Don't do anything. We might have to resend the datagram later. */
242         CDEBUG(D_NET, "Datagram with TID "LPX64" sent.\n", mad->hdr.transact_id);
243 }
244
245 /* 
246  * method is SUBN_ADM_SET, SUBN_ADM_GET, SUBN_ADM_DELETE. Tables not supported.
247  * nid is the nid to advertize/query/unadvertize
248  * Note: dgid is in network order.
249  */
250 static void fill_pathrecord_request(struct sa_request *request, vv_gid_t dgid)
251 {
252         gsi_dtgrm_t *dtgrm = request->dtgrm_req;
253         sa_mad_v2_t *mad = (sa_mad_v2_t *) dtgrm->mad;
254         ib_path_record_v2_t *path = (ib_path_record_v2_t *) mad->payload;
255
256         memset(mad, 0, MAD_BLOCK_SIZE);
257
258         request->mad = mad;
259
260         dtgrm->rlid = kibnal_data.kib_port_attr.port_sma_address_info.sm_lid;
261         dtgrm->sl = kibnal_data.kib_port_attr.port_sma_address_info.service_level;
262
263         mad->hdr.base_ver = MAD_IB_BASE_VERSION;
264         mad->hdr.class = MAD_CLASS_SUBN_ADM;
265         mad->hdr.class_ver = 2;
266         mad->hdr.m.ms.method = SUBN_ADM_GET;
267                 mad->hdr.attrib_id = SA_PATH_RECORD; /* something(?) will swap that field */
268                 mad->hdr.attrib_modifier = 0xFFFFFFFF; /* and that one too? */
269
270                 /* Note: the transaction ID is set by the Voltaire stack if it is 0. */
271
272         /* TODO: these harcoded value to something better */
273         mad->payload_len = cpu_to_be32(0x40 /*header size*/ + 0x35 /* PathRecord size */);
274
275         mad->component_mask = cpu_to_be64(
276                                                                          (1 << 2) | /* DGID      */
277                                                                          (1 << 3) | /* SGID      */
278                                                                          (1 << 12)| /* numb_paths*/
279                                                                          (1 << 13)  /* P_key     */
280                                                                          );
281
282                 path->pkey = cpu_to_be16(kibnal_data.kib_port_pkey);
283                 path->sgid = kibnal_data.kib_port_gid;
284                 gid_swap(&path->sgid);
285                 path->dgid = dgid;              /* already in network order */
286                 path->numb_path = 1;
287 }
288
289 /* 
290  * Do a path record query
291  * If callback is NULL, the function is synchronous (and context is ignored).
292  * Note: dgid is in network order.
293  */
294 /* TODO: passing a request is a bit of a hack, but since this function
295  * is called under interrupt, we cannot allocate memory here :(. */
296 int kibnal_pathrecord_op(struct sa_request *request, vv_gid_t dgid, sa_request_cb_t callback, void *context)
297 {
298         int ret;
299
300         LASSERT (kibnal_data.kib_nid != PTL_NID_ANY);
301
302         fill_pathrecord_request(request, dgid);
303
304         if (callback) {
305                 request->callback = callback;
306                 request->context = context;
307         } else {
308                 init_completion(&request->signal);
309         }
310
311         ret = vibnal_start_sa_request(request);
312         if (ret) {
313                 CERROR("vibnal_send_sa failed: %d\n", ret);
314                 free_sa_request(request);
315         } else {
316                 if (callback) {
317                         /* Return. The callback will have to free the SA request. */
318                         ret = 0;
319                 } else {
320                         wait_for_completion(&request->signal);
321
322                         ret = request->status;
323
324                         if (ret != 0) {
325                                 CERROR ("Error %d in querying a path record\n", ret);
326                         }
327                         
328                         free_sa_request(request);
329                 }
330         }
331
332         return ret;
333 }