smc_core.c 20.8 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
 *  Shared Memory Communications over RDMA (SMC-R) and RoCE
 *
 *  Basic Transport Functions exploiting Infiniband API
 *
 *  Copyright IBM Corp. 2016
 *
 *  Author(s):  Ursula Braun <ubraun@linux.vnet.ibm.com>
 */

#include <linux/socket.h>
#include <linux/if_vlan.h>
#include <linux/random.h>
#include <linux/workqueue.h>
#include <net/tcp.h>
#include <net/sock.h>
#include <rdma/ib_verbs.h>

#include "smc.h"
#include "smc_clc.h"
#include "smc_core.h"
#include "smc_ib.h"
24
#include "smc_wr.h"
Ursula Braun's avatar
Ursula Braun committed
25
#include "smc_llc.h"
26
#include "smc_cdc.h"
27
#include "smc_close.h"
28

29
30
31
#define SMC_LGR_NUM_INCR		256
#define SMC_LGR_FREE_DELAY_SERV		(600 * HZ)
#define SMC_LGR_FREE_DELAY_CLNT		(SMC_LGR_FREE_DELAY_SERV + 10)
32

Ursula Braun's avatar
Ursula Braun committed
33
34
static u32 smc_lgr_num;			/* unique link group number */

35
36
37
static void smc_buf_free(struct smc_buf_desc *buf_desc, struct smc_link *lnk,
			 bool is_rmb);

38
39
40
41
42
43
44
45
46
47
48
static void smc_lgr_schedule_free_work(struct smc_link_group *lgr)
{
	/* client link group creation always follows the server link group
	 * creation. For client use a somewhat higher removal delay time,
	 * otherwise there is a risk of out-of-sync link groups.
	 */
	mod_delayed_work(system_wq, &lgr->free_work,
			 lgr->role == SMC_CLNT ? SMC_LGR_FREE_DELAY_CLNT :
						 SMC_LGR_FREE_DELAY_SERV);
}

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/* Register connection's alert token in our lookup structure.
 * To use rbtrees we have to implement our own insert core.
 * Requires @conns_lock
 * @smc		connection to register
 * Returns 0 on success, != otherwise.
 */
static void smc_lgr_add_alert_token(struct smc_connection *conn)
{
	struct rb_node **link, *parent = NULL;
	u32 token = conn->alert_token_local;

	link = &conn->lgr->conns_all.rb_node;
	while (*link) {
		struct smc_connection *cur = rb_entry(*link,
					struct smc_connection, alert_node);

		parent = *link;
		if (cur->alert_token_local > token)
			link = &parent->rb_left;
		else
			link = &parent->rb_right;
	}
	/* Put the new node there */
	rb_link_node(&conn->alert_node, parent, link);
	rb_insert_color(&conn->alert_node, &conn->lgr->conns_all);
}

/* Register connection in link group by assigning an alert token
 * registered in a search tree.
 * Requires @conns_lock
 * Note that '0' is a reserved value and not assigned.
 */
static void smc_lgr_register_conn(struct smc_connection *conn)
{
	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
	static atomic_t nexttoken = ATOMIC_INIT(0);

	/* find a new alert_token_local value not yet used by some connection
	 * in this link group
	 */
	sock_hold(&smc->sk); /* sock_put in smc_lgr_unregister_conn() */
	while (!conn->alert_token_local) {
		conn->alert_token_local = atomic_inc_return(&nexttoken);
		if (smc_lgr_find_conn(conn->alert_token_local, conn->lgr))
			conn->alert_token_local = 0;
	}
	smc_lgr_add_alert_token(conn);
	conn->lgr->conns_num++;
}

/* Unregister connection and reset the alert token of the given connection<
 */
static void __smc_lgr_unregister_conn(struct smc_connection *conn)
{
	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
	struct smc_link_group *lgr = conn->lgr;

	rb_erase(&conn->alert_node, &lgr->conns_all);
	lgr->conns_num--;
	conn->alert_token_local = 0;
	conn->lgr = NULL;
	sock_put(&smc->sk); /* sock_hold in smc_lgr_register_conn() */
}

