sa_query.c 33 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
/*
 * Copyright (c) 2004 Topspin Communications.  All rights reserved.
3
 * Copyright (c) 2005 Voltaire, Inc.  All rights reserved.
4
 * Copyright (c) 2006 Intel Corporation.  All rights reserved.
Linus Torvalds's avatar
Linus Torvalds committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/random.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/kref.h>
#include <linux/idr.h>
Tim Schmielau's avatar
Tim Schmielau committed
44
#include <linux/workqueue.h>
45
#include <uapi/linux/if_ether.h>
46
#include <rdma/ib_pack.h>
Sean Hefty's avatar
Sean Hefty committed
47
#include <rdma/ib_cache.h>
48
#include "sa.h"
Linus Torvalds's avatar
Linus Torvalds committed
49
50
51
52
53
54
55
56

MODULE_AUTHOR("Roland Dreier");
MODULE_DESCRIPTION("InfiniBand subnet administration query support");
MODULE_LICENSE("Dual BSD/GPL");

struct ib_sa_sm_ah {
	struct ib_ah        *ah;
	struct kref          ref;
57
	u16		     pkey_index;
58
	u8		     src_path_mask;
Linus Torvalds's avatar
Linus Torvalds committed
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
};

struct ib_sa_port {
	struct ib_mad_agent *agent;
	struct ib_sa_sm_ah  *sm_ah;
	struct work_struct   update_task;
	spinlock_t           ah_lock;
	u8                   port_num;
};

struct ib_sa_device {
	int                     start_port, end_port;
	struct ib_event_handler event_handler;
	struct ib_sa_port port[0];
};

struct ib_sa_query {
	void (*callback)(struct ib_sa_query *, int, struct ib_sa_mad *);
	void (*release)(struct ib_sa_query *);
78
	struct ib_sa_client    *client;
79
80
81
82
	struct ib_sa_port      *port;
	struct ib_mad_send_buf *mad_buf;
	struct ib_sa_sm_ah     *sm_ah;
	int			id;
Linus Torvalds's avatar
Linus Torvalds committed
83
84
};

85
86
87
88
89
90
struct ib_sa_service_query {
	void (*callback)(int, struct ib_sa_service_rec *, void *);
	void *context;
	struct ib_sa_query sa_query;
};

Linus Torvalds's avatar
Linus Torvalds committed
91
92
93
94
95
96
struct ib_sa_path_query {
	void (*callback)(int, struct ib_sa_path_rec *, void *);
	void *context;
	struct ib_sa_query sa_query;
};

97
98
99
100
101
102
struct ib_sa_guidinfo_query {
	void (*callback)(int, struct ib_sa_guidinfo_rec *, void *);
	void *context;
	struct ib_sa_query sa_query;
};

Linus Torvalds's avatar
Linus Torvalds committed
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
struct ib_sa_mcmember_query {
	void (*callback)(int, struct ib_sa_mcmember_rec *, void *);
	void *context;
	struct ib_sa_query sa_query;
};

static void ib_sa_add_one(struct ib_device *device);
static void ib_sa_remove_one(struct ib_device *device);

static struct ib_client sa_client = {
	.name   = "sa",
	.add    = ib_sa_add_one,
	.remove = ib_sa_remove_one
};

118
static DEFINE_SPINLOCK(idr_lock);
Linus Torvalds's avatar
Linus Torvalds committed
119
120
static DEFINE_IDR(query_idr);

121
static DEFINE_SPINLOCK(tid_lock);
Linus Torvalds's avatar
Linus Torvalds committed
122
123
124
125
126
127
128
129
static u32 tid;

#define PATH_REC_FIELD(field) \
	.struct_offset_bytes = offsetof(struct ib_sa_path_rec, field),		\
	.struct_size_bytes   = sizeof ((struct ib_sa_path_rec *) 0)->field,	\
	.field_name          = "sa_path_rec:" #field

static const struct ib_field path_rec_table[] = {
130
	{ PATH_REC_FIELD(service_id),
Linus Torvalds's avatar
Linus Torvalds committed
131
132
	  .offset_words = 0,
	  .offset_bits  = 0,
133
	  .size_bits    = 64 },
Linus Torvalds's avatar
Linus Torvalds committed
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
	{ PATH_REC_FIELD(dgid),
	  .offset_words = 2,
	  .offset_bits  = 0,
	  .size_bits    = 128 },
	{ PATH_REC_FIELD(sgid),
	  .offset_words = 6,
	  .offset_bits  = 0,
	  .size_bits    = 128 },
	{ PATH_REC_FIELD(dlid),
	  .offset_words = 10,
	  .offset_bits  = 0,
	  .size_bits    = 16 },
	{ PATH_REC_FIELD(slid),
	  .offset_words = 10,
	  .offset_bits  = 16,
	  .size_bits    = 16 },
	{ PATH_REC_FIELD(raw_traffic),
	  .offset_words = 11,
	  .offset_bits  = 0,
	  .size_bits    = 1 },
	{ RESERVED,
	  .offset_words = 11,
	  .offset_bits  = 1,
	  .size_bits    = 3 },
	{ PATH_REC_FIELD(flow_label),
	  .offset_words = 11,
	  .offset_bits  = 4,
	  .size_bits    = 20 },
	{ PATH_REC_FIELD(hop_limit),
	  .offset_words = 11,
	  .offset_bits  = 24,
	  .size_bits    = 8 },
	{ PATH_REC_FIELD(traffic_class),
	  .offset_words = 12,
	  .offset_bits  = 0,
	  .size_bits    = 8 },
	{ PATH_REC_FIELD(reversible),
	  .offset_words = 12,
	  .offset_bits  = 8,
	  .size_bits    = 1 },
	{ PATH_REC_FIELD(numb_path),
	  .offset_words = 12,
	  .offset_bits  = 9,
	  .size_bits    = 7 },
	{ PATH_REC_FIELD(pkey),
	  .offset_words = 12,
	  .offset_bits  = 16,
	  .size_bits    = 16 },
182
	{ PATH_REC_FIELD(qos_class),
Linus Torvalds's avatar
Linus Torvalds committed
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
	  .offset_words = 13,
	  .offset_bits  = 0,
	  .size_bits    = 12 },
	{ PATH_REC_FIELD(sl),
	  .offset_words = 13,
	  .offset_bits  = 12,
	  .size_bits    = 4 },
	{ PATH_REC_FIELD(mtu_selector),
	  .offset_words = 13,
	  .offset_bits  = 16,
	  .size_bits    = 2 },
	{ PATH_REC_FIELD(mtu),
	  .offset_words = 13,
	  .offset_bits  = 18,
	  .size_bits    = 6 },
	{ PATH_REC_FIELD(rate_selector),
	  .offset_words = 13,
	  .offset_bits  = 24,
	  .size_bits    = 2 },
	{ PATH_REC_FIELD(rate),
	  .offset_words = 13,
	  .offset_bits  = 26,
	  .size_bits    = 6 },
	{ PATH_REC_FIELD(packet_life_time_selector),
	  .offset_words = 14,
	  .offset_bits  = 0,
	  .size_bits    = 2 },
	{ PATH_REC_FIELD(packet_life_time),
	  .offset_words = 14,
	  .offset_bits  = 2,
	  .size_bits    = 6 },
	{ PATH_REC_FIELD(preference),
	  .offset_words = 14,
	  .offset_bits  = 8,
	  .size_bits    = 8 },
	{ RESERVED,
	  .offset_words = 14,
	  .offset_bits  = 16,
	  .size_bits    = 48 },
};

#define MCMEMBER_REC_FIELD(field) \
	.struct_offset_bytes = offsetof(struct ib_sa_mcmember_rec, field),	\
	.struct_size_bytes   = sizeof ((struct ib_sa_mcmember_rec *) 0)->field,	\
	.field_name          = "sa_mcmember_rec:" #field

static const struct ib_field mcmember_rec_table[] = {
	{ MCMEMBER_REC_FIELD(mgid),
	  .offset_words = 0,
	  .offset_bits  = 0,
	  .size_bits    = 128 },
	{ MCMEMBER_REC_FIELD(port_gid),
	  .offset_words = 4,
	  .offset_bits  = 0,
	  .size_bits    = 128 },
	{ MCMEMBER_REC_FIELD(qkey),
	  .offset_words = 8,
	  .offset_bits  = 0,
	  .size_bits    = 32 },
	{ MCMEMBER_REC_FIELD(mlid),
	  .offset_words = 9,
	  .offset_bits  = 0,
	  .size_bits    = 16 },
	{ MCMEMBER_REC_FIELD(mtu_selector),
	  .offset_words = 9,
	  .offset_bits  = 16,
	  .size_bits    = 2 },
	{ MCMEMBER_REC_FIELD(mtu),
	  .offset_words = 9,
	  .offset_bits  = 18,
	  .size_bits    = 6 },
	{ MCMEMBER_REC_FIELD(traffic_class),
	  .offset_words = 9,
	  .offset_bits  = 24,
	  .size_bits    = 8 },
	{ MCMEMBER_REC_FIELD(pkey),
	  .offset_words = 10,
	  .offset_bits  = 0,
	  .size_bits    = 16 },
	{ MCMEMBER_REC_FIELD(rate_selector),
	  .offset_words = 10,
	  .offset_bits  = 16,
	  .size_bits    = 2 },
	{ MCMEMBER_REC_FIELD(rate),
	  .offset_words = 10,
	  .offset_bits  = 18,
	  .size_bits    = 6 },
	{ MCMEMBER_REC_FIELD(packet_life_time_selector),
	  .offset_words = 10,
	  .offset_bits  = 24,
	  .size_bits    = 2 },
	{ MCMEMBER_REC_FIELD(packet_life_time),
	  .offset_words = 10,
	  .offset_bits  = 26,
	  .size_bits    = 6 },
	{ MCMEMBER_REC_FIELD(sl),
	  .offset_words = 11,
	  .offset_bits  = 0,
	  .size_bits    = 4 },
	{ MCMEMBER_REC_FIELD(flow_label),
	  .offset_words = 11,
	  .offset_bits  = 4,
	  .size_bits    = 20 },
	{ MCMEMBER_REC_FIELD(hop_limit),
	  .offset_words = 11,
	  .offset_bits  = 24,
	  .size_bits    = 8 },
	{ MCMEMBER_REC_FIELD(scope),
	  .offset_words = 12,
	  .offset_bits  = 0,
	  .size_bits    = 4 },
	{ MCMEMBER_REC_FIELD(join_state),
	  .offset_words = 12,
	  .offset_bits  = 4,
	  .size_bits    = 4 },
	{ MCMEMBER_REC_FIELD(proxy_join),
	  .offset_words = 12,
	  .offset_bits  = 8,
	  .size_bits    = 1 },
	{ RESERVED,
	  .offset_words = 12,
	  .offset_bits  = 9,
	  .size_bits    = 23 },
};

308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
#define SERVICE_REC_FIELD(field) \
	.struct_offset_bytes = offsetof(struct ib_sa_service_rec, field),	\
	.struct_size_bytes   = sizeof ((struct ib_sa_service_rec *) 0)->field,	\
	.field_name          = "sa_service_rec:" #field

static const struct ib_field service_rec_table[] = {
	{ SERVICE_REC_FIELD(id),
	  .offset_words = 0,
	  .offset_bits  = 0,
	  .size_bits    = 64 },
	{ SERVICE_REC_FIELD(gid),
	  .offset_words = 2,
	  .offset_bits  = 0,
	  .size_bits    = 128 },
	{ SERVICE_REC_FIELD(pkey),
	  .offset_words = 6,
	  .offset_bits  = 0,
	  .size_bits    = 16 },
	{ SERVICE_REC_FIELD(lease),
	  .offset_words = 7,
	  .offset_bits  = 0,
	  .size_bits    = 32 },
	{ SERVICE_REC_FIELD(key),
	  .offset_words = 8,
	  .offset_bits  = 0,
	  .size_bits    = 128 },
	{ SERVICE_REC_FIELD(name),
	  .offset_words = 12,
	  .offset_bits  = 0,
	  .size_bits    = 64*8 },
	{ SERVICE_REC_FIELD(data8),
	  .offset_words = 28,
	  .offset_bits  = 0,
	  .size_bits    = 16*8 },
	{ SERVICE_REC_FIELD(data16),
	  .offset_words = 32,
	  .offset_bits  = 0,
	  .size_bits    = 8*16 },
	{ SERVICE_REC_FIELD(data32),
	  .offset_words = 36,
	  .offset_bits  = 0,
	  .size_bits    = 4*32 },
	{ SERVICE_REC_FIELD(data64),
	  .offset_words = 40,
	  .offset_bits  = 0,
	  .size_bits    = 2*64 },
};

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
#define GUIDINFO_REC_FIELD(field) \
	.struct_offset_bytes = offsetof(struct ib_sa_guidinfo_rec, field),	\
	.struct_size_bytes   = sizeof((struct ib_sa_guidinfo_rec *) 0)->field,	\
	.field_name          = "sa_guidinfo_rec:" #field

static const struct ib_field guidinfo_rec_table[] = {
	{ GUIDINFO_REC_FIELD(lid),
	  .offset_words = 0,
	  .offset_bits  = 0,
	  .size_bits    = 16 },
	{ GUIDINFO_REC_FIELD(block_num),
	  .offset_words = 0,
	  .offset_bits  = 16,
	  .size_bits    = 8 },
	{ GUIDINFO_REC_FIELD(res1),
	  .offset_words = 0,
	  .offset_bits  = 24,
	  .size_bits    = 8 },
	{ GUIDINFO_REC_FIELD(res2),
	  .offset_words = 1,
	  .offset_bits  = 0,
	  .size_bits    = 32 },
	{ GUIDINFO_REC_FIELD(guid_info_list),
	  .offset_words = 2,
	  .offset_bits  = 0,
	  .size_bits    = 512 },
};

Linus Torvalds's avatar
Linus Torvalds committed
384
385
386
387
388
389
390
391
static void free_sm_ah(struct kref *kref)
{
	struct ib_sa_sm_ah *sm_ah = container_of(kref, struct ib_sa_sm_ah, ref);

	ib_destroy_ah(sm_ah->ah);
	kfree(sm_ah);
}

David Howells's avatar
David Howells committed
392
static void update_sm_ah(struct work_struct *work)
Linus Torvalds's avatar
Linus Torvalds committed
393
{
David Howells's avatar
David Howells committed
394
395
	struct ib_sa_port *port =
		container_of(work, struct ib_sa_port, update_task);
396
	struct ib_sa_sm_ah *new_ah;
Linus Torvalds's avatar
Linus Torvalds committed
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
	struct ib_port_attr port_attr;
	struct ib_ah_attr   ah_attr;

	if (ib_query_port(port->agent->device, port->port_num, &port_attr)) {
		printk(KERN_WARNING "Couldn't query port\n");
		return;
	}

	new_ah = kmalloc(sizeof *new_ah, GFP_KERNEL);
	if (!new_ah) {
		printk(KERN_WARNING "Couldn't allocate new SM AH\n");
		return;
	}

	kref_init(&new_ah->ref);
412
	new_ah->src_path_mask = (1 << port_attr.lmc) - 1;
Linus Torvalds's avatar
Linus Torvalds committed
413

414
415
	new_ah->pkey_index = 0;
	if (ib_find_pkey(port->agent->device, port->port_num,
416
			 IB_DEFAULT_PKEY_FULL, &new_ah->pkey_index))
417
418
		printk(KERN_ERR "Couldn't find index for default PKey\n");

Linus Torvalds's avatar
Linus Torvalds committed
419
420
421
422
423
424
425
426
427
428
429
430
431
	memset(&ah_attr, 0, sizeof ah_attr);
	ah_attr.dlid     = port_attr.sm_lid;
	ah_attr.sl       = port_attr.sm_sl;
	ah_attr.port_num = port->port_num;

	new_ah->ah = ib_create_ah(port->agent->qp->pd, &ah_attr);
	if (IS_ERR(new_ah->ah)) {
		printk(KERN_WARNING "Couldn't create new SM AH\n");
		kfree(new_ah);
		return;
	}

	spin_lock_irq(&port->ah_lock);
432
433
	if (port->sm_ah)
		kref_put(&port->sm_ah->ref, free_sm_ah);
Linus Torvalds's avatar
Linus Torvalds committed
434
435
436
437
438
439
440
441
442
443
444
	port->sm_ah = new_ah;
	spin_unlock_irq(&port->ah_lock);

}

static void ib_sa_event(struct ib_event_handler *handler, struct ib_event *event)
{
	if (event->event == IB_EVENT_PORT_ERR    ||
	    event->event == IB_EVENT_PORT_ACTIVE ||
	    event->event == IB_EVENT_LID_CHANGE  ||
	    event->event == IB_EVENT_PKEY_CHANGE ||
445
446
	    event->event == IB_EVENT_SM_CHANGE   ||
	    event->event == IB_EVENT_CLIENT_REREGISTER) {
447
448
449
450
451
452
		unsigned long flags;
		struct ib_sa_device *sa_dev =
			container_of(handler, typeof(*sa_dev), event_handler);
		struct ib_sa_port *port =
			&sa_dev->port[event->element.port_num - sa_dev->start_port];

453
		if (WARN_ON(!rdma_cap_ib_sa(handler->device, port->port_num)))
454
455
			return;

456
457
458
459
460
		spin_lock_irqsave(&port->ah_lock, flags);
		if (port->sm_ah)
			kref_put(&port->sm_ah->ref, free_sm_ah);
		port->sm_ah = NULL;
		spin_unlock_irqrestore(&port->ah_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
461

Tejun Heo's avatar
Tejun Heo committed
462
		queue_work(ib_wq, &sa_dev->port[event->element.port_num -
Linus Torvalds's avatar
Linus Torvalds committed
463
464
465
466
					    sa_dev->start_port].update_task);
	}
}

467
468
469
470
471
472
473
474
475
476
477
478
479
480
void ib_sa_register_client(struct ib_sa_client *client)
{
	atomic_set(&client->users, 1);
	init_completion(&client->comp);
}
EXPORT_SYMBOL(ib_sa_register_client);

void ib_sa_unregister_client(struct ib_sa_client *client)
{
	ib_sa_client_put(client);
	wait_for_completion(&client->comp);
}
EXPORT_SYMBOL(ib_sa_unregister_client);

Linus Torvalds's avatar
Linus Torvalds committed
481
482
483
484
485
486
487
488
489
490
491
492
493
/**
 * ib_sa_cancel_query - try to cancel an SA query
 * @id:ID of query to cancel
 * @query:query pointer to cancel
 *
 * Try to cancel an SA query.  If the id and query don't match up or
 * the query has already completed, nothing is done.  Otherwise the
 * query is canceled and will complete with a status of -EINTR.
 */
