user_mad.c 29.7 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
/*
 * Copyright (c) 2004 Topspin Communications.  All rights reserved.
Roland Dreier's avatar
Roland Dreier committed
3
 * Copyright (c) 2005 Voltaire, Inc. All rights reserved.
4
 * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
5
 * Copyright (c) 2008 Cisco. All rights reserved.
Linus Torvalds's avatar
Linus Torvalds committed
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/device.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/dma-mapping.h>
#include <linux/poll.h>
44
#include <linux/mutex.h>
Linus Torvalds's avatar
Linus Torvalds committed
45
#include <linux/kref.h>
46
#include <linux/compat.h>
47
#include <linux/sched.h>
48
#include <linux/semaphore.h>
49
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
50
51
52

#include <asm/uaccess.h>

53
54
#include <rdma/ib_mad.h>
#include <rdma/ib_user_mad.h>
Linus Torvalds's avatar
Linus Torvalds committed
55
56
57
58
59
60
61
62
63
64
65
66
67

MODULE_AUTHOR("Roland Dreier");
MODULE_DESCRIPTION("InfiniBand userspace MAD packet access");
MODULE_LICENSE("Dual BSD/GPL");

enum {
	IB_UMAD_MAX_PORTS  = 64,
	IB_UMAD_MAX_AGENTS = 32,

	IB_UMAD_MAJOR      = 231,
	IB_UMAD_MINOR_BASE = 0
};

68
/*
69
70
71
 * Our lifetime rules for these structs are the following:
 * device special file is opened, we take a reference on the
 * ib_umad_port's struct ib_umad_device. We drop these
72
73
74
75
76
77
78
 * references in the corresponding close().
 *
 * In addition to references coming from open character devices, there
 * is one more reference to each ib_umad_device representing the
 * module's reference taken when allocating the ib_umad_device in
 * ib_umad_add_one().
 *
79
 * When destroying an ib_umad_device, we drop the module's reference.
80
81
 */

Linus Torvalds's avatar
Linus Torvalds committed
82
struct ib_umad_port {
83
	struct cdev           cdev;
84
	struct device	      *dev;
Linus Torvalds's avatar
Linus Torvalds committed
85

86
	struct cdev           sm_cdev;
87
	struct device	      *sm_dev;
Linus Torvalds's avatar
Linus Torvalds committed
88
89
	struct semaphore       sm_sem;

90
	struct mutex	       file_mutex;
91
92
	struct list_head       file_list;

Linus Torvalds's avatar
Linus Torvalds committed
93
94
	struct ib_device      *ib_dev;
	struct ib_umad_device *umad_dev;
95
	int                    dev_num;
Linus Torvalds's avatar
Linus Torvalds committed
96
97
98
99
100
101
102
103
104
105
	u8                     port_num;
};

struct ib_umad_device {
	int                  start_port, end_port;
	struct kref          ref;
	struct ib_umad_port  port[0];
};

struct ib_umad_file {
106
	struct mutex		mutex;
107
108
	struct ib_umad_port    *port;
	struct list_head	recv_list;
109
	struct list_head	send_list;
110
	struct list_head	port_list;
111
	spinlock_t		send_lock;
112
113
114
	wait_queue_head_t	recv_wait;
	struct ib_mad_agent    *agent[IB_UMAD_MAX_AGENTS];
	int			agents_dead;
115
116
	u8			use_pkey_index;
	u8			already_used;
Linus Torvalds's avatar
Linus Torvalds committed
117
118
119
};

struct ib_umad_packet {
120
	struct ib_mad_send_buf *msg;
121
	struct ib_mad_recv_wc  *recv_wc;
Linus Torvalds's avatar
Linus Torvalds committed
122
	struct list_head   list;
123
124
	int		   length;
	struct ib_user_mad mad;
Linus Torvalds's avatar
Linus Torvalds committed
125
126
};

127
128
static struct class *umad_class;

Linus Torvalds's avatar
Linus Torvalds committed
129
static const dev_t base_dev = MKDEV(IB_UMAD_MAJOR, IB_UMAD_MINOR_BASE);
130
131

static DEFINE_SPINLOCK(port_lock);
132
static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS);
Linus Torvalds's avatar
Linus Torvalds committed
133
134
135
136

static void ib_umad_add_one(struct ib_device *device);
static void ib_umad_remove_one(struct ib_device *device);

137
138
139
140
141
142
143
144
static void ib_umad_release_dev(struct kref *ref)
{
	struct ib_umad_device *dev =
		container_of(ref, struct ib_umad_device, ref);

	kfree(dev);
}

145
146
147
148
149
150
static int hdr_size(struct ib_umad_file *file)
{
	return file->use_pkey_index ? sizeof (struct ib_user_mad_hdr) :
		sizeof (struct ib_user_mad_hdr_old);
}

151
/* caller must hold file->mutex */
152
153
154
155
156
static struct ib_mad_agent *__get_agent(struct ib_umad_file *file, int id)
{
	return file->agents_dead ? NULL : file->agent[id];
}