/* Unregister connection and trigger lgr freeing if applicable
 */
static void smc_lgr_unregister_conn(struct smc_connection *conn)
{
	struct smc_link_group *lgr = conn->lgr;
	int reduced = 0;

	write_lock_bh(&lgr->conns_lock);
	if (conn->alert_token_local) {
		reduced = 1;
		__smc_lgr_unregister_conn(conn);
	}
	write_unlock_bh(&lgr->conns_lock);
126
127
	if (!reduced || lgr->conns_num)
		return;
128
	smc_lgr_schedule_free_work(lgr);
129
130
131
132
133
134
135
136
137
138
}

static void smc_lgr_free_work(struct work_struct *work)
{
	struct smc_link_group *lgr = container_of(to_delayed_work(work),
						  struct smc_link_group,
						  free_work);
	bool conns;

	spin_lock_bh(&smc_lgr_list.lock);
139
140
	if (list_empty(&lgr->list))
		goto free;
141
142
143
144
145
146
147
148
	read_lock_bh(&lgr->conns_lock);
	conns = RB_EMPTY_ROOT(&lgr->conns_all);
	read_unlock_bh(&lgr->conns_lock);
	if (!conns) { /* number of lgr connections is no longer zero */
		spin_unlock_bh(&smc_lgr_list.lock);
		return;
	}
	list_del_init(&lgr->list); /* remove from smc_lgr_list */
149
free:
150
	spin_unlock_bh(&smc_lgr_list.lock);
151
152
153
	if (!delayed_work_pending(&lgr->free_work)) {
		if (lgr->lnk[SMC_SINGLE_LINK].state != SMC_LNK_INACTIVE)
			smc_llc_link_inactive(&lgr->lnk[SMC_SINGLE_LINK]);
154
		smc_lgr_free(lgr);
155
	}
156
157
158
}

/* create a new SMC link group */
159
static int smc_lgr_create(struct smc_sock *smc,
160
161
162
163
164
165
166
			  struct smc_ib_device *smcibdev, u8 ibport,
			  char *peer_systemid, unsigned short vlan_id)
{
	struct smc_link_group *lgr;
	struct smc_link *lnk;
	u8 rndvec[3];
	int rc = 0;
167
	int i;
168
169
170
171
172
173
174
175
176
177

	lgr = kzalloc(sizeof(*lgr), GFP_KERNEL);
	if (!lgr) {
		rc = -ENOMEM;
		goto out;
	}
	lgr->role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
	lgr->sync_err = false;
	memcpy(lgr->peer_systemid, peer_systemid, SMC_SYSTEMID_LEN);
	lgr->vlan_id = vlan_id;
178
179
180
181
182
183
	rwlock_init(&lgr->sndbufs_lock);
	rwlock_init(&lgr->rmbs_lock);
	for (i = 0; i < SMC_RMBE_SIZES; i++) {
		INIT_LIST_HEAD(&lgr->sndbufs[i]);
		INIT_LIST_HEAD(&lgr->rmbs[i]);
	}
Ursula Braun's avatar
Ursula Braun committed
184
185
	smc_lgr_num += SMC_LGR_NUM_INCR;
	memcpy(&lgr->id, (u8 *)&smc_lgr_num, SMC_LGR_ID_SIZE);
186
187
188
189
190
	INIT_DELAYED_WORK(&lgr->free_work, smc_lgr_free_work);
	lgr->conns_all = RB_ROOT;

	lnk = &lgr->lnk[SMC_SINGLE_LINK];
	/* initialize link */
191
	lnk->state = SMC_LNK_ACTIVATING;
192
	lnk->link_id = SMC_SINGLE_LINK;
193
194
195
	lnk->smcibdev = smcibdev;
	lnk->ibport = ibport;
	lnk->path_mtu = smcibdev->pattr[ibport - 1].active_mtu;
196
197
	if (!smcibdev->initialized)
		smc_ib_setup_per_ibdev(smcibdev);
198
199
	get_random_bytes(rndvec, sizeof(rndvec));
	lnk->psn_initial = rndvec[0] + (rndvec[1] << 8) + (rndvec[2] << 16);
200
	rc = smc_llc_link_init(lnk);
201
202
	if (rc)
		goto free_lgr;
203
204
205
	rc = smc_wr_alloc_link_mem(lnk);
	if (rc)
		goto clear_llc_lnk;
206
207
208
209
210
211
212
213
214
	rc = smc_ib_create_protection_domain(lnk);
	if (rc)
		goto free_link_mem;
	rc = smc_ib_create_queue_pair(lnk);
	if (rc)
		goto dealloc_pd;
	rc = smc_wr_create_link(lnk);
	if (rc)
		goto destroy_qp;
215
216
217
218
219
220

	smc->conn.lgr = lgr;
	rwlock_init(&lgr->conns_lock);
	spin_lock_bh(&smc_lgr_list.lock);
	list_add(&lgr->list, &smc_lgr_list.list);
	spin_unlock_bh(&smc_lgr_list.lock);
221
222
	return 0;

223
224
225
226
227
228
destroy_qp:
	smc_ib_destroy_queue_pair(lnk);
dealloc_pd:
	smc_ib_dealloc_protection_domain(lnk);
free_link_mem:
	smc_wr_free_link_mem(lnk);
229
230
clear_llc_lnk:
	smc_llc_link_clear(lnk);
231
232
free_lgr:
	kfree(lgr);
233
234
235
236
out:
	return rc;
}