void ib_sa_cancel_query(int id, struct ib_sa_query *query)
{
	unsigned long flags;
	struct ib_mad_agent *agent;
494
	struct ib_mad_send_buf *mad_buf;
Linus Torvalds's avatar
Linus Torvalds committed
495
496
497
498
499
500
501

	spin_lock_irqsave(&idr_lock, flags);
	if (idr_find(&query_idr, id) != query) {
		spin_unlock_irqrestore(&idr_lock, flags);
		return;
	}
	agent = query->port->agent;
502
	mad_buf = query->mad_buf;
Linus Torvalds's avatar
Linus Torvalds committed
503
504
	spin_unlock_irqrestore(&idr_lock, flags);

505
	ib_cancel_mad(agent, mad_buf);
Linus Torvalds's avatar
Linus Torvalds committed
506
507
508
}
EXPORT_SYMBOL(ib_sa_cancel_query);

509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
static u8 get_src_path_mask(struct ib_device *device, u8 port_num)
{
	struct ib_sa_device *sa_dev;
	struct ib_sa_port   *port;
	unsigned long flags;
	u8 src_path_mask;

	sa_dev = ib_get_client_data(device, &sa_client);
	if (!sa_dev)
		return 0x7f;

	port  = &sa_dev->port[port_num - sa_dev->start_port];
	spin_lock_irqsave(&port->ah_lock, flags);
	src_path_mask = port->sm_ah ? port->sm_ah->src_path_mask : 0x7f;
	spin_unlock_irqrestore(&port->ah_lock, flags);

	return src_path_mask;
}