Linus Torvalds's avatar
Linus Torvalds committed
157
158
159
160
161
162
static int queue_packet(struct ib_umad_file *file,
			struct ib_mad_agent *agent,
			struct ib_umad_packet *packet)
{
	int ret = 1;

163
	mutex_lock(&file->mutex);
164

165
166
167
	for (packet->mad.hdr.id = 0;
	     packet->mad.hdr.id < IB_UMAD_MAX_AGENTS;
	     packet->mad.hdr.id++)
168
		if (agent == __get_agent(file, packet->mad.hdr.id)) {
Linus Torvalds's avatar
Linus Torvalds committed
169
170
171
172
173
174
			list_add_tail(&packet->list, &file->recv_list);
			wake_up_interruptible(&file->recv_wait);
			ret = 0;
			break;
		}

175
	mutex_unlock(&file->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
176
177
178
179

	return ret;
}

180
181
static void dequeue_send(struct ib_umad_file *file,
			 struct ib_umad_packet *packet)
182
{
183
184
185
	spin_lock_irq(&file->send_lock);
	list_del(&packet->list);
	spin_unlock_irq(&file->send_lock);
186
}
187

Linus Torvalds's avatar
Linus Torvalds committed
188
189
190
191
static void send_handler(struct ib_mad_agent *agent,
			 struct ib_mad_send_wc *send_wc)
{
	struct ib_umad_file *file = agent->context;
192
	struct ib_umad_packet *packet = send_wc->send_buf->context[0];
Linus Torvalds's avatar
Linus Torvalds committed
193

194
	dequeue_send(file, packet);
195
	ib_destroy_ah(packet->msg->ah);
196
	ib_free_send_mad(packet->msg);
Linus Torvalds's avatar
Linus Torvalds committed
197
198

	if (send_wc->status == IB_WC_RESP_TIMEOUT_ERR) {
199
200
201
202
		packet->length = IB_MGMT_MAD_HDR;
		packet->mad.hdr.status = ETIMEDOUT;
		if (!queue_packet(file, agent, packet))
			return;
203
	}
Linus Torvalds's avatar
Linus Torvalds committed
204
205
206
207
208
209
210
211
212
213
	kfree(packet);
}

static void recv_handler(struct ib_mad_agent *agent,
			 struct ib_mad_recv_wc *mad_recv_wc)
{
	struct ib_umad_file *file = agent->context;
	struct ib_umad_packet *packet;