237
static void smc_buf_unuse(struct smc_connection *conn)
238
239
240
241
242
243
{
	if (conn->sndbuf_desc) {
		conn->sndbuf_desc->used = 0;
		conn->sndbuf_size = 0;
	}
	if (conn->rmb_desc) {
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
		if (!conn->rmb_desc->regerr) {
			conn->rmb_desc->reused = 1;
			conn->rmb_desc->used = 0;
			conn->rmbe_size = 0;
		} else {
			/* buf registration failed, reuse not possible */
			struct smc_link_group *lgr = conn->lgr;
			struct smc_link *lnk;

			write_lock_bh(&lgr->rmbs_lock);
			list_del(&conn->rmb_desc->list);
			write_unlock_bh(&lgr->rmbs_lock);

			lnk = &lgr->lnk[SMC_SINGLE_LINK];
			smc_buf_free(conn->rmb_desc, lnk, true);
		}
260
261
262
	}
}

263
264
265
/* remove a finished connection from its link group */
void smc_conn_free(struct smc_connection *conn)
{
266
	if (!conn->lgr)
267
		return;
268
	smc_cdc_tx_dismiss_slots(conn);
269
	smc_lgr_unregister_conn(conn);
270
	smc_buf_unuse(conn);
271
272
273
274
275
}

static void smc_link_clear(struct smc_link *lnk)
{
	lnk->peer_qpn = 0;
276
	smc_llc_link_clear(lnk);
277
	smc_ib_modify_qp_reset(lnk);
278
	smc_wr_free_link(lnk);
279
280
	smc_ib_destroy_queue_pair(lnk);
	smc_ib_dealloc_protection_domain(lnk);
281
	smc_wr_free_link_mem(lnk);
282
283
}

284
285
static void smc_buf_free(struct smc_buf_desc *buf_desc, struct smc_link *lnk,
			 bool is_rmb)
286
{
287
288
289
290
291
292
293
294
295
	if (is_rmb) {
		if (buf_desc->mr_rx[SMC_SINGLE_LINK])
			smc_ib_put_memory_region(
					buf_desc->mr_rx[SMC_SINGLE_LINK]);
		smc_ib_buf_unmap_sg(lnk->smcibdev, buf_desc,
				    DMA_FROM_DEVICE);
	} else {
		smc_ib_buf_unmap_sg(lnk->smcibdev, buf_desc,
				    DMA_TO_DEVICE);
296
	}
297
	sg_free_table(&buf_desc->sgt[SMC_SINGLE_LINK]);
298
299
	if (buf_desc->pages)
		__free_pages(buf_desc->pages, buf_desc->order);
300
	kfree(buf_desc);
301
302
}