Sean Hefty's avatar
Sean Hefty committed
528
529
530
531
532
int ib_init_ah_from_path(struct ib_device *device, u8 port_num,
			 struct ib_sa_path_rec *rec, struct ib_ah_attr *ah_attr)
{
	int ret;
	u16 gid_index;
533
	int force_grh;
Sean Hefty's avatar
Sean Hefty committed
534
535
536
537

	memset(ah_attr, 0, sizeof *ah_attr);
	ah_attr->dlid = be16_to_cpu(rec->dlid);
	ah_attr->sl = rec->sl;
538
539
	ah_attr->src_path_bits = be16_to_cpu(rec->slid) &
				 get_src_path_mask(device, port_num);
Sean Hefty's avatar
Sean Hefty committed
540
	ah_attr->port_num = port_num;
541
	ah_attr->static_rate = rec->rate;
Sean Hefty's avatar
Sean Hefty committed
542

543
	force_grh = rdma_cap_eth_ah(device, port_num);
544
545

	if (rec->hop_limit > 1 || force_grh) {
Sean Hefty's avatar
Sean Hefty committed
546
547
548
549
550
551
552
553
		ah_attr->ah_flags = IB_AH_GRH;
		ah_attr->grh.dgid = rec->dgid;

		ret = ib_find_cached_gid(device, &rec->sgid, &port_num,
					 &gid_index);
		if (ret)
			return ret;

Sean Hefty's avatar
Sean Hefty committed
554
555
556
		ah_attr->grh.sgid_index    = gid_index;
		ah_attr->grh.flow_label    = be32_to_cpu(rec->flow_label);
		ah_attr->grh.hop_limit     = rec->hop_limit;
Sean Hefty's avatar
Sean Hefty committed
557
558
		ah_attr->grh.traffic_class = rec->traffic_class;
	}
559
560
561
562
563
564
565
	if (force_grh) {
		memcpy(ah_attr->dmac, rec->dmac, ETH_ALEN);
		ah_attr->vlan_id = rec->vlan_id;
	} else {
		ah_attr->vlan_id = 0xffff;
	}

Sean Hefty's avatar
Sean Hefty committed
566
567
568
569
	return 0;
}
EXPORT_SYMBOL(ib_init_ah_from_path);