	if (mad_recv_wc->wc->status != IB_WC_SUCCESS)
214
		goto err1;
Linus Torvalds's avatar
Linus Torvalds committed
215

216
	packet = kzalloc(sizeof *packet, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
217
	if (!packet)
218
		goto err1;
Linus Torvalds's avatar
Linus Torvalds committed
219

220
221
	packet->length = mad_recv_wc->mad_len;
	packet->recv_wc = mad_recv_wc;
Linus Torvalds's avatar
Linus Torvalds committed
222

223
224
225
226
227
228
229
	packet->mad.hdr.status	   = 0;
	packet->mad.hdr.length	   = hdr_size(file) + mad_recv_wc->mad_len;
	packet->mad.hdr.qpn	   = cpu_to_be32(mad_recv_wc->wc->src_qp);
	packet->mad.hdr.lid	   = cpu_to_be16(mad_recv_wc->wc->slid);
	packet->mad.hdr.sl	   = mad_recv_wc->wc->sl;
	packet->mad.hdr.path_bits  = mad_recv_wc->wc->dlid_path_bits;
	packet->mad.hdr.pkey_index = mad_recv_wc->wc->pkey_index;
230
231
	packet->mad.hdr.grh_present = !!(mad_recv_wc->wc->wc_flags & IB_WC_GRH);
	if (packet->mad.hdr.grh_present) {
232
233
234
235
236
237
238
239
240
241
242
		struct ib_ah_attr ah_attr;

		ib_init_ah_from_wc(agent->device, agent->port_num,
				   mad_recv_wc->wc, mad_recv_wc->recv_buf.grh,
				   &ah_attr);

		packet->mad.hdr.gid_index = ah_attr.grh.sgid_index;
		packet->mad.hdr.hop_limit = ah_attr.grh.hop_limit;
		packet->mad.hdr.traffic_class = ah_attr.grh.traffic_class;
		memcpy(packet->mad.hdr.gid, &ah_attr.grh.dgid, 16);
		packet->mad.hdr.flow_label = cpu_to_be32(ah_attr.grh.flow_label);
Linus Torvalds's avatar
Linus Torvalds committed
243
244
245
	}

	if (queue_packet(file, agent, packet))
246
247
		goto err2;
	return;
Linus Torvalds's avatar
Linus Torvalds committed
248

249
250
251
err2:
	kfree(packet);
err1:
Linus Torvalds's avatar
Linus Torvalds committed
252
253
254
	ib_free_recv_mad(mad_recv_wc);
}

255
256
static ssize_t copy_recv_mad(struct ib_umad_file *file, char __user *buf,
			     struct ib_umad_packet *packet, size_t count)
257
258
259
260
261
262
263
{
	struct ib_mad_recv_buf *recv_buf;
	int left, seg_payload, offset, max_seg_payload;

	/* We need enough room to copy the first (or only) MAD segment. */
	recv_buf = &packet->recv_wc->recv_buf;
	if ((packet->length <= sizeof (*recv_buf->mad) &&
264
	     count < hdr_size(file) + packet->length) ||
265
	    (packet->length > sizeof (*recv_buf->mad) &&
266
	     count < hdr_size(file) + sizeof (*recv_buf->mad)))
267
268
		return -EINVAL;

269
	if (copy_to_user(buf, &packet->mad, hdr_size(file)))
270
271
		return -EFAULT;

272
	buf += hdr_size(file);
273
274
275
276
277
278
279
280
281
	seg_payload = min_t(int, packet->length, sizeof (*recv_buf->mad));
	if (copy_to_user(buf, recv_buf->mad, seg_payload))
		return -EFAULT;

	if (seg_payload < packet->length) {
		/*
		 * Multipacket RMPP MAD message. Copy remainder of message.
		 * Note that last segment may have a shorter payload.
		 */
282
		if (count < hdr_size(file) + packet->length) {
283
284
285
286
287
288
			/*
			 * The buffer is too small, return the first RMPP segment,
			 * which includes the RMPP message length.
			 */
			return -ENOSPC;
		}
289
		offset = ib_get_mad_data_offset(recv_buf->mad->mad_hdr.mgmt_class);
290
291
292
293
294
295
296
297
298
299
300
301
		max_seg_payload = sizeof (struct ib_mad) - offset;

		for (left = packet->length - seg_payload, buf += seg_payload;
		     left; left -= seg_payload, buf += seg_payload) {
			recv_buf = container_of(recv_buf->list.next,
						struct ib_mad_recv_buf, list);
			seg_payload = min(left, max_seg_payload);
			if (copy_to_user(buf, ((void *) recv_buf->mad) + offset,
					 seg_payload))
				return -EFAULT;
		}
	}
302
	return hdr_size(file) + packet->length;
303
304
}

305
306
static ssize_t copy_send_mad(struct ib_umad_file *file, char __user *buf,
			     struct ib_umad_packet *packet, size_t count)
307
{
308
	ssize_t size = hdr_size(file) + packet->length;
309
310
311
312

	if (count < size)
		return -EINVAL;

313
314
315
316
317
318
	if (copy_to_user(buf, &packet->mad, hdr_size(file)))
		return -EFAULT;

	buf += hdr_size(file);

	if (copy_to_user(buf, packet->mad.data, packet->length))
319
320
321
322
323
		return -EFAULT;

	return size;
}

Linus Torvalds's avatar
Linus Torvalds committed
324
325
326
327
328
329
330
static ssize_t ib_umad_read(struct file *filp, char __user *buf,
			    size_t count, loff_t *pos)
{
	struct ib_umad_file *file = filp->private_data;
	struct ib_umad_packet *packet;
	ssize_t ret;

331
	if (count < hdr_size(file))
Linus Torvalds's avatar
Linus Torvalds committed
332
333
		return -EINVAL;

334
	mutex_lock(&file->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
335
336

	while (list_empty(&file->recv_list)) {
337
		mutex_unlock(&file->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
338
339
340
341
342
343
344
345

		if (filp->f_flags & O_NONBLOCK)
			return -EAGAIN;

		if (wait_event_interruptible(file->recv_wait,
					     !list_empty(&file->recv_list)))
			return -ERESTARTSYS;

346
		mutex_lock(&file->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
347
348
349
350
351
	}

	packet = list_entry(file->recv_list.next, struct ib_umad_packet, list);
	list_del(&packet->list);

352
	mutex_unlock(&file->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
353

354
	if (packet->recv_wc)
355
		ret = copy_recv_mad(file, buf, packet, count);
Linus Torvalds's avatar
Linus Torvalds committed
356
	else
357
		ret = copy_send_mad(file, buf, packet, count);
358

359
360
	if (ret < 0) {
		/* Requeue packet */
361
		mutex_lock(&file->mutex);
362
		list_add(&packet->list, &file->recv_list);
363
		mutex_unlock(&file->mutex);
364
365
366
	} else {
		if (packet->recv_wc)
			ib_free_recv_mad(packet->recv_wc);
367
		kfree(packet);
368
	}
Linus Torvalds's avatar
Linus Torvalds committed
369
370
371
	return ret;
}

372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
static int copy_rmpp_mad(struct ib_mad_send_buf *msg, const char __user *buf)
{
	int left, seg;

	/* Copy class specific header */
	if ((msg->hdr_len > IB_MGMT_RMPP_HDR) &&
	    copy_from_user(msg->mad + IB_MGMT_RMPP_HDR, buf + IB_MGMT_RMPP_HDR,
			   msg->hdr_len - IB_MGMT_RMPP_HDR))
		return -EFAULT;

	/* All headers are in place.  Copy data segments. */
	for (seg = 1, left = msg->data_len, buf += msg->hdr_len; left > 0;
	     seg++, left -= msg->seg_size, buf += msg->seg_size) {
		if (copy_from_user(ib_get_rmpp_segment(msg, seg), buf,
				   min(left, msg->seg_size)))
			return -EFAULT;
	}
	return 0;
}

392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
static int same_destination(struct ib_user_mad_hdr *hdr1,
			    struct ib_user_mad_hdr *hdr2)
{
	if (!hdr1->grh_present && !hdr2->grh_present)
	   return (hdr1->lid == hdr2->lid);

	if (hdr1->grh_present && hdr2->grh_present)
	   return !memcmp(hdr1->gid, hdr2->gid, 16);

	return 0;
}

static int is_duplicate(struct ib_umad_file *file,
			struct ib_umad_packet *packet)
{
	struct ib_umad_packet *sent_packet;
	struct ib_mad_hdr *sent_hdr, *hdr;

	hdr = (struct ib_mad_hdr *) packet->mad.data;
	list_for_each_entry(sent_packet, &file->send_list, list) {
		sent_hdr = (struct ib_mad_hdr *) sent_packet->mad.data;

		if ((hdr->tid != sent_hdr->tid) ||
		    (hdr->mgmt_class != sent_hdr->mgmt_class))
			continue;

		/*
		 * No need to be overly clever here.  If two new operations have
		 * the same TID, reject the second as a duplicate.  This is more
		 * restrictive than required by the spec.
		 */
		if (!ib_response_mad((struct ib_mad *) hdr)) {
			if (!ib_response_mad((struct ib_mad *) sent_hdr))
				return 1;
			continue;
		} else if (!ib_response_mad((struct ib_mad *) sent_hdr))
			continue;

		if (same_destination(&packet->mad.hdr, &sent_packet->mad.hdr))
			return 1;
	}

	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
437
438
439
440
441
442
443
static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
			     size_t count, loff_t *pos)
{
	struct ib_umad_file *file = filp->private_data;
	struct ib_umad_packet *packet;
	struct ib_mad_agent *agent;
	struct ib_ah_attr ah_attr;
444
	struct ib_ah *ah;
445
	struct ib_rmpp_mad *rmpp_mad;
446
	__be64 *tid;
447
	int ret, data_len, hdr_len, copy_offset, rmpp_active;
Linus Torvalds's avatar
Linus Torvalds committed
448

449
	if (count < hdr_size(file) + IB_MGMT_RMPP_HDR)
Linus Torvalds's avatar
Linus Torvalds committed
450
451
		return -EINVAL;

452
	packet = kzalloc(sizeof *packet + IB_MGMT_RMPP_HDR, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
453
454
455
	if (!packet)
		return -ENOMEM;

456
	if (copy_from_user(&packet->mad, buf, hdr_size(file))) {
457
458
		ret = -EFAULT;
		goto err;
Linus Torvalds's avatar
Linus Torvalds committed
459
460
	}

461
462
	if (packet->mad.hdr.id < 0 ||
	    packet->mad.hdr.id >= IB_UMAD_MAX_AGENTS) {
Linus Torvalds's avatar
Linus Torvalds committed
463
464
465
466
		ret = -EINVAL;
		goto err;
	}

467
468
469
470
471
472
473
	buf += hdr_size(file);

	if (copy_from_user(packet->mad.data, buf, IB_MGMT_RMPP_HDR)) {
		ret = -EFAULT;
		goto err;
	}

474
	mutex_lock(&file->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
475

476
	agent = __get_agent(file, packet->mad.hdr.id);
Linus Torvalds's avatar
Linus Torvalds committed
477
478
479
480
481
482
	if (!agent) {
		ret = -EINVAL;
		goto err_up;
	}

	memset(&ah_attr, 0, sizeof ah_attr);
483
484
485
	ah_attr.dlid          = be16_to_cpu(packet->mad.hdr.lid);
	ah_attr.sl            = packet->mad.hdr.sl;
	ah_attr.src_path_bits = packet->mad.hdr.path_bits;
Linus Torvalds's avatar
Linus Torvalds committed
486
	ah_attr.port_num      = file->port->port_num;
487
	if (packet->mad.hdr.grh_present) {
Linus Torvalds's avatar
Linus Torvalds committed
488
		ah_attr.ah_flags = IB_AH_GRH;
489
		memcpy(ah_attr.grh.dgid.raw, packet->mad.hdr.gid, 16);
490
		ah_attr.grh.sgid_index	   = packet->mad.hdr.gid_index;
Alexander Chiang's avatar
Alexander Chiang committed
491
492
		ah_attr.grh.flow_label	   = be32_to_cpu(packet->mad.hdr.flow_label);
		ah_attr.grh.hop_limit	   = packet->mad.hdr.hop_limit;
493
		ah_attr.grh.traffic_class  = packet->mad.hdr.traffic_class;
Linus Torvalds's avatar
Linus Torvalds committed
494
495
	}

496
497
498
	ah = ib_create_ah(agent->qp->pd, &ah_attr);
	if (IS_ERR(ah)) {
		ret = PTR_ERR(ah);
Linus Torvalds's avatar
Linus Torvalds committed
499
500
501
		goto err_up;
	}

502
	rmpp_mad = (struct ib_rmpp_mad *) packet->mad.data;
503
504
505
506
507
	hdr_len = ib_get_mad_data_offset(rmpp_mad->mad_hdr.mgmt_class);
	if (!ib_is_mad_class_rmpp(rmpp_mad->mad_hdr.mgmt_class)) {
		copy_offset = IB_MGMT_MAD_HDR;
		rmpp_active = 0;
	} else {
508
509
510
		copy_offset = IB_MGMT_RMPP_HDR;
		rmpp_active = ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) &
			      IB_MGMT_RMPP_FLAG_ACTIVE;
511
512
	}

513
	data_len = count - hdr_size(file) - hdr_len;
514
515
	packet->msg = ib_create_send_mad(agent,
					 be32_to_cpu(packet->mad.hdr.qpn),
516
517
					 packet->mad.hdr.pkey_index, rmpp_active,
					 hdr_len, data_len, GFP_KERNEL);
518
519
520
521
	if (IS_ERR(packet->msg)) {
		ret = PTR_ERR(packet->msg);
		goto err_ah;
	}
Linus Torvalds's avatar
Linus Torvalds committed
522

Alexander Chiang's avatar
Alexander Chiang committed
523
	packet->msg->ah		= ah;
524
	packet->msg->timeout_ms = packet->mad.hdr.timeout_ms;
Alexander Chiang's avatar
Alexander Chiang committed
525
	packet->msg->retries	= packet->mad.hdr.retries;
526
	packet->msg->context[0] = packet;
Linus Torvalds's avatar
Linus Torvalds committed
527

528
	/* Copy MAD header.  Any RMPP header is already in place. */
Sean Hefty's avatar
Sean Hefty committed
529
	memcpy(packet->msg->mad, packet->mad.data, IB_MGMT_MAD_HDR);
530
531
532
533
534
535
536
537
538
539
540
541

	if (!rmpp_active) {
		if (copy_from_user(packet->msg->mad + copy_offset,
				   buf + copy_offset,
				   hdr_len + data_len - copy_offset)) {
			ret = -EFAULT;
			goto err_msg;
		}
	} else {
		ret = copy_rmpp_mad(packet->msg, buf);
		if (ret)
			goto err_msg;
542
543
544
	}

	/*
545
546
547
	 * Set the high-order part of the transaction ID to make MADs from
	 * different agents unique, and allow routing responses back to the
	 * original requestor.
548
	 */
549
	if (!ib_response_mad(packet->msg->mad)) {
550
		tid = &((struct ib_mad_hdr *) packet->msg->mad)->tid;
551
552
		*tid = cpu_to_be64(((u64) agent->hi_tid) << 32 |
				   (be64_to_cpup(tid) & 0xffffffff));
553
554
555
556
557
558
559
560
561
562
563
		rmpp_mad->mad_hdr.tid = *tid;
	}

	spin_lock_irq(&file->send_lock);
	ret = is_duplicate(file, packet);
	if (!ret)
		list_add_tail(&packet->list, &file->send_list);
	spin_unlock_irq(&file->send_lock);
	if (ret) {
		ret = -EINVAL;
		goto err_msg;
Linus Torvalds's avatar
Linus Torvalds committed
564
565
	}

566
	ret = ib_post_send_mad(packet->msg, NULL);
567
	if (ret)
568
		goto err_send;
569

570
	mutex_unlock(&file->mutex);
Sean Hefty's avatar
Sean Hefty committed
571
	return count;
572

573
574
err_send:
	dequeue_send(file, packet);
575
576
577
err_msg:
	ib_free_send_mad(packet->msg);
err_ah:
578
	ib_destroy_ah(ah);
Linus Torvalds's avatar
Linus Torvalds committed
579
err_up:
580
	mutex_unlock(&file->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
err:
	kfree(packet);
	return ret;
}

static unsigned int ib_umad_poll(struct file *filp, struct poll_table_struct *wait)
{
	struct ib_umad_file *file = filp->private_data;

	/* we will always be able to post a MAD send */
	unsigned int mask = POLLOUT | POLLWRNORM;

	poll_wait(filp, &file->recv_wait, wait);

	if (!list_empty(&file->recv_list))
		mask |= POLLIN | POLLRDNORM;

	return mask;
}

601
602
static int ib_umad_reg_agent(struct ib_umad_file *file, void __user *arg,
			     int compat_method_mask)
Linus Torvalds's avatar
Linus Torvalds committed
603
604
605
{
	struct ib_user_mad_reg_req ureq;
	struct ib_mad_reg_req req;
606
	struct ib_mad_agent *agent = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
607
608
609
	int agent_id;
	int ret;

610
611
	mutex_lock(&file->port->file_mutex);
	mutex_lock(&file->mutex);
612
613
614
615
616

	if (!file->port->ib_dev) {
		ret = -EPIPE;
		goto out;
	}
Linus Torvalds's avatar
Linus Torvalds committed
617

618
	if (copy_from_user(&ureq, arg, sizeof ureq)) {
Linus Torvalds's avatar
Linus Torvalds committed
619
620
621
622
623
624
625
626
627
628
		ret = -EFAULT;
		goto out;
	}

	if (ureq.qpn != 0 && ureq.qpn != 1) {
		ret = -EINVAL;
		goto out;
	}

	for (agent_id = 0; agent_id < IB_UMAD_MAX_AGENTS; ++agent_id)
629
		if (!__get_agent(file, agent_id))
Linus Torvalds's avatar
Linus Torvalds committed
630
631
632
633
634
635
			goto found;

	ret = -ENOMEM;
	goto out;

found:
636
637
638
	if (ureq.mgmt_class) {
		req.mgmt_class         = ureq.mgmt_class;
		req.mgmt_class_version = ureq.mgmt_class_version;
639
640
641
642
643
644
645
646
647
648
649
650
		memcpy(req.oui, ureq.oui, sizeof req.oui);

		if (compat_method_mask) {
			u32 *umm = (u32 *) ureq.method_mask;
			int i;

			for (i = 0; i < BITS_TO_LONGS(IB_MGMT_MAX_METHODS); ++i)
				req.method_mask[i] =
					umm[i * 2] | ((u64) umm[i * 2 + 1] << 32);
		} else
			memcpy(req.method_mask, ureq.method_mask,
			       sizeof req.method_mask);
651
	}
Linus Torvalds's avatar
Linus Torvalds committed
652
653
654

	agent = ib_register_mad_agent(file->port->ib_dev, file->port->port_num,
				      ureq.qpn ? IB_QPT_GSI : IB_QPT_SMI,
655
				      ureq.mgmt_class ? &req : NULL,
656
657
				      ureq.rmpp_version,
				      send_handler, recv_handler, file);
Linus Torvalds's avatar
Linus Torvalds committed
658
659
	if (IS_ERR(agent)) {
		ret = PTR_ERR(agent);
660
		agent = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
661
662
663
664
665
666
		goto out;
	}

	if (put_user(agent_id,
		     (u32 __user *) (arg + offsetof(struct ib_user_mad_reg_req, id)))) {
		ret = -EFAULT;
667
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
668
669
	}

670
671
672
673
674
675
676
677
678
679
	if (!file->already_used) {
		file->already_used = 1;
		if (!file->use_pkey_index) {
			printk(KERN_WARNING "user_mad: process %s did not enable "
			       "P_Key index support.\n", current->comm);
			printk(KERN_WARNING "user_mad:   Documentation/infiniband/user_mad.txt "
			       "has info on the new ABI.\n");
		}
	}

680
	file->agent[agent_id] = agent;
Linus Torvalds's avatar
Linus Torvalds committed
681
	ret = 0;
682

Linus Torvalds's avatar
Linus Torvalds committed
683
out:
684
685
686
687
688
689
690
	mutex_unlock(&file->mutex);

	if (ret && agent)
		ib_unregister_mad_agent(agent);

	mutex_unlock(&file->port->file_mutex);

Linus Torvalds's avatar
Linus Torvalds committed
691
692
693
	return ret;
}

694
static int ib_umad_unreg_agent(struct ib_umad_file *file, u32 __user *arg)
Linus Torvalds's avatar
Linus Torvalds committed
695
{
696
	struct ib_mad_agent *agent = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
697
698
699
	u32 id;
	int ret = 0;

700
	if (get_user(id, arg))
701
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
702

703
704
	mutex_lock(&file->port->file_mutex);
	mutex_lock(&file->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
705

706
	if (id < 0 || id >= IB_UMAD_MAX_AGENTS || !__get_agent(file, id)) {
Linus Torvalds's avatar
Linus Torvalds committed
707
708
709
710
		ret = -EINVAL;
		goto out;
	}

711
	agent = file->agent[id];
Linus Torvalds's avatar
Linus Torvalds committed
712
713
714
	file->agent[id] = NULL;

out:
715
	mutex_unlock(&file->mutex);
716

717
	if (agent)
718
719
		ib_unregister_mad_agent(agent);

720
721
	mutex_unlock(&file->port->file_mutex);

Linus Torvalds's avatar
Linus Torvalds committed
722
723
724
	return ret;
}

725
726
727
728
static long ib_umad_enable_pkey(struct ib_umad_file *file)
{
	int ret = 0;

729
	mutex_lock(&file->mutex);
730
731
732
733
	if (file->already_used)
		ret = -EINVAL;
	else
		file->use_pkey_index = 1;
734
	mutex_unlock(&file->mutex);
735
736
737
738

	return ret;
}

739
740
static long ib_umad_ioctl(struct file *filp, unsigned int cmd,
			  unsigned long arg)
Linus Torvalds's avatar
Linus Torvalds committed
741
742
743
{
	switch (cmd) {
	case IB_USER_MAD_REGISTER_AGENT:
744
		return ib_umad_reg_agent(filp->private_data, (void __user *) arg, 0);
Linus Torvalds's avatar
Linus Torvalds committed
745
	case IB_USER_MAD_UNREGISTER_AGENT:
746
		return ib_umad_unreg_agent(filp->private_data, (__u32 __user *) arg);
747
748
	case IB_USER_MAD_ENABLE_PKEY:
		return ib_umad_enable_pkey(filp->private_data);
Linus Torvalds's avatar
Linus Torvalds committed
749
750
751
752
753
	default:
		return -ENOIOCTLCMD;
	}
}

754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
#ifdef CONFIG_COMPAT
static long ib_umad_compat_ioctl(struct file *filp, unsigned int cmd,
				 unsigned long arg)
{
	switch (cmd) {
	case IB_USER_MAD_REGISTER_AGENT:
		return ib_umad_reg_agent(filp->private_data, compat_ptr(arg), 1);
	case IB_USER_MAD_UNREGISTER_AGENT:
		return ib_umad_unreg_agent(filp->private_data, compat_ptr(arg));
	case IB_USER_MAD_ENABLE_PKEY:
		return ib_umad_enable_pkey(filp->private_data);
	default:
		return -ENOIOCTLCMD;
	}
}
#endif

771
772
773
/*
 * ib_umad_open() does not need the BKL:
 *
774
 *  - the ib_umad_port structures are properly reference counted, and
775
776
777
778
779
 *    everything else is purely local to the file being created, so
 *    races against other open calls are not a problem;
 *  - the ioctl method does not affect any global state outside of the
 *    file structure being operated on;
 */
Linus Torvalds's avatar
Linus Torvalds committed
780
781
static int ib_umad_open(struct inode *inode, struct file *filp)
{
782
	struct ib_umad_port *port;
Linus Torvalds's avatar
Linus Torvalds committed
783
	struct ib_umad_file *file;
784
	int ret = 0;
Linus Torvalds's avatar
Linus Torvalds committed
785

786
	port = container_of(inode->i_cdev, struct ib_umad_port, cdev);
787
788
	if (port)
		kref_get(&port->umad_dev->ref);
789
	else
790
791
		return -ENXIO;

792
	mutex_lock(&port->file_mutex);
793
794
795
796
797
798

	if (!port->ib_dev) {
		ret = -ENXIO;
		goto out;
	}

Sean Hefty's avatar
Sean Hefty committed
799
	file = kzalloc(sizeof *file, GFP_KERNEL);
800
801
	if (!file) {
		kref_put(&port->umad_dev->ref, ib_umad_release_dev);
802
803
		ret = -ENOMEM;
		goto out;
804
	}
Linus Torvalds's avatar
Linus Torvalds committed
805

806
	mutex_init(&file->mutex);
807
	spin_lock_init(&file->send_lock);
Linus Torvalds's avatar
Linus Torvalds committed
808
	INIT_LIST_HEAD(&file->recv_list);
809
	INIT_LIST_HEAD(&file->send_list);
Linus Torvalds's avatar
Linus Torvalds committed
810
811
812
813
814
	init_waitqueue_head(&file->recv_wait);

	file->port = port;
	filp->private_data = file;

815
816
817
	list_add_tail(&file->port_list, &port->file_list);

out:
818
	mutex_unlock(&port->file_mutex);
819
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
820
821
822
823
824
}

static int ib_umad_close(struct inode *inode, struct file *filp)
{
	struct ib_umad_file *file = filp->private_data;
825
	struct ib_umad_device *dev = file->port->umad_dev;
826
	struct ib_umad_packet *packet, *tmp;
827
	int already_dead;
Linus Torvalds's avatar
Linus Torvalds committed
828
829
	int i;

830
831
	mutex_lock(&file->port->file_mutex);
	mutex_lock(&file->mutex);
832
833
834

	already_dead = file->agents_dead;
	file->agents_dead = 1;
Linus Torvalds's avatar
Linus Torvalds committed
835

836
837
838
	list_for_each_entry_safe(packet, tmp, &file->recv_list, list) {
		if (packet->recv_wc)
			ib_free_recv_mad(packet->recv_wc);
839
		kfree(packet);
840
	}
841

842
843
	list_del(&file->port_list);

844
	mutex_unlock(&file->mutex);
845
846
847
848
849

	if (!already_dead)
		for (i = 0; i < IB_UMAD_MAX_AGENTS; ++i)
			if (file->agent[i])
				ib_unregister_mad_agent(file->agent[i]);
Linus Torvalds's avatar
Linus Torvalds committed
850

851
	mutex_unlock(&file->port->file_mutex);
852
853

	kfree(file);
854
855
	kref_put(&dev->ref, ib_umad_release_dev);

Linus Torvalds's avatar
Linus Torvalds committed
856
857
858
	return 0;
}

859
static const struct file_operations umad_fops = {
Alexander Chiang's avatar
Alexander Chiang committed
860
861
862
863
	.owner		= THIS_MODULE,
	.read		= ib_umad_read,
	.write		= ib_umad_write,
	.poll		= ib_umad_poll,
Linus Torvalds's avatar
Linus Torvalds committed
864
	.unlocked_ioctl = ib_umad_ioctl,
865
#ifdef CONFIG_COMPAT
Alexander Chiang's avatar
Alexander Chiang committed
866
	.compat_ioctl	= ib_umad_compat_ioctl,
867
#endif
Alexander Chiang's avatar
Alexander Chiang committed
868
869
	.open		= ib_umad_open,
	.release	= ib_umad_close
Linus Torvalds's avatar
Linus Torvalds committed
870
871
872
873
};

static int ib_umad_sm_open(struct inode *inode, struct file *filp)
{
874
	struct ib_umad_port *port;
Linus Torvalds's avatar
Linus Torvalds committed
875
876
877
878
879
	struct ib_port_modify props = {
		.set_port_cap_mask = IB_PORT_SM
	};
	int ret;

880
	port = container_of(inode->i_cdev, struct ib_umad_port, sm_cdev);
881
882
	if (port)
		kref_get(&port->umad_dev->ref);
883
	else
884
885
		return -ENXIO;

Linus Torvalds's avatar
Linus Torvalds committed
886
	if (filp->f_flags & O_NONBLOCK) {
887
888
889
890
		if (down_trylock(&port->sm_sem)) {
			ret = -EAGAIN;
			goto fail;
		}
Linus Torvalds's avatar
Linus Torvalds committed
891
	} else {
892
893
894
895
		if (down_interruptible(&port->sm_sem)) {
			ret = -ERESTARTSYS;
			goto fail;
		}
Linus Torvalds's avatar
Linus Torvalds committed
896
897
898
899
900
	}

	ret = ib_modify_port(port->ib_dev, port->port_num, 0, &props);
	if (ret) {
		up(&port->sm_sem);
901
		goto fail;
Linus Torvalds's avatar
Linus Torvalds committed
902
903
904
905
906
	}

	filp->private_data = port;

	return 0;
907
908
909
910

fail:
	kref_put(&port->umad_dev->ref, ib_umad_release_dev);
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
911
912
913
914
915
916
917
918
}

static int ib_umad_sm_close(struct inode *inode, struct file *filp)
{
	struct ib_umad_port *port = filp->private_data;
	struct ib_port_modify props = {
		.clr_port_cap_mask = IB_PORT_SM
	};
919
920
	int ret = 0;

921
	mutex_lock(&port->file_mutex);
922
923
	if (port->ib_dev)
		ret = ib_modify_port(port->ib_dev, port->port_num, 0, &props);
924
	mutex_unlock(&port->file_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
925
926
927

	up(&port->sm_sem);

928
929
	kref_put(&port->umad_dev->ref, ib_umad_release_dev);

Linus Torvalds's avatar
Linus Torvalds committed
930
931
932
	return ret;
}

933
static const struct file_operations umad_sm_fops = {
Alexander Chiang's avatar
Alexander Chiang committed
934
935
	.owner	 = THIS_MODULE,
	.open	 = ib_umad_sm_open,
Linus Torvalds's avatar
Linus Torvalds committed
936
937
938
939
940
941
942
943
944
	.release = ib_umad_sm_close
};

static struct ib_client umad_client = {
	.name   = "umad",
	.add    = ib_umad_add_one,
	.remove = ib_umad_remove_one
};

945
946
static ssize_t show_ibdev(struct device *dev, struct device_attribute *attr,
			  char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
947
{
948
	struct ib_umad_port *port = dev_get_drvdata(dev);
Linus Torvalds's avatar
Linus Torvalds committed
949

950
951
952
	if (!port)
		return -ENODEV;

Linus Torvalds's avatar
Linus Torvalds committed
953
954
	return sprintf(buf, "%s\n", port->ib_dev->name);
}
955
static DEVICE_ATTR(ibdev, S_IRUGO, show_ibdev, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
956

957
958
static ssize_t show_port(struct device *dev, struct device_attribute *attr,
			 char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
959
{
960
	struct ib_umad_port *port = dev_get_drvdata(dev);
Linus Torvalds's avatar
Linus Torvalds committed
961

962
963
964
	if (!port)
		return -ENODEV;

Linus Torvalds's avatar
Linus Torvalds committed
965
966
	return sprintf(buf, "%d\n", port->port_num);
}
967
static DEVICE_ATTR(port, S_IRUGO, show_port, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
968

969
970
static CLASS_ATTR_STRING(abi_version, S_IRUGO,
			 __stringify(IB_USER_MAD_ABI_VERSION));
Linus Torvalds's avatar
Linus Torvalds committed
971

972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
static dev_t overflow_maj;
static DECLARE_BITMAP(overflow_map, IB_UMAD_MAX_PORTS);
static int find_overflow_devnum(void)
{
	int ret;

	if (!overflow_maj) {
		ret = alloc_chrdev_region(&overflow_maj, 0, IB_UMAD_MAX_PORTS * 2,
					  "infiniband_mad");
		if (ret) {
			printk(KERN_ERR "user_mad: couldn't register dynamic device number\n");
			return ret;
		}
	}

	ret = find_first_zero_bit(overflow_map, IB_UMAD_MAX_PORTS);
	if (ret >= IB_UMAD_MAX_PORTS)
		return -1;

	return ret;
}

Linus Torvalds's avatar
Linus Torvalds committed
994
995
996
static int ib_umad_init_port(struct ib_device *device, int port_num,
			     struct ib_umad_port *port)
{
997
	int devnum;
998
	dev_t base;
999

1000
	spin_lock(&port_lock);
1001
1002
	devnum = find_first_zero_bit(dev_map, IB_UMAD_MAX_PORTS);
	if (devnum >= IB_UMAD_MAX_PORTS) {
1003
		spin_unlock(&port_lock);
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
		devnum = find_overflow_devnum();
		if (devnum < 0)
			return -1;

		spin_lock(&port_lock);
		port->dev_num = devnum + IB_UMAD_MAX_PORTS;
		base = devnum + overflow_maj;
		set_bit(devnum, overflow_map);
	} else {
		port->dev_num = devnum;
		base = devnum + base_dev;
		set_bit(devnum, dev_map);
Linus Torvalds's avatar
Linus Torvalds committed
1016
	}
1017
	spin_unlock(&port_lock);
Linus Torvalds's avatar
Linus Torvalds committed
1018
1019
1020
1021

	port->ib_dev   = device;
	port->port_num = port_num;
	init_MUTEX(&port->sm_sem);
1022
	mutex_init(&port->file_mutex);
1023
	INIT_LIST_HEAD(&port->file_list);
Linus Torvalds's avatar
Linus Torvalds committed
1024

1025
1026
1027
	cdev_init(&port->cdev, &umad_fops);
	port->cdev.owner = THIS_MODULE;
	kobject_set_name(&port->cdev.kobj, "umad%d", port->dev_num);
1028
	if (cdev_add(&port->cdev, base, 1))
Linus Torvalds's avatar
Linus Torvalds committed
1029
1030
		goto err_cdev;

1031
	port->dev = device_create(umad_class, device->dma_device,
1032
				  port->cdev.dev, port,
1033
				  "umad%d", port->dev_num);
1034
	if (IS_ERR(port->dev))
1035
		goto err_cdev;
Linus Torvalds's avatar
Linus Torvalds committed
1036

1037
1038
1039
1040
1041
	if (device_create_file(port->dev, &dev_attr_ibdev))
		goto err_dev;
	if (device_create_file(port->dev, &dev_attr_port))
		goto err_dev;

1042
	base += IB_UMAD_MAX_PORTS;
1043
1044
1045
	cdev_init(&port->sm_cdev, &umad_sm_fops);
	port->sm_cdev.owner = THIS_MODULE;
	kobject_set_name(&port->sm_cdev.kobj, "issm%d", port->dev_num);
1046
	if (cdev_add(&port->sm_cdev, base, 1))
1047
		goto err_sm_cdev;
Linus Torvalds's avatar
Linus Torvalds committed
1048

1049
	port->sm_dev = device_create(umad_class, device->dma_device,
1050
				     port->sm_cdev.dev, port,
1051
				     "issm%d", port->dev_num);
1052
	if (IS_ERR(port->sm_dev))
Linus Torvalds's avatar
Linus Torvalds committed
1053
1054
		goto err_sm_cdev;

1055
1056
1057
1058
	if (device_create_file(port->sm_dev, &dev_attr_ibdev))
		goto err_sm_dev;
	if (device_create_file(port->sm_dev, &dev_attr_port))
		goto err_sm_dev;
Linus Torvalds's avatar
Linus Torvalds committed
1059
1060
1061

	return 0;

1062
err_sm_dev:
1063
	device_destroy(umad_class, port->sm_cdev.dev);
Linus Torvalds's avatar
Linus Torvalds committed
1064
1065

err_sm_cdev:
1066
	cdev_del(&port->sm_cdev);
Linus Torvalds's avatar
Linus Torvalds committed
1067

1068
err_dev:
1069
	device_destroy(umad_class, port->cdev.dev);
Linus Torvalds's avatar
Linus Torvalds committed
1070
1071

err_cdev:
1072
	cdev_del(&port->cdev);
1073
1074
1075
1076
	if (port->dev_num < IB_UMAD_MAX_PORTS)
		clear_bit(devnum, dev_map);
	else
		clear_bit(devnum, overflow_map);
Linus Torvalds's avatar
Linus Torvalds committed
1077
1078
1079
1080

	return -1;
}

1081
1082
static void ib_umad_kill_port(struct ib_umad_port *port)
{
1083
	struct ib_umad_file *file;
1084
	int already_dead;
1085
1086
	int id;

1087
1088
	dev_set_drvdata(port->dev,    NULL);
	dev_set_drvdata(port->sm_dev, NULL);
1089

1090
1091
	device_destroy(umad_class, port->cdev.dev);
	device_destroy(umad_class, port->sm_cdev.dev);
1092

1093
1094
	cdev_del(&port->cdev);
	cdev_del(&port->sm_cdev);
1095

1096
	mutex_lock(&port->file_mutex);
1097
1098
1099

	port->ib_dev = NULL;

1100
1101
1102
	list_for_each_entry(file, &port->file_list, port_list) {
		mutex_lock(&file->mutex);
		already_dead = file->agents_dead;
1103
		file->agents_dead = 1;
1104
		mutex_unlock(&file->mutex);
1105
1106
1107
1108
1109

		for (id = 0; id < IB_UMAD_MAX_AGENTS; ++id)
			if (file->agent[id])
				ib_unregister_mad_agent(file->agent[id]);
	}