303
static void __smc_lgr_free_bufs(struct smc_link_group *lgr, bool is_rmb)
304
{
305
	struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK];
306
307
	struct smc_buf_desc *buf_desc, *bf_desc;
	struct list_head *buf_list;
308
309
310
	int i;

	for (i = 0; i < SMC_RMBE_SIZES; i++) {
311
312
313
314
315
		if (is_rmb)
			buf_list = &lgr->rmbs[i];
		else
			buf_list = &lgr->sndbufs[i];
		list_for_each_entry_safe(buf_desc, bf_desc, buf_list,
316
					 list) {
317
318
			list_del(&buf_desc->list);
			smc_buf_free(buf_desc, lnk, is_rmb);
319
320
321
322
		}
	}
}

323
324
325
326
327
328
329
330
static void smc_lgr_free_bufs(struct smc_link_group *lgr)
{
	/* free send buffers */
	__smc_lgr_free_bufs(lgr, false);
	/* free rmbs */
	__smc_lgr_free_bufs(lgr, true);
}

331
332
333
/* remove a link group */
void smc_lgr_free(struct smc_link_group *lgr)
{
334
	smc_lgr_free_bufs(lgr);
335
336
337
338
	smc_link_clear(&lgr->lnk[SMC_SINGLE_LINK]);
	kfree(lgr);
}

339
340
341
342
343
344
345
346
347
void smc_lgr_forget(struct smc_link_group *lgr)
{
	spin_lock_bh(&smc_lgr_list.lock);
	/* do not use this link group for new connections */
	if (!list_empty(&lgr->list))
		list_del_init(&lgr->list);
	spin_unlock_bh(&smc_lgr_list.lock);
}

348
349
350
351
/* terminate linkgroup abnormally */
void smc_lgr_terminate(struct smc_link_group *lgr)
{
	struct smc_connection *conn;
352
	struct smc_sock *smc;
353
354
	struct rb_node *node;

355
	smc_lgr_forget(lgr);
356
	smc_llc_link_inactive(&lgr->lnk[SMC_SINGLE_LINK]);
357
358
359
360
361

	write_lock_bh(&lgr->conns_lock);
	node = rb_first(&lgr->conns_all);
	while (node) {
		conn = rb_entry(node, struct smc_connection, alert_node);
362
		smc = container_of(conn, struct smc_sock, conn);
363
		sock_hold(&smc->sk); /* sock_put in close work */
364
		conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
365
		__smc_lgr_unregister_conn(conn);
366
		write_unlock_bh(&lgr->conns_lock);
367
368
		if (!schedule_work(&conn->close_work))
			sock_put(&smc->sk);
369
		write_lock_bh(&lgr->conns_lock);
370
371
372
		node = rb_first(&lgr->conns_all);
	}
	write_unlock_bh(&lgr->conns_lock);
373
	wake_up(&lgr->lnk[SMC_SINGLE_LINK].wr_reg_wait);
374
	smc_lgr_schedule_free_work(lgr);
375
376
377
378
379
380
381
382
}

/* Determine vlan of internal TCP socket.
 * @vlan_id: address to store the determined vlan id into
 */
static int smc_vlan_by_tcpsk(struct socket *clcsock, unsigned short *vlan_id)
{
	struct dst_entry *dst = sk_dst_get(clcsock->sk);
383
384
	struct net_device *ndev;
	int i, nest_lvl, rc = 0;
385
386
387
388
389
390
391
392
393
394
395

	*vlan_id = 0;
	if (!dst) {
		rc = -ENOTCONN;
		goto out;
	}
	if (!dst->dev) {
		rc = -ENODEV;
		goto out_rel;
	}

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
	ndev = dst->dev;
	if (is_vlan_dev(ndev)) {
		*vlan_id = vlan_dev_vlan_id(ndev);
		goto out_rel;
	}

	rtnl_lock();
	nest_lvl = dev_get_nest_level(ndev);
	for (i = 0; i < nest_lvl; i++) {
		struct list_head *lower = &ndev->adj_list.lower;

		if (list_empty(lower))
			break;
		lower = lower->next;
		ndev = (struct net_device *)netdev_lower_get_next(ndev, &lower);
		if (is_vlan_dev(ndev)) {
			*vlan_id = vlan_dev_vlan_id(ndev);
			break;
		}
	}
	rtnl_unlock();
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441

out_rel:
	dst_release(dst);
out:
	return rc;
}