570
571
572
573
574
static int alloc_mad(struct ib_sa_query *query, gfp_t gfp_mask)
{
	unsigned long flags;

	spin_lock_irqsave(&query->port->ah_lock, flags);
575
576
577
578
	if (!query->port->sm_ah) {
		spin_unlock_irqrestore(&query->port->ah_lock, flags);
		return -EAGAIN;
	}
579
580
581
582
583
584
585
586
	kref_get(&query->port->sm_ah->ref);
	query->sm_ah = query->port->sm_ah;
	spin_unlock_irqrestore(&query->port->ah_lock, flags);

	query->mad_buf = ib_create_send_mad(query->port->agent, 1,
					    query->sm_ah->pkey_index,
					    0, IB_MGMT_SA_HDR, IB_MGMT_SA_DATA,
					    gfp_mask);
Ali Ayoub's avatar
Ali Ayoub committed
587
	if (IS_ERR(query->mad_buf)) {
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
		kref_put(&query->sm_ah->ref, free_sm_ah);
		return -ENOMEM;
	}

	query->mad_buf->ah = query->sm_ah->ah;

	return 0;
}

static void free_mad(struct ib_sa_query *query)
{
	ib_free_send_mad(query->mad_buf);
	kref_put(&query->sm_ah->ref, free_sm_ah);
}

Linus Torvalds's avatar
Linus Torvalds committed
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
static void init_mad(struct ib_sa_mad *mad, struct ib_mad_agent *agent)
{
	unsigned long flags;

	memset(mad, 0, sizeof *mad);

	mad->mad_hdr.base_version  = IB_MGMT_BASE_VERSION;
	mad->mad_hdr.mgmt_class    = IB_MGMT_CLASS_SUBN_ADM;
	mad->mad_hdr.class_version = IB_SA_CLASS_VERSION;

	spin_lock_irqsave(&tid_lock, flags);
	mad->mad_hdr.tid           =
		cpu_to_be64(((u64) agent->hi_tid) << 32 | tid++);
	spin_unlock_irqrestore(&tid_lock, flags);
}