/* determine the link gid matching the vlan id of the link group */
static int smc_link_determine_gid(struct smc_link_group *lgr)
{
	struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK];
	struct ib_gid_attr gattr;
	union ib_gid gid;
	int i;

	if (!lgr->vlan_id) {
		lnk->gid = lnk->smcibdev->gid[lnk->ibport - 1];
		return 0;
	}

	for (i = 0; i < lnk->smcibdev->pattr[lnk->ibport - 1].gid_tbl_len;
	     i++) {
		if (ib_query_gid(lnk->smcibdev->ibdev, lnk->ibport, i, &gid,
				 &gattr))
			continue;
442
443
444
445
446
447
448
449
		if (gattr.ndev) {
			if (is_vlan_dev(gattr.ndev) &&
			    vlan_dev_vlan_id(gattr.ndev) == lgr->vlan_id) {
				lnk->gid = gid;
				dev_put(gattr.ndev);
				return 0;
			}
			dev_put(gattr.ndev);
450
451
452
453
454
455
		}
	}
	return -ENODEV;
}

/* create a new SMC connection (and a new link group if necessary) */
456
int smc_conn_create(struct smc_sock *smc,
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
		    struct smc_ib_device *smcibdev, u8 ibport,
		    struct smc_clc_msg_local *lcl, int srv_first_contact)
{
	struct smc_connection *conn = &smc->conn;
	struct smc_link_group *lgr;
	unsigned short vlan_id;
	enum smc_lgr_role role;
	int local_contact = SMC_FIRST_CONTACT;
	int rc = 0;

	role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
	rc = smc_vlan_by_tcpsk(smc->clcsock, &vlan_id);
	if (rc)
		return rc;

	if ((role == SMC_CLNT) && srv_first_contact)
		/* create new link group as well */
		goto create;

	/* determine if an existing link group can be reused */
	spin_lock_bh(&smc_lgr_list.lock);
	list_for_each_entry(lgr, &smc_lgr_list.list, list) {
		write_lock_bh(&lgr->conns_lock);
		if (!memcmp(lgr->peer_systemid, lcl->id_for_peer,
			    SMC_SYSTEMID_LEN) &&
		    !memcmp(lgr->lnk[SMC_SINGLE_LINK].peer_gid, &lcl->gid,
			    SMC_GID_SIZE) &&
		    !memcmp(lgr->lnk[SMC_SINGLE_LINK].peer_mac, lcl->mac,
			    sizeof(lcl->mac)) &&
		    !lgr->sync_err &&
		    (lgr->role == role) &&
488
489
490
		    (lgr->vlan_id == vlan_id) &&
		    ((role == SMC_CLNT) ||
		     (lgr->conns_num < SMC_RMBS_PER_LGR_MAX))) {
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
			/* link group found */
			local_contact = SMC_REUSE_CONTACT;
			conn->lgr = lgr;
			smc_lgr_register_conn(conn); /* add smc conn to lgr */
			write_unlock_bh(&lgr->conns_lock);
			break;
		}
		write_unlock_bh(&lgr->conns_lock);
	}
	spin_unlock_bh(&smc_lgr_list.lock);