619
static int send_mad(struct ib_sa_query *query, int timeout_ms, gfp_t gfp_mask)
Linus Torvalds's avatar
Linus Torvalds committed
620
{
621
	bool preload = !!(gfp_mask & __GFP_WAIT);
Linus Torvalds's avatar
Linus Torvalds committed
622
	unsigned long flags;
623
	int ret, id;
Linus Torvalds's avatar
Linus Torvalds committed
624

Tejun Heo's avatar
Tejun Heo committed
625
626
	if (preload)
		idr_preload(gfp_mask);
Linus Torvalds's avatar
Linus Torvalds committed
627
	spin_lock_irqsave(&idr_lock, flags);
Tejun Heo's avatar
Tejun Heo committed
628
629
630

	id = idr_alloc(&query_idr, query, 0, 0, GFP_NOWAIT);

Linus Torvalds's avatar
Linus Torvalds committed
631
	spin_unlock_irqrestore(&idr_lock, flags);
Tejun Heo's avatar
Tejun Heo committed
632
633
634
635
	if (preload)
		idr_preload_end();
	if (id < 0)
		return id;
Linus Torvalds's avatar
Linus Torvalds committed
636

637
638
639
	query->mad_buf->timeout_ms  = timeout_ms;
	query->mad_buf->context[0] = query;
	query->id = id;
Linus Torvalds's avatar
Linus Torvalds committed
640

641
	ret = ib_post_send_mad(query->mad_buf, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
642
643
	if (ret) {
		spin_lock_irqsave(&idr_lock, flags);
644
		idr_remove(&query_idr, id);
Linus Torvalds's avatar
Linus Torvalds committed
645
646
647
		spin_unlock_irqrestore(&idr_lock, flags);
	}

648
649
650
	/*
	 * It's not safe to dereference query any more, because the
	 * send may already have completed and freed the query in
651
	 * another context.
652
	 */
653
	return ret ? ret : id;
Linus Torvalds's avatar
Linus Torvalds committed
654
655
}

656
657
658
659
660
661
void ib_sa_unpack_path(void *attribute, struct ib_sa_path_rec *rec)
{
	ib_unpack(path_rec_table, ARRAY_SIZE(path_rec_table), attribute, rec);
}
EXPORT_SYMBOL(ib_sa_unpack_path);

662
663
664
665
666
667
void ib_sa_pack_path(struct ib_sa_path_rec *rec, void *attribute)
{
	ib_pack(path_rec_table, ARRAY_SIZE(path_rec_table), rec, attribute);
}
EXPORT_SYMBOL(ib_sa_pack_path);

Linus Torvalds's avatar
Linus Torvalds committed
668
669
670
671
672
673
674
675
676
677
678
679
static void ib_sa_path_rec_callback(struct ib_sa_query *sa_query,
				    int status,
				    struct ib_sa_mad *mad)
{
	struct ib_sa_path_query *query =
		container_of(sa_query, struct ib_sa_path_query, sa_query);

	if (mad) {
		struct ib_sa_path_rec rec;

		ib_unpack(path_rec_table, ARRAY_SIZE(path_rec_table),
			  mad->data, &rec);
680
681
682
		rec.vlan_id = 0xffff;
		memset(rec.dmac, 0, ETH_ALEN);
		memset(rec.smac, 0, ETH_ALEN);
Linus Torvalds's avatar
Linus Torvalds committed
683
684
685
686
687
688
689
690
691
692
693
694
		query->callback(status, &rec, query->context);
	} else
		query->callback(status, NULL, query->context);
}

static void ib_sa_path_rec_release(struct ib_sa_query *sa_query)
{
	kfree(container_of(sa_query, struct ib_sa_path_query, sa_query));
}

/**
 * ib_sa_path_rec_get - Start a Path get query
695
 * @client:SA client
Linus Torvalds's avatar
Linus Torvalds committed
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
 * @device:device to send query on
 * @port_num: port number to send query on
 * @rec:Path Record to send in query
 * @comp_mask:component mask to send in query
 * @timeout_ms:time to wait for response
 * @gfp_mask:GFP mask to use for internal allocations
 * @callback:function called when query completes, times out or is
 * canceled
 * @context:opaque user context passed to callback
 * @sa_query:query context, used to cancel query
 *
 * Send a Path Record Get query to the SA to look up a path.  The
 * callback function will be called when the query completes (or
 * fails); status is 0 for a successful response, -EINTR if the query
 * is canceled, -ETIMEDOUT is the query timed out, or -EIO if an error
 * occurred sending the query.  The resp parameter of the callback is
 * only valid if status is 0.
 *
 * If the return value of ib_sa_path_rec_get() is negative, it is an
 * error code.  Otherwise it is a query ID that can be used to cancel
 * the query.
 */
718
719
int ib_sa_path_rec_get(struct ib_sa_client *client,
		       struct ib_device *device, u8 port_num,
Linus Torvalds's avatar
Linus Torvalds committed
720
721
		       struct ib_sa_path_rec *rec,
		       ib_sa_comp_mask comp_mask,
722
		       int timeout_ms, gfp_t gfp_mask,
Linus Torvalds's avatar
Linus Torvalds committed
723
724
725
726
727
728
729
730
		       void (*callback)(int status,
					struct ib_sa_path_rec *resp,
					void *context),
		       void *context,
		       struct ib_sa_query **sa_query)
{
	struct ib_sa_path_query *query;
	struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
731
732
	struct ib_sa_port   *port;
	struct ib_mad_agent *agent;
733
	struct ib_sa_mad *mad;
Linus Torvalds's avatar
Linus Torvalds committed
734
735
	int ret;

736
737
738
739
740
741
	if (!sa_dev)
		return -ENODEV;

	port  = &sa_dev->port[port_num - sa_dev->start_port];
	agent = port->agent;

Linus Torvalds's avatar
Linus Torvalds committed
742
743
744
	query = kmalloc(sizeof *query, gfp_mask);
	if (!query)
		return -ENOMEM;
745

746
747
748
	query->sa_query.port     = port;
	ret = alloc_mad(&query->sa_query, gfp_mask);
	if (ret)
749
		goto err1;
Linus Torvalds's avatar
Linus Torvalds committed
750

751
752
753
754
	ib_sa_client_get(client);
	query->sa_query.client = client;
	query->callback        = callback;
	query->context         = context;
Linus Torvalds's avatar
Linus Torvalds committed
755

756
757
	mad = query->sa_query.mad_buf->mad;
	init_mad(mad, agent);
Linus Torvalds's avatar
Linus Torvalds committed
758

759
760
761
762
763
	query->sa_query.callback = callback ? ib_sa_path_rec_callback : NULL;
	query->sa_query.release  = ib_sa_path_rec_release;
	mad->mad_hdr.method	 = IB_MGMT_METHOD_GET;
	mad->mad_hdr.attr_id	 = cpu_to_be16(IB_SA_ATTR_PATH_REC);
	mad->sa_hdr.comp_mask	 = comp_mask;
Linus Torvalds's avatar
Linus Torvalds committed
764

765
	ib_pack(path_rec_table, ARRAY_SIZE(path_rec_table), rec, mad->data);
Linus Torvalds's avatar
Linus Torvalds committed
766
767

	*sa_query = &query->sa_query;
768

769
	ret = send_mad(&query->sa_query, timeout_ms, gfp_mask);
770
771
772
773
774
775
776
	if (ret < 0)
		goto err2;

	return ret;

err2:
	*sa_query = NULL;
777
	ib_sa_client_put(query->sa_query.client);
778
	free_mad(&query->sa_query);
Linus Torvalds's avatar
Linus Torvalds committed
779

780
781
err1:
	kfree(query);
782
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
783
784
785
}
EXPORT_SYMBOL(ib_sa_path_rec_get);

786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
static void ib_sa_service_rec_callback(struct ib_sa_query *sa_query,
				    int status,
				    struct ib_sa_mad *mad)
{
	struct ib_sa_service_query *query =
		container_of(sa_query, struct ib_sa_service_query, sa_query);

	if (mad) {
		struct ib_sa_service_rec rec;

		ib_unpack(service_rec_table, ARRAY_SIZE(service_rec_table),
			  mad->data, &rec);
		query->callback(status, &rec, query->context);
	} else
		query->callback(status, NULL, query->context);
}

static void ib_sa_service_rec_release(struct ib_sa_query *sa_query)
{
	kfree(container_of(sa_query, struct ib_sa_service_query, sa_query));
}

/**
 * ib_sa_service_rec_query - Start Service Record operation
810
 * @client:SA client
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
 * @device:device to send request on
 * @port_num: port number to send request on
 * @method:SA method - should be get, set, or delete
 * @rec:Service Record to send in request
 * @comp_mask:component mask to send in request
 * @timeout_ms:time to wait for response
 * @gfp_mask:GFP mask to use for internal allocations
 * @callback:function called when request completes, times out or is
 * canceled
 * @context:opaque user context passed to callback
 * @sa_query:request context, used to cancel request
 *
 * Send a Service Record set/get/delete to the SA to register,
 * unregister or query a service record.
 * The callback function will be called when the request completes (or
 * fails); status is 0 for a successful response, -EINTR if the query
 * is canceled, -ETIMEDOUT is the query timed out, or -EIO if an error
 * occurred sending the query.  The resp parameter of the callback is
 * only valid if status is 0.
 *
 * If the return value of ib_sa_service_rec_query() is negative, it is an
 * error code.  Otherwise it is a request ID that can be used to cancel
 * the query.
 */
835
836
int ib_sa_service_rec_query(struct ib_sa_client *client,
			    struct ib_device *device, u8 port_num, u8 method,
837
838
			    struct ib_sa_service_rec *rec,
			    ib_sa_comp_mask comp_mask,
839
			    int timeout_ms, gfp_t gfp_mask,
840
841
842
843
844
845
846
847
			    void (*callback)(int status,
					     struct ib_sa_service_rec *resp,
					     void *context),
			    void *context,
			    struct ib_sa_query **sa_query)
{
	struct ib_sa_service_query *query;
	struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
848
849
	struct ib_sa_port   *port;
	struct ib_mad_agent *agent;
850
	struct ib_sa_mad *mad;
851
852
	int ret;

853
854
855
856
857
858
	if (!sa_dev)
		return -ENODEV;

	port  = &sa_dev->port[port_num - sa_dev->start_port];
	agent = port->agent;

859
860
861
862
863
864
865
866
	if (method != IB_MGMT_METHOD_GET &&
	    method != IB_MGMT_METHOD_SET &&
	    method != IB_SA_METHOD_DELETE)
		return -EINVAL;

	query = kmalloc(sizeof *query, gfp_mask);
	if (!query)
		return -ENOMEM;
867

868
869
870
	query->sa_query.port     = port;
	ret = alloc_mad(&query->sa_query, gfp_mask);
	if (ret)
871
		goto err1;
872

873
874
875
876
	ib_sa_client_get(client);
	query->sa_query.client = client;
	query->callback        = callback;
	query->context         = context;
877

878
879
	mad = query->sa_query.mad_buf->mad;
	init_mad(mad, agent);
880

881
882
883
884
885
	query->sa_query.callback = callback ? ib_sa_service_rec_callback : NULL;
	query->sa_query.release  = ib_sa_service_rec_release;
	mad->mad_hdr.method	 = method;
	mad->mad_hdr.attr_id	 = cpu_to_be16(IB_SA_ATTR_SERVICE_REC);
	mad->sa_hdr.comp_mask	 = comp_mask;
886
887

	ib_pack(service_rec_table, ARRAY_SIZE(service_rec_table),
888
		rec, mad->data);
889
890
891

	*sa_query = &query->sa_query;

892
	ret = send_mad(&query->sa_query, timeout_ms, gfp_mask);
893
894
895
896
	if (ret < 0)
		goto err2;

	return ret;
897

898
899
err2:
	*sa_query = NULL;
900
	ib_sa_client_put(query->sa_query.client);
901
	free_mad(&query->sa_query);
902
903
904

err1:
	kfree(query);
905
906
907
908
	return ret;
}
EXPORT_SYMBOL(ib_sa_service_rec_query);

Linus Torvalds's avatar
Linus Torvalds committed
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
static void ib_sa_mcmember_rec_callback(struct ib_sa_query *sa_query,
					int status,
					struct ib_sa_mad *mad)
{
	struct ib_sa_mcmember_query *query =
		container_of(sa_query, struct ib_sa_mcmember_query, sa_query);

	if (mad) {
		struct ib_sa_mcmember_rec rec;

		ib_unpack(mcmember_rec_table, ARRAY_SIZE(mcmember_rec_table),
			  mad->data, &rec);
		query->callback(status, &rec, query->context);
	} else
		query->callback(status, NULL, query->context);
}

static void ib_sa_mcmember_rec_release(struct ib_sa_query *sa_query)
{
	kfree(container_of(sa_query, struct ib_sa_mcmember_query, sa_query));
}

931
932
int ib_sa_mcmember_rec_query(struct ib_sa_client *client,
			     struct ib_device *device, u8 port_num,
Linus Torvalds's avatar
Linus Torvalds committed
933
934
935
			     u8 method,
			     struct ib_sa_mcmember_rec *rec,
			     ib_sa_comp_mask comp_mask,
936
			     int timeout_ms, gfp_t gfp_mask,
Linus Torvalds's avatar
Linus Torvalds committed
937
938
939
940
941
942
943
944
			     void (*callback)(int status,
					      struct ib_sa_mcmember_rec *resp,
					      void *context),
			     void *context,
			     struct ib_sa_query **sa_query)
{
	struct ib_sa_mcmember_query *query;
	struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
945
946
	struct ib_sa_port   *port;
	struct ib_mad_agent *agent;
947
	struct ib_sa_mad *mad;
Linus Torvalds's avatar
Linus Torvalds committed
948
949
	int ret;

950
951
952
953
954
955
	if (!sa_dev)
		return -ENODEV;

	port  = &sa_dev->port[port_num - sa_dev->start_port];
	agent = port->agent;

Linus Torvalds's avatar
Linus Torvalds committed
956
957
958
	query = kmalloc(sizeof *query, gfp_mask);
	if (!query)
		return -ENOMEM;
959

960
961
962
	query->sa_query.port     = port;
	ret = alloc_mad(&query->sa_query, gfp_mask);
	if (ret)
963
		goto err1;
Linus Torvalds's avatar
Linus Torvalds committed
964

965
966
967
968
	ib_sa_client_get(client);
	query->sa_query.client = client;
	query->callback        = callback;
	query->context         = context;
Linus Torvalds's avatar
Linus Torvalds committed
969

970
971
	mad = query->sa_query.mad_buf->mad;
	init_mad(mad, agent);
Linus Torvalds's avatar
Linus Torvalds committed
972

973
974
975
976
977
	query->sa_query.callback = callback ? ib_sa_mcmember_rec_callback : NULL;
	query->sa_query.release  = ib_sa_mcmember_rec_release;
	mad->mad_hdr.method	 = method;
	mad->mad_hdr.attr_id	 = cpu_to_be16(IB_SA_ATTR_MC_MEMBER_REC);
	mad->sa_hdr.comp_mask	 = comp_mask;
Linus Torvalds's avatar
Linus Torvalds committed
978
979

	ib_pack(mcmember_rec_table, ARRAY_SIZE(mcmember_rec_table),
980
		rec, mad->data);
Linus Torvalds's avatar
Linus Torvalds committed
981
982

	*sa_query = &query->sa_query;
983

984
	ret = send_mad(&query->sa_query, timeout_ms, gfp_mask);
985
986
	if (ret < 0)
		goto err2;
Linus Torvalds's avatar
Linus Torvalds committed
987

988
	return ret;
989
990
991

err2:
	*sa_query = NULL;
992
	ib_sa_client_put(query->sa_query.client);
993
	free_mad(&query->sa_query);
994
995
996
997

err1:
	kfree(query);
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
998
999
}

1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
/* Support GuidInfoRecord */
static void ib_sa_guidinfo_rec_callback(struct ib_sa_query *sa_query,
					int status,
					struct ib_sa_mad *mad)
{
	struct ib_sa_guidinfo_query *query =
		container_of(sa_query, struct ib_sa_guidinfo_query, sa_query);

	if (mad) {
		struct ib_sa_guidinfo_rec rec;

		ib_unpack(guidinfo_rec_table, ARRAY_SIZE(guidinfo_rec_table),
			  mad->data, &rec);
		query->callback(status, &rec, query->context);
	} else
		query->callback(status, NULL, query->context);
}

static void ib_sa_guidinfo_rec_release(struct ib_sa_query *sa_query)
{
	kfree(container_of(sa_query, struct ib_sa_guidinfo_query, sa_query));
}

int ib_sa_guid_info_rec_query(struct ib_sa_client *client,
			      struct ib_device *device, u8 port_num,
			      struct ib_sa_guidinfo_rec *rec,
			      ib_sa_comp_mask comp_mask, u8 method,
			      int timeout_ms, gfp_t gfp_mask,
			      void (*callback)(int status,
					       struct ib_sa_guidinfo_rec *resp,
					       void *context),
			      void *context,
			      struct ib_sa_query **sa_query)
{
	struct ib_sa_guidinfo_query *query;
	struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
	struct ib_sa_port *port;
	struct ib_mad_agent *agent;
	struct ib_sa_mad *mad;
	int ret;

	if (!sa_dev)
		return -ENODEV;

	if (method != IB_MGMT_METHOD_GET &&
	    method != IB_MGMT_METHOD_SET &&
	    method != IB_SA_METHOD_DELETE) {
		return -EINVAL;
	}

	port  = &sa_dev->port[port_num - sa_dev->start_port];
	agent = port->agent;

	query = kmalloc(sizeof *query, gfp_mask);
	if (!query)
		return -ENOMEM;

	query->sa_query.port = port;
	ret = alloc_mad(&query->sa_query, gfp_mask);
	if (ret)
		goto err1;

	ib_sa_client_get(client);
	query->sa_query.client = client;
	query->callback        = callback;
	query->context         = context;

	mad = query->sa_query.mad_buf->mad;
	init_mad(mad, agent);

	query->sa_query.callback = callback ? ib_sa_guidinfo_rec_callback : NULL;
	query->sa_query.release  = ib_sa_guidinfo_rec_release;

	mad->mad_hdr.method	 = method;
	mad->mad_hdr.attr_id	 = cpu_to_be16(IB_SA_ATTR_GUID_INFO_REC);
	mad->sa_hdr.comp_mask	 = comp_mask;

	ib_pack(guidinfo_rec_table, ARRAY_SIZE(guidinfo_rec_table), rec,
		mad->data);

	*sa_query = &query->sa_query;

	ret = send_mad(&query->sa_query, timeout_ms, gfp_mask);
	if (ret < 0)
		goto err2;

	return ret;

err2:
	*sa_query = NULL;
	ib_sa_client_put(query->sa_query.client);
	free_mad(&query->sa_query);

err1:
	kfree(query);
	return ret;
}
EXPORT_SYMBOL(ib_sa_guid_info_rec_query);

Linus Torvalds's avatar
Linus Torvalds committed
1099
1100
1101
static void send_handler(struct ib_mad_agent *agent,
			 struct ib_mad_send_wc *mad_send_wc)
{
1102
	struct ib_sa_query *query = mad_send_wc->send_buf->context[0];
Linus Torvalds's avatar
Linus Torvalds committed
1103
1104
	unsigned long flags;

1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
	if (query->callback)
		switch (mad_send_wc->status) {
		case IB_WC_SUCCESS:
			/* No callback -- already got recv */
			break;
		case IB_WC_RESP_TIMEOUT_ERR:
			query->callback(query, -ETIMEDOUT, NULL);
			break;
		case IB_WC_WR_FLUSH_ERR:
			query->callback(query, -EINTR, NULL);
			break;
		default:
			query->callback(query, -EIO, NULL);
			break;
		}
Linus Torvalds's avatar
Linus Torvalds committed
1120
1121

	spin_lock_irqsave(&idr_lock, flags);
1122
	idr_remove(&query_idr, query->id);
Linus Torvalds's avatar
Linus Torvalds committed
1123
	spin_unlock_irqrestore(&idr_lock, flags);
1124

1125
	free_mad(query);
1126
	ib_sa_client_put(query->client);
1127
	query->release(query);
Linus Torvalds's avatar
Linus Torvalds committed
1128
1129
1130
1131
1132
1133
}

static void recv_handler(struct ib_mad_agent *mad_agent,
			 struct ib_mad_recv_wc *mad_recv_wc)
{
	struct ib_sa_query *query;
1134
	struct ib_mad_send_buf *mad_buf;
Linus Torvalds's avatar
Linus Torvalds committed
1135

1136
1137
	mad_buf = (void *) (unsigned long) mad_recv_wc->wc->wr_id;
	query = mad_buf->context[0];
Linus Torvalds's avatar
Linus Torvalds committed
1138

1139
	if (query->callback) {
Linus Torvalds's avatar
Linus Torvalds committed
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
		if (mad_recv_wc->wc->status == IB_WC_SUCCESS)
			query->callback(query,
					mad_recv_wc->recv_buf.mad->mad_hdr.status ?
					-EINVAL : 0,
					(struct ib_sa_mad *) mad_recv_wc->recv_buf.mad);
		else
			query->callback(query, -EIO, NULL);
	}

	ib_free_recv_mad(mad_recv_wc);
}

static void ib_sa_add_one(struct ib_device *device)
{
	struct ib_sa_device *sa_dev;
	int s, e, i;
1156
	int count = 0;
Tom Tucker's avatar
Tom Tucker committed
1157
1158

	if (device->node_type == RDMA_NODE_IB_SWITCH)
Linus Torvalds's avatar
Linus Torvalds committed
1159
1160
1161
1162
1163
1164
		s = e = 0;
	else {
		s = 1;
		e = device->phys_port_cnt;
	}

1165
	sa_dev = kzalloc(sizeof *sa_dev +
Linus Torvalds's avatar
Linus Torvalds committed
1166
1167
1168
1169
1170
1171
1172
1173
1174
			 (e - s + 1) * sizeof (struct ib_sa_port),
			 GFP_KERNEL);
	if (!sa_dev)
		return;

	sa_dev->start_port = s;
	sa_dev->end_port   = e;

	for (i = 0; i <= e - s; ++i) {
1175
		spin_lock_init(&sa_dev->port[i].ah_lock);
1176
		if (!rdma_cap_ib_sa(device, i + 1))
1177
1178
			continue;

Linus Torvalds's avatar
Linus Torvalds committed
1179
1180
1181
1182
1183
1184
		sa_dev->port[i].sm_ah    = NULL;
		sa_dev->port[i].port_num = i + s;

		sa_dev->port[i].agent =
			ib_register_mad_agent(device, i + s, IB_QPT_GSI,
					      NULL, 0, send_handler,
1185
					      recv_handler, sa_dev, 0);
Linus Torvalds's avatar
Linus Torvalds committed
1186
1187
1188
		if (IS_ERR(sa_dev->port[i].agent))
			goto err;

David Howells's avatar
David Howells committed
1189
		INIT_WORK(&sa_dev->port[i].update_task, update_sm_ah);
1190
1191

		count++;
Linus Torvalds's avatar
Linus Torvalds committed
1192
1193
	}

1194
1195
1196
	if (!count)
		goto free;

Linus Torvalds's avatar
Linus Torvalds committed
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
	ib_set_client_data(device, &sa_client, sa_dev);

	/*
	 * We register our event handler after everything is set up,
	 * and then update our cached info after the event handler is
	 * registered to avoid any problems if a port changes state
	 * during our initialization.
	 */

	INIT_IB_EVENT_HANDLER(&sa_dev->event_handler, device, ib_sa_event);
	if (ib_register_event_handler(&sa_dev->event_handler))
		goto err;

1210
	for (i = 0; i <= e - s; ++i) {
1211
		if (rdma_cap_ib_sa(device, i + 1))
1212
			update_sm_ah(&sa_dev->port[i].update_task);
1213
	}
Linus Torvalds's avatar
Linus Torvalds committed
1214
1215
1216
1217

	return;

err:
1218
	while (--i >= 0) {
1219
		if (rdma_cap_ib_sa(device, i + 1))
1220
			ib_unregister_mad_agent(sa_dev->port[i].agent);
1221
1222
	}
free:
Linus Torvalds's avatar
Linus Torvalds committed
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
	kfree(sa_dev);
	return;
}

static void ib_sa_remove_one(struct ib_device *device)
{
	struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
	int i;

	if (!sa_dev)
		return;

	ib_unregister_event_handler(&sa_dev->event_handler);

1237
	flush_workqueue(ib_wq);
1238

Linus Torvalds's avatar
Linus Torvalds committed
1239
	for (i = 0; i <= sa_dev->end_port - sa_dev->start_port; ++i) {
1240
		if (rdma_cap_ib_sa(device, i + 1)) {
1241
1242
1243
1244
1245
			ib_unregister_mad_agent(sa_dev->port[i].agent);
			if (sa_dev->port[i].sm_ah)
				kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah);
		}

Linus Torvalds's avatar
Linus Torvalds committed
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
	}

	kfree(sa_dev);
}

static int __init ib_sa_init(void)
{
	int ret;

	get_random_bytes(&tid, sizeof tid);

	ret = ib_register_client(&sa_client);
1258
	if (ret) {
Linus Torvalds's avatar
Linus Torvalds committed
1259
		printk(KERN_ERR "Couldn't register ib_sa client\n");
1260
1261
1262
1263
1264
1265
1266
1267
		goto err1;
	}

	ret = mcast_init();
	if (ret) {
		printk(KERN_ERR "Couldn't initialize multicast handling\n");
		goto err2;
	}
Linus Torvalds's avatar
Linus Torvalds committed
1268

1269
1270
1271
1272
	return 0;
err2:
	ib_unregister_client(&sa_client);
err1:
Linus Torvalds's avatar
Linus Torvalds committed
1273
1274
1275
1276
1277
	return ret;
}

static void __exit ib_sa_cleanup(void)
{
1278
	mcast_cleanup();
Linus Torvalds's avatar
Linus Torvalds committed
1279
	ib_unregister_client(&sa_client);
1280
	idr_destroy(&query_idr);
Linus Torvalds's avatar
Linus Torvalds committed
1281
1282
1283
1284
}

module_init(ib_sa_init);
module_exit(ib_sa_cleanup);