	if (role == SMC_CLNT && !srv_first_contact &&
	    (local_contact == SMC_FIRST_CONTACT)) {
		/* Server reuses a link group, but Client wants to start
		 * a new one
		 * send out_of_sync decline, reason synchr. error
		 */
		return -ENOLINK;
	}

create:
	if (local_contact == SMC_FIRST_CONTACT) {
513
		rc = smc_lgr_create(smc, smcibdev, ibport,
514
515
516
517
518
519
				    lcl->id_for_peer, vlan_id);
		if (rc)
			goto out;
		smc_lgr_register_conn(conn); /* add smc conn to lgr */
		rc = smc_link_determine_gid(conn->lgr);
	}
520
	conn->local_tx_ctrl.common.type = SMC_CDC_MSG_TYPE;
521
	conn->local_tx_ctrl.len = SMC_WR_TX_SIZE;
522
523
524
#ifndef KERNEL_HAS_ATOMIC64
	spin_lock_init(&conn->acurs_lock);
#endif
525
526
527
528

out:
	return rc ? rc : local_contact;
}
529

530
531
/* try to reuse a sndbuf or rmb description slot for a certain
 * buffer size; if not available, return NULL
532
533
 */
static inline
534
535
536
537
struct smc_buf_desc *smc_buf_get_slot(struct smc_link_group *lgr,
				      int compressed_bufsize,
				      rwlock_t *lock,
				      struct list_head *buf_list)
538
{
539
	struct smc_buf_desc *buf_slot;
540

541
542
543
544
545
	read_lock_bh(lock);
	list_for_each_entry(buf_slot, buf_list, list) {
		if (cmpxchg(&buf_slot->used, 0, 1) == 0) {
			read_unlock_bh(lock);
			return buf_slot;
546
547
		}
	}
548
	read_unlock_bh(lock);
549
550
551
	return NULL;
}

Ursula Braun's avatar
Ursula Braun committed
552
553
554
555
556
557
558
559
560
/* one of the conditions for announcing a receiver's current window size is
 * that it "results in a minimum increase in the window size of 10% of the
 * receive buffer space" [RFC7609]
 */
static inline int smc_rmb_wnd_update_limit(int rmbe_size)
{
	return min_t(int, rmbe_size / 10, SOCK_MIN_SNDBUF / 2);
}

561
562
563
564
565
566
567
568
569
570
571
572
static struct smc_buf_desc *smc_new_buf_create(struct smc_link_group *lgr,
					       bool is_rmb, int bufsize)
{
	struct smc_buf_desc *buf_desc;
	struct smc_link *lnk;
	int rc;

	/* try to alloc a new buffer */
	buf_desc = kzalloc(sizeof(*buf_desc), GFP_KERNEL);
	if (!buf_desc)
		return ERR_PTR(-ENOMEM);

573
574
575
576
577
578
	buf_desc->order = get_order(bufsize);
	buf_desc->pages = alloc_pages(GFP_KERNEL | __GFP_NOWARN |
				      __GFP_NOMEMALLOC | __GFP_COMP |
				      __GFP_NORETRY | __GFP_ZERO,
				      buf_desc->order);
	if (!buf_desc->pages) {
579
580
581
		kfree(buf_desc);
		return ERR_PTR(-EAGAIN);
	}
582
	buf_desc->cpu_addr = (void *)page_address(buf_desc->pages);
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618

	/* build the sg table from the pages */
	lnk = &lgr->lnk[SMC_SINGLE_LINK];
	rc = sg_alloc_table(&buf_desc->sgt[SMC_SINGLE_LINK], 1,
			    GFP_KERNEL);
	if (rc) {
		smc_buf_free(buf_desc, lnk, is_rmb);
		return ERR_PTR(rc);
	}
	sg_set_buf(buf_desc->sgt[SMC_SINGLE_LINK].sgl,
		   buf_desc->cpu_addr, bufsize);

	/* map sg table to DMA address */
	rc = smc_ib_buf_map_sg(lnk->smcibdev, buf_desc,
			       is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
	/* SMC protocol depends on mapping to one DMA address only */
	if (rc != 1)  {
		smc_buf_free(buf_desc, lnk, is_rmb);
		return ERR_PTR(-EAGAIN);
	}

	/* create a new memory region for the RMB */
	if (is_rmb) {
		rc = smc_ib_get_memory_region(lnk->roce_pd,
					      IB_ACCESS_REMOTE_WRITE |
					      IB_ACCESS_LOCAL_WRITE,
					      buf_desc);
		if (rc) {
			smc_buf_free(buf_desc, lnk, is_rmb);
			return ERR_PTR(rc);
		}
	}

	return buf_desc;
}

619
static int __smc_buf_create(struct smc_sock *smc, bool is_rmb)
620
621
622
{
	struct smc_connection *conn = &smc->conn;
	struct smc_link_group *lgr = conn->lgr;
623
	struct smc_buf_desc *buf_desc = ERR_PTR(-ENOMEM);
624
	struct list_head *buf_list;
625
	int bufsize, bufsize_short;
626
627
	int sk_buf_size;
	rwlock_t *lock;
628

629
630
631
632
633
634
635
	if (is_rmb)
		/* use socket recv buffer size (w/o overhead) as start value */
		sk_buf_size = smc->sk.sk_rcvbuf / 2;
	else
		/* use socket send buffer size (w/o overhead) as start value */
		sk_buf_size = smc->sk.sk_sndbuf / 2;

636
	for (bufsize_short = smc_compress_bufsize(sk_buf_size);
637
	     bufsize_short >= 0; bufsize_short--) {
638

639
640
641
642
643
644
		if (is_rmb) {
			lock = &lgr->rmbs_lock;
			buf_list = &lgr->rmbs[bufsize_short];
		} else {
			lock = &lgr->sndbufs_lock;
			buf_list = &lgr->sndbufs[bufsize_short];
645
		}
646
		bufsize = smc_uncompress_bufsize(bufsize_short);
647
648
649
		if ((1 << get_order(bufsize)) > SG_MAX_SINGLE_ALLOC)
			continue;

650
651
652
653
		/* check for reusable slot in the link group */
		buf_desc = smc_buf_get_slot(lgr, bufsize_short, lock, buf_list);
		if (buf_desc) {
			memset(buf_desc->cpu_addr, 0, bufsize);
654
655
			break; /* found reusable slot */
		}
656

657
658
659
660
		buf_desc = smc_new_buf_create(lgr, is_rmb, bufsize);
		if (PTR_ERR(buf_desc) == -ENOMEM)
			break;
		if (IS_ERR(buf_desc))
661
			continue;
662

663
664
665
666
667
		buf_desc->used = 1;
		write_lock_bh(lock);
		list_add(&buf_desc->list, buf_list);
		write_unlock_bh(lock);
		break; /* found */
668
	}
669

670
	if (IS_ERR(buf_desc))
671
672
673
674
		return -ENOMEM;

	if (is_rmb) {
		conn->rmb_desc = buf_desc;
675
676
677
		conn->rmbe_size = bufsize;
		conn->rmbe_size_short = bufsize_short;
		smc->sk.sk_rcvbuf = bufsize * 2;
678
		atomic_set(&conn->bytes_to_rcv, 0);
679
		conn->rmbe_update_limit = smc_rmb_wnd_update_limit(bufsize);
680
	} else {
681
682
683
684
		conn->sndbuf_desc = buf_desc;
		conn->sndbuf_size = bufsize;
		smc->sk.sk_sndbuf = bufsize * 2;
		atomic_set(&conn->sndbuf_space, bufsize);
685
	}
686
687
688
	return 0;
}

689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
void smc_sndbuf_sync_sg_for_cpu(struct smc_connection *conn)
{
	struct smc_link_group *lgr = conn->lgr;

	smc_ib_sync_sg_for_cpu(lgr->lnk[SMC_SINGLE_LINK].smcibdev,
			       conn->sndbuf_desc, DMA_TO_DEVICE);
}

void smc_sndbuf_sync_sg_for_device(struct smc_connection *conn)
{
	struct smc_link_group *lgr = conn->lgr;

	smc_ib_sync_sg_for_device(lgr->lnk[SMC_SINGLE_LINK].smcibdev,
				  conn->sndbuf_desc, DMA_TO_DEVICE);
}

void smc_rmb_sync_sg_for_cpu(struct smc_connection *conn)
{
	struct smc_link_group *lgr = conn->lgr;

	smc_ib_sync_sg_for_cpu(lgr->lnk[SMC_SINGLE_LINK].smcibdev,
			       conn->rmb_desc, DMA_FROM_DEVICE);
}

void smc_rmb_sync_sg_for_device(struct smc_connection *conn)
{
	struct smc_link_group *lgr = conn->lgr;

	smc_ib_sync_sg_for_device(lgr->lnk[SMC_SINGLE_LINK].smcibdev,
				  conn->rmb_desc, DMA_FROM_DEVICE);
}

721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
/* create the send and receive buffer for an SMC socket;
 * receive buffers are called RMBs;
 * (even though the SMC protocol allows more than one RMB-element per RMB,
 * the Linux implementation uses just one RMB-element per RMB, i.e. uses an
 * extra RMB for every connection in a link group
 */
int smc_buf_create(struct smc_sock *smc)
{
	int rc;

	/* create send buffer */
	rc = __smc_buf_create(smc, false);
	if (rc)
		return rc;
	/* create rmb */
	rc = __smc_buf_create(smc, true);
	if (rc)
		smc_buf_free(smc->conn.sndbuf_desc,
			     &smc->conn.lgr->lnk[SMC_SINGLE_LINK], false);
	return rc;
741
}
742
743
744
745
746
747
748
749
750
751
752
753

static inline int smc_rmb_reserve_rtoken_idx(struct smc_link_group *lgr)
{
	int i;

	for_each_clear_bit(i, lgr->rtokens_used_mask, SMC_RMBS_PER_LGR_MAX) {
		if (!test_and_set_bit(i, lgr->rtokens_used_mask))
			return i;
	}
	return -ENOSPC;
}

754
755
/* add a new rtoken from peer */
int smc_rtoken_add(struct smc_link_group *lgr, __be64 nw_vaddr, __be32 nw_rkey)
756
{
757
758
	u64 dma_addr = be64_to_cpu(nw_vaddr);
	u32 rkey = ntohl(nw_rkey);
759
760
761
762
	int i;

	for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
		if ((lgr->rtokens[i][SMC_SINGLE_LINK].rkey == rkey) &&
763
		    (lgr->rtokens[i][SMC_SINGLE_LINK].dma_addr == dma_addr) &&
764
		    test_bit(i, lgr->rtokens_used_mask)) {
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
			/* already in list */
			return i;
		}
	}
	i = smc_rmb_reserve_rtoken_idx(lgr);
	if (i < 0)
		return i;
	lgr->rtokens[i][SMC_SINGLE_LINK].rkey = rkey;
	lgr->rtokens[i][SMC_SINGLE_LINK].dma_addr = dma_addr;
	return i;
}

/* delete an rtoken */
int smc_rtoken_delete(struct smc_link_group *lgr, __be32 nw_rkey)
{
	u32 rkey = ntohl(nw_rkey);
	int i;

	for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
		if (lgr->rtokens[i][SMC_SINGLE_LINK].rkey == rkey &&
		    test_bit(i, lgr->rtokens_used_mask)) {
			lgr->rtokens[i][SMC_SINGLE_LINK].rkey = 0;
			lgr->rtokens[i][SMC_SINGLE_LINK].dma_addr = 0;

			clear_bit(i, lgr->rtokens_used_mask);
790
791
792
			return 0;
		}
	}
793
794
795
796
797
798
799
800
801
	return -ENOENT;
}

/* save rkey and dma_addr received from peer during clc handshake */
int smc_rmb_rtoken_handling(struct smc_connection *conn,
			    struct smc_clc_msg_accept_confirm *clc)
{
	conn->rtoken_idx = smc_rtoken_add(conn->lgr, clc->rmb_dma_addr,
					  clc->rmb_rkey);
802
803
804
805
	if (conn->rtoken_idx < 0)
		return conn->rtoken_idx;
	return 0;
}