Blame view

fs/dlm/lowcomms.c 36.2 KB
fdda387f7   Patrick Caulfield   [DLM] Add support...
1
2
3
4
  /******************************************************************************
  *******************************************************************************
  **
  **  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
a34fbc636   Patrick Caulfield   [DLM] fix softloc...
5
  **  Copyright (C) 2004-2007 Red Hat, Inc.  All rights reserved.
fdda387f7   Patrick Caulfield   [DLM] Add support...
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
  **
  **  This copyrighted material is made available to anyone wishing to use,
  **  modify, copy, or redistribute it subject to the terms and conditions
  **  of the GNU General Public License v.2.
  **
  *******************************************************************************
  ******************************************************************************/
  
  /*
   * lowcomms.c
   *
   * This is the "low-level" comms layer.
   *
   * It is responsible for sending/receiving messages
   * from other nodes in the cluster.
   *
   * Cluster nodes are referred to by their nodeids. nodeids are
   * simply 32 bit numbers to the locking module - if they need to
   * be expanded for the cluster infrastructure then that is it's
   * responsibility. It is this layer's
   * responsibility to resolve these into IP address or
   * whatever it needs for inter-node communication.
   *
   * The comms level is two kernel threads that deal mainly with
   * the receiving of messages from other nodes and passing them
   * up to the mid-level comms layer (which understands the
   * message format) for execution by the locking core, and
   * a send thread which does all the setting up of connections
   * to remote nodes and the sending of data. Threads are not allowed
   * to send their own data because it may cause them to wait in times
   * of high load. Also, this way, the sending thread can collect together
   * messages bound for one node and send them in one block.
   *
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
39
40
41
42
43
   * lowcomms will choose to use wither TCP or SCTP as its transport layer
   * depending on the configuration variable 'protocol'. This should be set
   * to 0 (default) for TCP or 1 for SCTP. It shouldbe configured using a
   * cluster-wide mechanism as it must be the same on all nodes of the cluster
   * for the DLM to function.
fdda387f7   Patrick Caulfield   [DLM] Add support...
44
45
   *
   */
fdda387f7   Patrick Caulfield   [DLM] Add support...
46
47
48
49
  #include <asm/ioctls.h>
  #include <net/sock.h>
  #include <net/tcp.h>
  #include <linux/pagemap.h>
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
50
51
  #include <linux/idr.h>
  #include <linux/file.h>
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
52
  #include <linux/mutex.h>
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
53
54
  #include <linux/sctp.h>
  #include <net/sctp/user.h>
fdda387f7   Patrick Caulfield   [DLM] Add support...
55
56
57
58
59
  
  #include "dlm_internal.h"
  #include "lowcomms.h"
  #include "midcomms.h"
  #include "config.h"
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
60
  #define NEEDED_RMEM (4*1024*1024)
fdda387f7   Patrick Caulfield   [DLM] Add support...
61
  struct cbuf {
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
62
63
64
  	unsigned int base;
  	unsigned int len;
  	unsigned int mask;
fdda387f7   Patrick Caulfield   [DLM] Add support...
65
  };
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
66
67
68
69
  static void cbuf_add(struct cbuf *cb, int n)
  {
  	cb->len += n;
  }
fdda387f7   Patrick Caulfield   [DLM] Add support...
70

ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  static int cbuf_data(struct cbuf *cb)
  {
  	return ((cb->base + cb->len) & cb->mask);
  }
  
  static void cbuf_init(struct cbuf *cb, int size)
  {
  	cb->base = cb->len = 0;
  	cb->mask = size-1;
  }
  
  static void cbuf_eat(struct cbuf *cb, int n)
  {
  	cb->len  -= n;
  	cb->base += n;
  	cb->base &= cb->mask;
  }
  
  static bool cbuf_empty(struct cbuf *cb)
  {
  	return cb->len == 0;
  }
fdda387f7   Patrick Caulfield   [DLM] Add support...
93

fdda387f7   Patrick Caulfield   [DLM] Add support...
94
95
96
  struct connection {
  	struct socket *sock;	/* NULL if not connected */
  	uint32_t nodeid;	/* So we know who we are in the list */
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
97
  	struct mutex sock_mutex;
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
98
  	unsigned long flags;
fdda387f7   Patrick Caulfield   [DLM] Add support...
99
100
101
  #define CF_READ_PENDING 1
  #define CF_WRITE_PENDING 2
  #define CF_CONNECT_PENDING 3
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
102
103
  #define CF_INIT_PENDING 4
  #define CF_IS_OTHERCON 5
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
104
  	struct list_head writequeue;  /* List of outgoing writequeue_entries */
fdda387f7   Patrick Caulfield   [DLM] Add support...
105
106
  	spinlock_t writequeue_lock;
  	int (*rx_action) (struct connection *);	/* What to do when active */
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
107
  	void (*connect_action) (struct connection *);	/* What to do to connect */
fdda387f7   Patrick Caulfield   [DLM] Add support...
108
109
110
  	struct page *rx_page;
  	struct cbuf cb;
  	int retries;
fdda387f7   Patrick Caulfield   [DLM] Add support...
111
  #define MAX_CONNECT_RETRIES 3
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
112
  	int sctp_assoc;
fdda387f7   Patrick Caulfield   [DLM] Add support...
113
  	struct connection *othercon;
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
114
115
  	struct work_struct rwork; /* Receive workqueue */
  	struct work_struct swork; /* Send workqueue */
fdda387f7   Patrick Caulfield   [DLM] Add support...
116
117
118
119
120
121
122
123
124
125
126
127
128
  };
  #define sock2con(x) ((struct connection *)(x)->sk_user_data)
  
  /* An entry waiting to be sent */
  struct writequeue_entry {
  	struct list_head list;
  	struct page *page;
  	int offset;
  	int len;
  	int end;
  	int users;
  	struct connection *con;
  };
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
129
130
  static struct sockaddr_storage *dlm_local_addr[DLM_MAX_ADDR_COUNT];
  static int dlm_local_count;
fdda387f7   Patrick Caulfield   [DLM] Add support...
131

1d6e8131c   Patrick Caulfield   [DLM] Use workque...
132
133
134
  /* Work queues */
  static struct workqueue_struct *recv_workqueue;
  static struct workqueue_struct *send_workqueue;
fdda387f7   Patrick Caulfield   [DLM] Add support...
135

6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
136
  static DEFINE_IDR(connections_idr);
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
137
  static DEFINE_MUTEX(connections_lock);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
138
  static int max_nodeid;
c80e7c83d   Patrick Caulfield   [DLM] fix compile...
139
  static struct kmem_cache *con_cache;
fdda387f7   Patrick Caulfield   [DLM] Add support...
140

1d6e8131c   Patrick Caulfield   [DLM] Use workque...
141
142
  static void process_recv_sockets(struct work_struct *work);
  static void process_send_sockets(struct work_struct *work);
fdda387f7   Patrick Caulfield   [DLM] Add support...
143

6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
144
145
146
147
148
  /*
   * If 'allocation' is zero then we don't attempt to create a new
   * connection structure for this node.
   */
  static struct connection *__nodeid2con(int nodeid, gfp_t alloc)
fdda387f7   Patrick Caulfield   [DLM] Add support...
149
150
  {
  	struct connection *con = NULL;
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
151
152
  	int r;
  	int n;
fdda387f7   Patrick Caulfield   [DLM] Add support...
153

6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
154
155
156
  	con = idr_find(&connections_idr, nodeid);
  	if (con || !alloc)
  		return con;
fdda387f7   Patrick Caulfield   [DLM] Add support...
157

6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
158
159
160
161
162
163
164
  	r = idr_pre_get(&connections_idr, alloc);
  	if (!r)
  		return NULL;
  
  	con = kmem_cache_zalloc(con_cache, alloc);
  	if (!con)
  		return NULL;
fdda387f7   Patrick Caulfield   [DLM] Add support...
165

6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
166
167
168
169
170
  	r = idr_get_new_above(&connections_idr, con, nodeid, &n);
  	if (r) {
  		kmem_cache_free(con_cache, con);
  		return NULL;
  	}
fdda387f7   Patrick Caulfield   [DLM] Add support...
171

6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
172
173
174
175
  	if (n != nodeid) {
  		idr_remove(&connections_idr, n);
  		kmem_cache_free(con_cache, con);
  		return NULL;
fdda387f7   Patrick Caulfield   [DLM] Add support...
176
  	}
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
177
178
179
180
181
182
  	con->nodeid = nodeid;
  	mutex_init(&con->sock_mutex);
  	INIT_LIST_HEAD(&con->writequeue);
  	spin_lock_init(&con->writequeue_lock);
  	INIT_WORK(&con->swork, process_send_sockets);
  	INIT_WORK(&con->rwork, process_recv_sockets);
fdda387f7   Patrick Caulfield   [DLM] Add support...
183

6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
184
185
186
  	/* Setup action pointers for child sockets */
  	if (con->nodeid) {
  		struct connection *zerocon = idr_find(&connections_idr, 0);
fdda387f7   Patrick Caulfield   [DLM] Add support...
187

6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
188
189
190
  		con->connect_action = zerocon->connect_action;
  		if (!con->rx_action)
  			con->rx_action = zerocon->rx_action;
fdda387f7   Patrick Caulfield   [DLM] Add support...
191
  	}
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
192
193
194
195
196
197
198
199
200
  	if (nodeid > max_nodeid)
  		max_nodeid = nodeid;
  
  	return con;
  }
  
  static struct connection *nodeid2con(int nodeid, gfp_t allocation)
  {
  	struct connection *con;
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
201
  	mutex_lock(&connections_lock);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
202
  	con = __nodeid2con(nodeid, allocation);
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
203
  	mutex_unlock(&connections_lock);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
204

fdda387f7   Patrick Caulfield   [DLM] Add support...
205
206
  	return con;
  }
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
207
208
209
210
211
  /* This is a bit drastic, but only called when things go wrong */
  static struct connection *assoc2con(int assoc_id)
  {
  	int i;
  	struct connection *con;
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
212
  	mutex_lock(&connections_lock);
30d3a2373   Patrick Caulfield   [DLM] Lowcomms no...
213
  	for (i=0; i<=max_nodeid; i++) {
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
214
215
  		con = __nodeid2con(i, 0);
  		if (con && con->sctp_assoc == assoc_id) {
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
216
  			mutex_unlock(&connections_lock);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
217
218
219
  			return con;
  		}
  	}
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
220
  	mutex_unlock(&connections_lock);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
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
  	return NULL;
  }
  
  static int nodeid_to_addr(int nodeid, struct sockaddr *retaddr)
  {
  	struct sockaddr_storage addr;
  	int error;
  
  	if (!dlm_local_count)
  		return -1;
  
  	error = dlm_nodeid_to_addr(nodeid, &addr);
  	if (error)
  		return error;
  
  	if (dlm_local_addr[0]->ss_family == AF_INET) {
  		struct sockaddr_in *in4  = (struct sockaddr_in *) &addr;
  		struct sockaddr_in *ret4 = (struct sockaddr_in *) retaddr;
  		ret4->sin_addr.s_addr = in4->sin_addr.s_addr;
  	} else {
  		struct sockaddr_in6 *in6  = (struct sockaddr_in6 *) &addr;
  		struct sockaddr_in6 *ret6 = (struct sockaddr_in6 *) retaddr;
  		memcpy(&ret6->sin6_addr, &in6->sin6_addr,
  		       sizeof(in6->sin6_addr));
  	}
  
  	return 0;
  }
fdda387f7   Patrick Caulfield   [DLM] Add support...
249
250
251
252
  /* Data available on socket or listen socket received a connect */
  static void lowcomms_data_ready(struct sock *sk, int count_unused)
  {
  	struct connection *con = sock2con(sk);
afb853fb4   Patrick Caulfield   [DLM] fix socket ...
253
  	if (con && !test_and_set_bit(CF_READ_PENDING, &con->flags))
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
254
  		queue_work(recv_workqueue, &con->rwork);
fdda387f7   Patrick Caulfield   [DLM] Add support...
255
256
257
258
259
  }
  
  static void lowcomms_write_space(struct sock *sk)
  {
  	struct connection *con = sock2con(sk);
afb853fb4   Patrick Caulfield   [DLM] fix socket ...
260
  	if (con && !test_and_set_bit(CF_WRITE_PENDING, &con->flags))
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
261
  		queue_work(send_workqueue, &con->swork);
fdda387f7   Patrick Caulfield   [DLM] Add support...
262
263
264
265
  }
  
  static inline void lowcomms_connect_sock(struct connection *con)
  {
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
266
267
  	if (!test_and_set_bit(CF_CONNECT_PENDING, &con->flags))
  		queue_work(send_workqueue, &con->swork);
fdda387f7   Patrick Caulfield   [DLM] Add support...
268
269
270
271
  }
  
  static void lowcomms_state_change(struct sock *sk)
  {
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
272
  	if (sk->sk_state == TCP_ESTABLISHED)
fdda387f7   Patrick Caulfield   [DLM] Add support...
273
  		lowcomms_write_space(sk);
fdda387f7   Patrick Caulfield   [DLM] Add support...
274
275
276
277
278
279
280
281
282
283
284
  }
  
  /* Make a socket active */
  static int add_sock(struct socket *sock, struct connection *con)
  {
  	con->sock = sock;
  
  	/* Install a data_ready callback */
  	con->sock->sk->sk_data_ready = lowcomms_data_ready;
  	con->sock->sk->sk_write_space = lowcomms_write_space;
  	con->sock->sk->sk_state_change = lowcomms_state_change;
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
285
  	con->sock->sk->sk_user_data = con;
fdda387f7   Patrick Caulfield   [DLM] Add support...
286
287
  	return 0;
  }
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
288
  /* Add the port number to an IPv6 or 4 sockaddr and return the address
fdda387f7   Patrick Caulfield   [DLM] Add support...
289
290
291
292
     length */
  static void make_sockaddr(struct sockaddr_storage *saddr, uint16_t port,
  			  int *addr_len)
  {
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
293
  	saddr->ss_family =  dlm_local_addr[0]->ss_family;
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
294
  	if (saddr->ss_family == AF_INET) {
fdda387f7   Patrick Caulfield   [DLM] Add support...
295
296
297
  		struct sockaddr_in *in4_addr = (struct sockaddr_in *)saddr;
  		in4_addr->sin_port = cpu_to_be16(port);
  		*addr_len = sizeof(struct sockaddr_in);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
298
  		memset(&in4_addr->sin_zero, 0, sizeof(in4_addr->sin_zero));
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
299
  	} else {
fdda387f7   Patrick Caulfield   [DLM] Add support...
300
301
302
303
  		struct sockaddr_in6 *in6_addr = (struct sockaddr_in6 *)saddr;
  		in6_addr->sin6_port = cpu_to_be16(port);
  		*addr_len = sizeof(struct sockaddr_in6);
  	}
01c8cab25   Patrick Caulfield   [DLM] zero unused...
304
  	memset((char *)saddr + *addr_len, 0, sizeof(struct sockaddr_storage) - *addr_len);
fdda387f7   Patrick Caulfield   [DLM] Add support...
305
306
307
  }
  
  /* Close a remote connection and tidy up */
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
308
  static void close_connection(struct connection *con, bool and_other)
fdda387f7   Patrick Caulfield   [DLM] Add support...
309
  {
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
310
  	mutex_lock(&con->sock_mutex);
fdda387f7   Patrick Caulfield   [DLM] Add support...
311
312
313
314
315
316
  
  	if (con->sock) {
  		sock_release(con->sock);
  		con->sock = NULL;
  	}
  	if (con->othercon && and_other) {
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
317
318
  		/* Will only re-enter once. */
  		close_connection(con->othercon, false);
fdda387f7   Patrick Caulfield   [DLM] Add support...
319
320
321
322
323
  	}
  	if (con->rx_page) {
  		__free_page(con->rx_page);
  		con->rx_page = NULL;
  	}
9e5f2825a   Patrick Caulfield   [DLM] More otherc...
324

61d96be0f   Patrick Caulfield   [DLM] Fix lowcomm...
325
326
  	con->retries = 0;
  	mutex_unlock(&con->sock_mutex);
fdda387f7   Patrick Caulfield   [DLM] Add support...
327
  }
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
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
356
357
358
359
360
361
362
363
364
365
366
367
368
369
  /* We only send shutdown messages to nodes that are not part of the cluster */
  static void sctp_send_shutdown(sctp_assoc_t associd)
  {
  	static char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
  	struct msghdr outmessage;
  	struct cmsghdr *cmsg;
  	struct sctp_sndrcvinfo *sinfo;
  	int ret;
  	struct connection *con;
  
  	con = nodeid2con(0,0);
  	BUG_ON(con == NULL);
  
  	outmessage.msg_name = NULL;
  	outmessage.msg_namelen = 0;
  	outmessage.msg_control = outcmsg;
  	outmessage.msg_controllen = sizeof(outcmsg);
  	outmessage.msg_flags = MSG_EOR;
  
  	cmsg = CMSG_FIRSTHDR(&outmessage);
  	cmsg->cmsg_level = IPPROTO_SCTP;
  	cmsg->cmsg_type = SCTP_SNDRCV;
  	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
  	outmessage.msg_controllen = cmsg->cmsg_len;
  	sinfo = CMSG_DATA(cmsg);
  	memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo));
  
  	sinfo->sinfo_flags |= MSG_EOF;
  	sinfo->sinfo_assoc_id = associd;
  
  	ret = kernel_sendmsg(con->sock, &outmessage, NULL, 0, 0);
  
  	if (ret != 0)
  		log_print("send EOF to node failed: %d", ret);
  }
  
  /* INIT failed but we don't know which node...
     restart INIT on all pending nodes */
  static void sctp_init_failed(void)
  {
  	int i;
  	struct connection *con;
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
370
  	mutex_lock(&connections_lock);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
371
372
373
374
375
376
377
378
379
380
381
  	for (i=1; i<=max_nodeid; i++) {
  		con = __nodeid2con(i, 0);
  		if (!con)
  			continue;
  		con->sctp_assoc = 0;
  		if (test_and_clear_bit(CF_CONNECT_PENDING, &con->flags)) {
  			if (!test_and_set_bit(CF_WRITE_PENDING, &con->flags)) {
  				queue_work(send_workqueue, &con->swork);
  			}
  		}
  	}
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
382
  	mutex_unlock(&connections_lock);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
383
384
385
  }
  
  /* Something happened to an association */
617e82e10   David Teigland   [DLM] lowcomms style
386
387
  static void process_sctp_notification(struct connection *con,
  				      struct msghdr *msg, char *buf)
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  {
  	union sctp_notification *sn = (union sctp_notification *)buf;
  
  	if (sn->sn_header.sn_type == SCTP_ASSOC_CHANGE) {
  		switch (sn->sn_assoc_change.sac_state) {
  
  		case SCTP_COMM_UP:
  		case SCTP_RESTART:
  		{
  			/* Check that the new node is in the lockspace */
  			struct sctp_prim prim;
  			int nodeid;
  			int prim_len, ret;
  			int addr_len;
  			struct connection *new_con;
  			struct file *file;
  			sctp_peeloff_arg_t parg;
  			int parglen = sizeof(parg);
  
  			/*
  			 * We get this before any data for an association.
  			 * We verify that the node is in the cluster and
  			 * then peel off a socket for it.
  			 */
  			if ((int)sn->sn_assoc_change.sac_assoc_id <= 0) {
  				log_print("COMM_UP for invalid assoc ID %d",
617e82e10   David Teigland   [DLM] lowcomms style
414
  					 (int)sn->sn_assoc_change.sac_assoc_id);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
415
416
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
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
  				sctp_init_failed();
  				return;
  			}
  			memset(&prim, 0, sizeof(struct sctp_prim));
  			prim_len = sizeof(struct sctp_prim);
  			prim.ssp_assoc_id = sn->sn_assoc_change.sac_assoc_id;
  
  			ret = kernel_getsockopt(con->sock,
  						IPPROTO_SCTP,
  						SCTP_PRIMARY_ADDR,
  						(char*)&prim,
  						&prim_len);
  			if (ret < 0) {
  				log_print("getsockopt/sctp_primary_addr on "
  					  "new assoc %d failed : %d",
  					  (int)sn->sn_assoc_change.sac_assoc_id,
  					  ret);
  
  				/* Retry INIT later */
  				new_con = assoc2con(sn->sn_assoc_change.sac_assoc_id);
  				if (new_con)
  					clear_bit(CF_CONNECT_PENDING, &con->flags);
  				return;
  			}
  			make_sockaddr(&prim.ssp_addr, 0, &addr_len);
  			if (dlm_addr_to_nodeid(&prim.ssp_addr, &nodeid)) {
  				int i;
  				unsigned char *b=(unsigned char *)&prim.ssp_addr;
  				log_print("reject connect from unknown addr");
  				for (i=0; i<sizeof(struct sockaddr_storage);i++)
  					printk("%02x ", b[i]);
  				printk("
  ");
  				sctp_send_shutdown(prim.ssp_assoc_id);
  				return;
  			}
  
  			new_con = nodeid2con(nodeid, GFP_KERNEL);
  			if (!new_con)
  				return;
  
  			/* Peel off a new sock */
  			parg.associd = sn->sn_assoc_change.sac_assoc_id;
617e82e10   David Teigland   [DLM] lowcomms style
458
459
  			ret = kernel_getsockopt(con->sock, IPPROTO_SCTP,
  						SCTP_SOCKOPT_PEELOFF,
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
460
  						(void *)&parg, &parglen);
30d3a2373   Patrick Caulfield   [DLM] Lowcomms no...
461
  			if (ret) {
617e82e10   David Teigland   [DLM] lowcomms style
462
463
464
  				log_print("Can't peel off a socket for "
  					  "connection %d to node %d: err=%d
  ",
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
465
  					  parg.associd, nodeid, ret);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
466
  			}
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
467
468
469
470
471
472
473
  			file = fget(parg.sd);
  			new_con->sock = SOCKET_I(file->f_dentry->d_inode);
  			add_sock(new_con->sock, new_con);
  			fput(file);
  			put_unused_fd(parg.sd);
  
  			log_print("got new/restarted association %d nodeid %d",
617e82e10   David Teigland   [DLM] lowcomms style
474
  				 (int)sn->sn_assoc_change.sac_assoc_id, nodeid);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
  
  			/* Send any pending writes */
  			clear_bit(CF_CONNECT_PENDING, &new_con->flags);
  			clear_bit(CF_INIT_PENDING, &con->flags);
  			if (!test_and_set_bit(CF_WRITE_PENDING, &new_con->flags)) {
  				queue_work(send_workqueue, &new_con->swork);
  			}
  			if (!test_and_set_bit(CF_READ_PENDING, &new_con->flags))
  				queue_work(recv_workqueue, &new_con->rwork);
  		}
  		break;
  
  		case SCTP_COMM_LOST:
  		case SCTP_SHUTDOWN_COMP:
  		{
  			con = assoc2con(sn->sn_assoc_change.sac_assoc_id);
  			if (con) {
  				con->sctp_assoc = 0;
  			}
  		}
  		break;
  
  		/* We don't know which INIT failed, so clear the PENDING flags
  		 * on them all.  if assoc_id is zero then it will then try
  		 * again */
  
  		case SCTP_CANT_STR_ASSOC:
  		{
  			log_print("Can't start SCTP association - retrying");
  			sctp_init_failed();
  		}
  		break;
  
  		default:
  			log_print("unexpected SCTP assoc change id=%d state=%d",
  				  (int)sn->sn_assoc_change.sac_assoc_id,
  				  sn->sn_assoc_change.sac_state);
  		}
  	}
  }
fdda387f7   Patrick Caulfield   [DLM] Add support...
515
516
517
518
  /* Data received from remote end */
  static int receive_from_sock(struct connection *con)
  {
  	int ret = 0;
58addbffd   Al Viro   [PATCH] dlm: use ...
519
520
  	struct msghdr msg = {};
  	struct kvec iov[2];
fdda387f7   Patrick Caulfield   [DLM] Add support...
521
522
523
  	unsigned len;
  	int r;
  	int call_again_soon = 0;
58addbffd   Al Viro   [PATCH] dlm: use ...
524
  	int nvec;
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
525
  	char incmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
fdda387f7   Patrick Caulfield   [DLM] Add support...
526

f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
527
  	mutex_lock(&con->sock_mutex);
fdda387f7   Patrick Caulfield   [DLM] Add support...
528

a34fbc636   Patrick Caulfield   [DLM] fix softloc...
529
530
531
532
  	if (con->sock == NULL) {
  		ret = -EAGAIN;
  		goto out_close;
  	}
fdda387f7   Patrick Caulfield   [DLM] Add support...
533
534
535
536
537
538
539
540
  	if (con->rx_page == NULL) {
  		/*
  		 * This doesn't need to be atomic, but I think it should
  		 * improve performance if it is.
  		 */
  		con->rx_page = alloc_page(GFP_ATOMIC);
  		if (con->rx_page == NULL)
  			goto out_resched;
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
541
  		cbuf_init(&con->cb, PAGE_CACHE_SIZE);
fdda387f7   Patrick Caulfield   [DLM] Add support...
542
  	}
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
543
544
545
546
  	/* Only SCTP needs these really */
  	memset(&incmsg, 0, sizeof(incmsg));
  	msg.msg_control = incmsg;
  	msg.msg_controllen = sizeof(incmsg);
fdda387f7   Patrick Caulfield   [DLM] Add support...
547
548
549
550
  	/*
  	 * iov[0] is the bit of the circular buffer between the current end
  	 * point (cb.base + cb.len) and the end of the buffer.
  	 */
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
551
552
  	iov[0].iov_len = con->cb.base - cbuf_data(&con->cb);
  	iov[0].iov_base = page_address(con->rx_page) + cbuf_data(&con->cb);
89adc934f   Patrick Caulfield   [DLM] Fix uniniti...
553
  	iov[1].iov_len = 0;
58addbffd   Al Viro   [PATCH] dlm: use ...
554
  	nvec = 1;
fdda387f7   Patrick Caulfield   [DLM] Add support...
555
556
557
558
559
  
  	/*
  	 * iov[1] is the bit of the circular buffer between the start of the
  	 * buffer and the start of the currently used section (cb.base)
  	 */
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
560
561
  	if (cbuf_data(&con->cb) >= con->cb.base) {
  		iov[0].iov_len = PAGE_CACHE_SIZE - cbuf_data(&con->cb);
fdda387f7   Patrick Caulfield   [DLM] Add support...
562
563
  		iov[1].iov_len = con->cb.base;
  		iov[1].iov_base = page_address(con->rx_page);
58addbffd   Al Viro   [PATCH] dlm: use ...
564
  		nvec = 2;
fdda387f7   Patrick Caulfield   [DLM] Add support...
565
566
  	}
  	len = iov[0].iov_len + iov[1].iov_len;
58addbffd   Al Viro   [PATCH] dlm: use ...
567
  	r = ret = kernel_recvmsg(con->sock, &msg, iov, nvec, len,
fdda387f7   Patrick Caulfield   [DLM] Add support...
568
  			       MSG_DONTWAIT | MSG_NOSIGNAL);
fdda387f7   Patrick Caulfield   [DLM] Add support...
569
570
  	if (ret <= 0)
  		goto out_close;
bd44e2b00   Patrick Caulfield   [DLM] fix lowcomm...
571

6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
572
573
  	/* Process SCTP notifications */
  	if (msg.msg_flags & MSG_NOTIFICATION) {
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
574
575
576
577
  		msg.msg_control = incmsg;
  		msg.msg_controllen = sizeof(incmsg);
  
  		process_sctp_notification(con, &msg,
617e82e10   David Teigland   [DLM] lowcomms style
578
  				page_address(con->rx_page) + con->cb.base);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
579
580
581
582
  		mutex_unlock(&con->sock_mutex);
  		return 0;
  	}
  	BUG_ON(con->nodeid == 0);
fdda387f7   Patrick Caulfield   [DLM] Add support...
583
584
  	if (ret == len)
  		call_again_soon = 1;
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
585
  	cbuf_add(&con->cb, ret);
fdda387f7   Patrick Caulfield   [DLM] Add support...
586
587
588
589
590
  	ret = dlm_process_incoming_buffer(con->nodeid,
  					  page_address(con->rx_page),
  					  con->cb.base, con->cb.len,
  					  PAGE_CACHE_SIZE);
  	if (ret == -EBADMSG) {
617e82e10   David Teigland   [DLM] lowcomms style
591
592
593
594
  		log_print("lowcomms: addr=%p, base=%u, len=%u, "
  			  "iov_len=%u, iov_base[0]=%p, read=%d",
  			  page_address(con->rx_page), con->cb.base, con->cb.len,
  			  len, iov[0].iov_base, r);
fdda387f7   Patrick Caulfield   [DLM] Add support...
595
596
597
  	}
  	if (ret < 0)
  		goto out_close;
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
598
  	cbuf_eat(&con->cb, ret);
fdda387f7   Patrick Caulfield   [DLM] Add support...
599

ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
600
  	if (cbuf_empty(&con->cb) && !call_again_soon) {
fdda387f7   Patrick Caulfield   [DLM] Add support...
601
602
603
  		__free_page(con->rx_page);
  		con->rx_page = NULL;
  	}
fdda387f7   Patrick Caulfield   [DLM] Add support...
604
605
  	if (call_again_soon)
  		goto out_resched;
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
606
  	mutex_unlock(&con->sock_mutex);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
607
  	return 0;
fdda387f7   Patrick Caulfield   [DLM] Add support...
608

ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
609
  out_resched:
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
610
611
  	if (!test_and_set_bit(CF_READ_PENDING, &con->flags))
  		queue_work(recv_workqueue, &con->rwork);
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
612
  	mutex_unlock(&con->sock_mutex);
bd44e2b00   Patrick Caulfield   [DLM] fix lowcomm...
613
  	return -EAGAIN;
fdda387f7   Patrick Caulfield   [DLM] Add support...
614

ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
615
  out_close:
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
616
  	mutex_unlock(&con->sock_mutex);
9e5f2825a   Patrick Caulfield   [DLM] More otherc...
617
  	if (ret != -EAGAIN) {
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
618
  		close_connection(con, false);
fdda387f7   Patrick Caulfield   [DLM] Add support...
619
620
  		/* Reconnect when there is something to send */
  	}
a34fbc636   Patrick Caulfield   [DLM] fix softloc...
621
622
623
  	/* Don't return success if we really got EOF */
  	if (ret == 0)
  		ret = -EAGAIN;
fdda387f7   Patrick Caulfield   [DLM] Add support...
624

fdda387f7   Patrick Caulfield   [DLM] Add support...
625
626
627
628
  	return ret;
  }
  
  /* Listening socket is busy, accept a connection */
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
629
  static int tcp_accept_from_sock(struct connection *con)
fdda387f7   Patrick Caulfield   [DLM] Add support...
630
631
632
633
634
635
636
  {
  	int result;
  	struct sockaddr_storage peeraddr;
  	struct socket *newsock;
  	int len;
  	int nodeid;
  	struct connection *newcon;
bd44e2b00   Patrick Caulfield   [DLM] fix lowcomm...
637
  	struct connection *addcon;
fdda387f7   Patrick Caulfield   [DLM] Add support...
638
639
  
  	memset(&peeraddr, 0, sizeof(peeraddr));
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
640
  	result = sock_create_kern(dlm_local_addr[0]->ss_family, SOCK_STREAM,
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
641
  				  IPPROTO_TCP, &newsock);
fdda387f7   Patrick Caulfield   [DLM] Add support...
642
643
  	if (result < 0)
  		return -ENOMEM;
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
644
  	mutex_lock_nested(&con->sock_mutex, 0);
fdda387f7   Patrick Caulfield   [DLM] Add support...
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
  
  	result = -ENOTCONN;
  	if (con->sock == NULL)
  		goto accept_err;
  
  	newsock->type = con->sock->type;
  	newsock->ops = con->sock->ops;
  
  	result = con->sock->ops->accept(con->sock, newsock, O_NONBLOCK);
  	if (result < 0)
  		goto accept_err;
  
  	/* Get the connected socket's peer */
  	memset(&peeraddr, 0, sizeof(peeraddr));
  	if (newsock->ops->getname(newsock, (struct sockaddr *)&peeraddr,
  				  &len, 2)) {
  		result = -ECONNABORTED;
  		goto accept_err;
  	}
  
  	/* Get the new node's NODEID */
  	make_sockaddr(&peeraddr, 0, &len);
  	if (dlm_addr_to_nodeid(&peeraddr, &nodeid)) {
617e82e10   David Teigland   [DLM] lowcomms style
668
  		log_print("connect from non cluster node");
fdda387f7   Patrick Caulfield   [DLM] Add support...
669
  		sock_release(newsock);
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
670
  		mutex_unlock(&con->sock_mutex);
fdda387f7   Patrick Caulfield   [DLM] Add support...
671
672
673
674
675
676
677
678
  		return -1;
  	}
  
  	log_print("got connection from %d", nodeid);
  
  	/*  Check to see if we already have a connection to this node. This
  	 *  could happen if the two nodes initiate a connection at roughly
  	 *  the same time and the connections cross on the wire.
fdda387f7   Patrick Caulfield   [DLM] Add support...
679
680
681
682
683
684
685
  	 *  In this case we store the incoming one in "othercon"
  	 */
  	newcon = nodeid2con(nodeid, GFP_KERNEL);
  	if (!newcon) {
  		result = -ENOMEM;
  		goto accept_err;
  	}
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
686
  	mutex_lock_nested(&newcon->sock_mutex, 1);
fdda387f7   Patrick Caulfield   [DLM] Add support...
687
  	if (newcon->sock) {
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
688
  		struct connection *othercon = newcon->othercon;
fdda387f7   Patrick Caulfield   [DLM] Add support...
689
690
  
  		if (!othercon) {
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
691
  			othercon = kmem_cache_zalloc(con_cache, GFP_KERNEL);
fdda387f7   Patrick Caulfield   [DLM] Add support...
692
  			if (!othercon) {
617e82e10   David Teigland   [DLM] lowcomms style
693
  				log_print("failed to allocate incoming socket");
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
694
  				mutex_unlock(&newcon->sock_mutex);
fdda387f7   Patrick Caulfield   [DLM] Add support...
695
696
697
  				result = -ENOMEM;
  				goto accept_err;
  			}
fdda387f7   Patrick Caulfield   [DLM] Add support...
698
699
  			othercon->nodeid = nodeid;
  			othercon->rx_action = receive_from_sock;
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
700
  			mutex_init(&othercon->sock_mutex);
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
701
702
  			INIT_WORK(&othercon->swork, process_send_sockets);
  			INIT_WORK(&othercon->rwork, process_recv_sockets);
fdda387f7   Patrick Caulfield   [DLM] Add support...
703
  			set_bit(CF_IS_OTHERCON, &othercon->flags);
61d96be0f   Patrick Caulfield   [DLM] Fix lowcomm...
704
705
  		}
  		if (!othercon->sock) {
fdda387f7   Patrick Caulfield   [DLM] Add support...
706
  			newcon->othercon = othercon;
97d848365   Patrick Caulfield   [DLM] Telnet to p...
707
708
709
710
711
712
713
714
715
  			othercon->sock = newsock;
  			newsock->sk->sk_user_data = othercon;
  			add_sock(newsock, othercon);
  			addcon = othercon;
  		}
  		else {
  			printk("Extra connection from node %d attempted
  ", nodeid);
  			result = -EAGAIN;
f4fadb23c   akpm@linux-foundation.org   [GFS2] git-gfs2-n...
716
  			mutex_unlock(&newcon->sock_mutex);
97d848365   Patrick Caulfield   [DLM] Telnet to p...
717
  			goto accept_err;
fdda387f7   Patrick Caulfield   [DLM] Add support...
718
  		}
fdda387f7   Patrick Caulfield   [DLM] Add support...
719
720
721
722
723
  	}
  	else {
  		newsock->sk->sk_user_data = newcon;
  		newcon->rx_action = receive_from_sock;
  		add_sock(newsock, newcon);
bd44e2b00   Patrick Caulfield   [DLM] fix lowcomm...
724
  		addcon = newcon;
fdda387f7   Patrick Caulfield   [DLM] Add support...
725
  	}
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
726
  	mutex_unlock(&newcon->sock_mutex);
fdda387f7   Patrick Caulfield   [DLM] Add support...
727
728
729
730
731
732
  
  	/*
  	 * Add it to the active queue in case we got data
  	 * beween processing the accept adding the socket
  	 * to the read_sockets list
  	 */
bd44e2b00   Patrick Caulfield   [DLM] fix lowcomm...
733
734
  	if (!test_and_set_bit(CF_READ_PENDING, &addcon->flags))
  		queue_work(recv_workqueue, &addcon->rwork);
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
735
  	mutex_unlock(&con->sock_mutex);
fdda387f7   Patrick Caulfield   [DLM] Add support...
736
737
  
  	return 0;
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
738
  accept_err:
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
739
  	mutex_unlock(&con->sock_mutex);
fdda387f7   Patrick Caulfield   [DLM] Add support...
740
741
742
  	sock_release(newsock);
  
  	if (result != -EAGAIN)
617e82e10   David Teigland   [DLM] lowcomms style
743
  		log_print("error accepting connection from node: %d", result);
fdda387f7   Patrick Caulfield   [DLM] Add support...
744
745
  	return result;
  }
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
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
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
  static void free_entry(struct writequeue_entry *e)
  {
  	__free_page(e->page);
  	kfree(e);
  }
  
  /* Initiate an SCTP association.
     This is a special case of send_to_sock() in that we don't yet have a
     peeled-off socket for this association, so we use the listening socket
     and add the primary IP address of the remote node.
   */
  static void sctp_init_assoc(struct connection *con)
  {
  	struct sockaddr_storage rem_addr;
  	char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
  	struct msghdr outmessage;
  	struct cmsghdr *cmsg;
  	struct sctp_sndrcvinfo *sinfo;
  	struct connection *base_con;
  	struct writequeue_entry *e;
  	int len, offset;
  	int ret;
  	int addrlen;
  	struct kvec iov[1];
  
  	if (test_and_set_bit(CF_INIT_PENDING, &con->flags))
  		return;
  
  	if (con->retries++ > MAX_CONNECT_RETRIES)
  		return;
  
  	log_print("Initiating association with node %d", con->nodeid);
  
  	if (nodeid_to_addr(con->nodeid, (struct sockaddr *)&rem_addr)) {
  		log_print("no address for nodeid %d", con->nodeid);
  		return;
  	}
  	base_con = nodeid2con(0, 0);
  	BUG_ON(base_con == NULL);
  
  	make_sockaddr(&rem_addr, dlm_config.ci_tcp_port, &addrlen);
  
  	outmessage.msg_name = &rem_addr;
  	outmessage.msg_namelen = addrlen;
  	outmessage.msg_control = outcmsg;
  	outmessage.msg_controllen = sizeof(outcmsg);
  	outmessage.msg_flags = MSG_EOR;
  
  	spin_lock(&con->writequeue_lock);
  	e = list_entry(con->writequeue.next, struct writequeue_entry,
  		       list);
  
  	BUG_ON((struct list_head *) e == &con->writequeue);
  
  	len = e->len;
  	offset = e->offset;
  	spin_unlock(&con->writequeue_lock);
  	kmap(e->page);
  
  	/* Send the first block off the write queue */
  	iov[0].iov_base = page_address(e->page)+offset;
  	iov[0].iov_len = len;
  
  	cmsg = CMSG_FIRSTHDR(&outmessage);
  	cmsg->cmsg_level = IPPROTO_SCTP;
  	cmsg->cmsg_type = SCTP_SNDRCV;
  	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
  	sinfo = CMSG_DATA(cmsg);
  	memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo));
  	sinfo->sinfo_ppid = cpu_to_le32(dlm_our_nodeid());
  	outmessage.msg_controllen = cmsg->cmsg_len;
  
  	ret = kernel_sendmsg(base_con->sock, &outmessage, iov, 1, len);
  	if (ret < 0) {
617e82e10   David Teigland   [DLM] lowcomms style
820
821
  		log_print("Send first packet to node %d failed: %d",
  			  con->nodeid, ret);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
  
  		/* Try again later */
  		clear_bit(CF_CONNECT_PENDING, &con->flags);
  		clear_bit(CF_INIT_PENDING, &con->flags);
  	}
  	else {
  		spin_lock(&con->writequeue_lock);
  		e->offset += ret;
  		e->len -= ret;
  
  		if (e->len == 0 && e->users == 0) {
  			list_del(&e->list);
  			kunmap(e->page);
  			free_entry(e);
  		}
  		spin_unlock(&con->writequeue_lock);
  	}
  }
fdda387f7   Patrick Caulfield   [DLM] Add support...
840
  /* Connect a new socket to its peer */
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
841
  static void tcp_connect_to_sock(struct connection *con)
fdda387f7   Patrick Caulfield   [DLM] Add support...
842
843
  {
  	int result = -EHOSTUNREACH;
6bd8fedaa   Lon Hohberger   dlm: bind connect...
844
  	struct sockaddr_storage saddr, src_addr;
fdda387f7   Patrick Caulfield   [DLM] Add support...
845
846
847
848
849
  	int addr_len;
  	struct socket *sock;
  
  	if (con->nodeid == 0) {
  		log_print("attempt to connect sock 0 foiled");
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
850
  		return;
fdda387f7   Patrick Caulfield   [DLM] Add support...
851
  	}
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
852
  	mutex_lock(&con->sock_mutex);
fdda387f7   Patrick Caulfield   [DLM] Add support...
853
854
855
856
857
858
859
860
861
862
  	if (con->retries++ > MAX_CONNECT_RETRIES)
  		goto out;
  
  	/* Some odd races can cause double-connects, ignore them */
  	if (con->sock) {
  		result = 0;
  		goto out;
  	}
  
  	/* Create a socket to communicate with */
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
863
  	result = sock_create_kern(dlm_local_addr[0]->ss_family, SOCK_STREAM,
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
864
  				  IPPROTO_TCP, &sock);
fdda387f7   Patrick Caulfield   [DLM] Add support...
865
866
867
868
869
  	if (result < 0)
  		goto out_err;
  
  	memset(&saddr, 0, sizeof(saddr));
  	if (dlm_nodeid_to_addr(con->nodeid, &saddr))
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
870
  		goto out_err;
fdda387f7   Patrick Caulfield   [DLM] Add support...
871
872
873
  
  	sock->sk->sk_user_data = con;
  	con->rx_action = receive_from_sock;
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
874
875
  	con->connect_action = tcp_connect_to_sock;
  	add_sock(sock, con);
fdda387f7   Patrick Caulfield   [DLM] Add support...
876

6bd8fedaa   Lon Hohberger   dlm: bind connect...
877
878
879
880
881
882
883
884
885
886
  	/* Bind to our cluster-known address connecting to avoid
  	   routing problems */
  	memcpy(&src_addr, dlm_local_addr[0], sizeof(src_addr));
  	make_sockaddr(&src_addr, 0, &addr_len);
  	result = sock->ops->bind(sock, (struct sockaddr *) &src_addr,
  				 addr_len);
  	if (result < 0) {
  		log_print("could not bind for connect: %d", result);
  		/* This *may* not indicate a critical error */
  	}
68c817a1c   David Teigland   [DLM] rename dlm_...
887
  	make_sockaddr(&saddr, dlm_config.ci_tcp_port, &addr_len);
fdda387f7   Patrick Caulfield   [DLM] Add support...
888

fdda387f7   Patrick Caulfield   [DLM] Add support...
889
890
891
  	log_print("connecting to %d", con->nodeid);
  	result =
  		sock->ops->connect(sock, (struct sockaddr *)&saddr, addr_len,
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
892
  				   O_NONBLOCK);
fdda387f7   Patrick Caulfield   [DLM] Add support...
893
894
  	if (result == -EINPROGRESS)
  		result = 0;
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
895
896
  	if (result == 0)
  		goto out;
fdda387f7   Patrick Caulfield   [DLM] Add support...
897

ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
898
  out_err:
fdda387f7   Patrick Caulfield   [DLM] Add support...
899
900
901
902
903
904
905
906
907
  	if (con->sock) {
  		sock_release(con->sock);
  		con->sock = NULL;
  	}
  	/*
  	 * Some errors are fatal and this list might need adjusting. For other
  	 * errors we try again until the max number of retries is reached.
  	 */
  	if (result != -EHOSTUNREACH && result != -ENETUNREACH &&
0035a4b14   Marcin Slusarz   dlm: tcp_connect_...
908
  	    result != -ENETDOWN && result != -EINVAL
fdda387f7   Patrick Caulfield   [DLM] Add support...
909
910
911
912
  	    && result != -EPROTONOSUPPORT) {
  		lowcomms_connect_sock(con);
  		result = 0;
  	}
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
913
  out:
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
914
  	mutex_unlock(&con->sock_mutex);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
915
  	return;
fdda387f7   Patrick Caulfield   [DLM] Add support...
916
  }
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
917
918
  static struct socket *tcp_create_listen_sock(struct connection *con,
  					     struct sockaddr_storage *saddr)
fdda387f7   Patrick Caulfield   [DLM] Add support...
919
  {
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
920
  	struct socket *sock = NULL;
fdda387f7   Patrick Caulfield   [DLM] Add support...
921
922
923
  	int result = 0;
  	int one = 1;
  	int addr_len;
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
924
  	if (dlm_local_addr[0]->ss_family == AF_INET)
fdda387f7   Patrick Caulfield   [DLM] Add support...
925
926
927
928
929
  		addr_len = sizeof(struct sockaddr_in);
  	else
  		addr_len = sizeof(struct sockaddr_in6);
  
  	/* Create a socket to communicate with */
617e82e10   David Teigland   [DLM] lowcomms style
930
931
  	result = sock_create_kern(dlm_local_addr[0]->ss_family, SOCK_STREAM,
  				  IPPROTO_TCP, &sock);
fdda387f7   Patrick Caulfield   [DLM] Add support...
932
  	if (result < 0) {
617e82e10   David Teigland   [DLM] lowcomms style
933
  		log_print("Can't create listening comms socket");
fdda387f7   Patrick Caulfield   [DLM] Add support...
934
935
  		goto create_out;
  	}
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
936
937
  	result = kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
  				   (char *)&one, sizeof(one));
fdda387f7   Patrick Caulfield   [DLM] Add support...
938
  	if (result < 0) {
617e82e10   David Teigland   [DLM] lowcomms style
939
  		log_print("Failed to set SO_REUSEADDR on socket: %d", result);
fdda387f7   Patrick Caulfield   [DLM] Add support...
940
941
  	}
  	sock->sk->sk_user_data = con;
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
942
943
  	con->rx_action = tcp_accept_from_sock;
  	con->connect_action = tcp_connect_to_sock;
fdda387f7   Patrick Caulfield   [DLM] Add support...
944
945
946
  	con->sock = sock;
  
  	/* Bind to our port */
68c817a1c   David Teigland   [DLM] rename dlm_...
947
  	make_sockaddr(saddr, dlm_config.ci_tcp_port, &addr_len);
fdda387f7   Patrick Caulfield   [DLM] Add support...
948
949
  	result = sock->ops->bind(sock, (struct sockaddr *) saddr, addr_len);
  	if (result < 0) {
617e82e10   David Teigland   [DLM] lowcomms style
950
  		log_print("Can't bind to port %d", dlm_config.ci_tcp_port);
fdda387f7   Patrick Caulfield   [DLM] Add support...
951
952
953
954
955
  		sock_release(sock);
  		sock = NULL;
  		con->sock = NULL;
  		goto create_out;
  	}
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
956
  	result = kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
957
  				 (char *)&one, sizeof(one));
fdda387f7   Patrick Caulfield   [DLM] Add support...
958
  	if (result < 0) {
617e82e10   David Teigland   [DLM] lowcomms style
959
  		log_print("Set keepalive failed: %d", result);
fdda387f7   Patrick Caulfield   [DLM] Add support...
960
961
962
963
  	}
  
  	result = sock->ops->listen(sock, 5);
  	if (result < 0) {
617e82e10   David Teigland   [DLM] lowcomms style
964
  		log_print("Can't listen on port %d", dlm_config.ci_tcp_port);
fdda387f7   Patrick Caulfield   [DLM] Add support...
965
966
967
968
  		sock_release(sock);
  		sock = NULL;
  		goto create_out;
  	}
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
969
  create_out:
fdda387f7   Patrick Caulfield   [DLM] Add support...
970
971
  	return sock;
  }
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
972
973
974
975
976
  /* Get local addresses */
  static void init_local(void)
  {
  	struct sockaddr_storage sas, *addr;
  	int i;
30d3a2373   Patrick Caulfield   [DLM] Lowcomms no...
977
  	dlm_local_count = 0;
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
978
979
980
981
982
983
984
985
986
987
988
  	for (i = 0; i < DLM_MAX_ADDR_COUNT - 1; i++) {
  		if (dlm_our_addr(&sas, i))
  			break;
  
  		addr = kmalloc(sizeof(*addr), GFP_KERNEL);
  		if (!addr)
  			break;
  		memcpy(addr, &sas, sizeof(*addr));
  		dlm_local_addr[dlm_local_count++] = addr;
  	}
  }
617e82e10   David Teigland   [DLM] lowcomms style
989
990
991
992
993
  /* Bind to an IP address. SCTP allows multiple address so it can do
     multi-homing */
  static int add_sctp_bind_addr(struct connection *sctp_con,
  			      struct sockaddr_storage *addr,
  			      int addr_len, int num)
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
  {
  	int result = 0;
  
  	if (num == 1)
  		result = kernel_bind(sctp_con->sock,
  				     (struct sockaddr *) addr,
  				     addr_len);
  	else
  		result = kernel_setsockopt(sctp_con->sock, SOL_SCTP,
  					   SCTP_SOCKOPT_BINDX_ADD,
  					   (char *)addr, addr_len);
  
  	if (result < 0)
  		log_print("Can't bind to port %d addr number %d",
  			  dlm_config.ci_tcp_port, num);
  
  	return result;
  }
fdda387f7   Patrick Caulfield   [DLM] Add support...
1012

6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
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
  /* Initialise SCTP socket and bind to all interfaces */
  static int sctp_listen_for_all(void)
  {
  	struct socket *sock = NULL;
  	struct sockaddr_storage localaddr;
  	struct sctp_event_subscribe subscribe;
  	int result = -EINVAL, num = 1, i, addr_len;
  	struct connection *con = nodeid2con(0, GFP_KERNEL);
  	int bufsize = NEEDED_RMEM;
  
  	if (!con)
  		return -ENOMEM;
  
  	log_print("Using SCTP for communications");
  
  	result = sock_create_kern(dlm_local_addr[0]->ss_family, SOCK_SEQPACKET,
  				  IPPROTO_SCTP, &sock);
  	if (result < 0) {
  		log_print("Can't create comms socket, check SCTP is loaded");
  		goto out;
  	}
  
  	/* Listen for events */
  	memset(&subscribe, 0, sizeof(subscribe));
  	subscribe.sctp_data_io_event = 1;
  	subscribe.sctp_association_event = 1;
  	subscribe.sctp_send_failure_event = 1;
  	subscribe.sctp_shutdown_event = 1;
  	subscribe.sctp_partial_delivery_event = 1;
df61c9526   David S. Miller   [DLM] lowcomms: D...
1042
  	result = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVBUFFORCE,
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1043
1044
  				 (char *)&bufsize, sizeof(bufsize));
  	if (result)
617e82e10   David Teigland   [DLM] lowcomms style
1045
  		log_print("Error increasing buffer space on socket %d", result);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1046
1047
  
  	result = kernel_setsockopt(sock, SOL_SCTP, SCTP_EVENTS,
617e82e10   David Teigland   [DLM] lowcomms style
1048
  				   (char *)&subscribe, sizeof(subscribe));
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
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
  	if (result < 0) {
  		log_print("Failed to set SCTP_EVENTS on socket: result=%d",
  			  result);
  		goto create_delsock;
  	}
  
  	/* Init con struct */
  	sock->sk->sk_user_data = con;
  	con->sock = sock;
  	con->sock->sk->sk_data_ready = lowcomms_data_ready;
  	con->rx_action = receive_from_sock;
  	con->connect_action = sctp_init_assoc;
  
  	/* Bind to all interfaces. */
  	for (i = 0; i < dlm_local_count; i++) {
  		memcpy(&localaddr, dlm_local_addr[i], sizeof(localaddr));
  		make_sockaddr(&localaddr, dlm_config.ci_tcp_port, &addr_len);
  
  		result = add_sctp_bind_addr(con, &localaddr, addr_len, num);
  		if (result)
  			goto create_delsock;
  		++num;
  	}
  
  	result = sock->ops->listen(sock, 5);
  	if (result < 0) {
  		log_print("Can't set socket listening");
  		goto create_delsock;
  	}
  
  	return 0;
  
  create_delsock:
  	sock_release(sock);
  	con->sock = NULL;
  out:
  	return result;
  }
  
  static int tcp_listen_for_all(void)
fdda387f7   Patrick Caulfield   [DLM] Add support...
1089
1090
1091
1092
  {
  	struct socket *sock = NULL;
  	struct connection *con = nodeid2con(0, GFP_KERNEL);
  	int result = -EINVAL;
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1093
1094
  	if (!con)
  		return -ENOMEM;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1095
  	/* We don't support multi-homed hosts */
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1096
  	if (dlm_local_addr[1] != NULL) {
617e82e10   David Teigland   [DLM] lowcomms style
1097
1098
  		log_print("TCP protocol can't handle multi-homed hosts, "
  			  "try SCTP");
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1099
1100
1101
1102
  		return -EINVAL;
  	}
  
  	log_print("Using TCP for communications");
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1103
  	sock = tcp_create_listen_sock(con, dlm_local_addr[0]);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
  	if (sock) {
  		add_sock(sock, con);
  		result = 0;
  	}
  	else {
  		result = -EADDRINUSE;
  	}
  
  	return result;
  }
  
  
  
  static struct writequeue_entry *new_writequeue_entry(struct connection *con,
  						     gfp_t allocation)
  {
  	struct writequeue_entry *entry;
  
  	entry = kmalloc(sizeof(struct writequeue_entry), allocation);
  	if (!entry)
  		return NULL;
  
  	entry->page = alloc_page(allocation);
  	if (!entry->page) {
  		kfree(entry);
  		return NULL;
  	}
  
  	entry->offset = 0;
  	entry->len = 0;
  	entry->end = 0;
  	entry->users = 0;
  	entry->con = con;
  
  	return entry;
  }
617e82e10   David Teigland   [DLM] lowcomms style
1140
  void *dlm_lowcomms_get_buffer(int nodeid, int len, gfp_t allocation, char **ppc)
fdda387f7   Patrick Caulfield   [DLM] Add support...
1141
1142
1143
1144
1145
  {
  	struct connection *con;
  	struct writequeue_entry *e;
  	int offset = 0;
  	int users = 0;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1146
1147
1148
  	con = nodeid2con(nodeid, allocation);
  	if (!con)
  		return NULL;
4edde74ee   Patrick Caulfield   [DLM] Fix spin lo...
1149
  	spin_lock(&con->writequeue_lock);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1150
  	e = list_entry(con->writequeue.prev, struct writequeue_entry, list);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1151
  	if ((&e->list == &con->writequeue) ||
fdda387f7   Patrick Caulfield   [DLM] Add support...
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
  	    (PAGE_CACHE_SIZE - e->end < len)) {
  		e = NULL;
  	} else {
  		offset = e->end;
  		e->end += len;
  		users = e->users++;
  	}
  	spin_unlock(&con->writequeue_lock);
  
  	if (e) {
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1162
  	got_one:
fdda387f7   Patrick Caulfield   [DLM] Add support...
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
  		if (users == 0)
  			kmap(e->page);
  		*ppc = page_address(e->page) + offset;
  		return e;
  	}
  
  	e = new_writequeue_entry(con, allocation);
  	if (e) {
  		spin_lock(&con->writequeue_lock);
  		offset = e->end;
  		e->end += len;
  		users = e->users++;
  		list_add_tail(&e->list, &con->writequeue);
  		spin_unlock(&con->writequeue_lock);
  		goto got_one;
  	}
  	return NULL;
  }
  
  void dlm_lowcomms_commit_buffer(void *mh)
  {
  	struct writequeue_entry *e = (struct writequeue_entry *)mh;
  	struct connection *con = e->con;
  	int users;
4edde74ee   Patrick Caulfield   [DLM] Fix spin lo...
1187
  	spin_lock(&con->writequeue_lock);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1188
1189
1190
1191
1192
1193
  	users = --e->users;
  	if (users)
  		goto out;
  	e->len = e->end - e->offset;
  	kunmap(e->page);
  	spin_unlock(&con->writequeue_lock);
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1194
1195
  	if (!test_and_set_bit(CF_WRITE_PENDING, &con->flags)) {
  		queue_work(send_workqueue, &con->swork);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1196
1197
  	}
  	return;
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1198
  out:
fdda387f7   Patrick Caulfield   [DLM] Add support...
1199
1200
1201
  	spin_unlock(&con->writequeue_lock);
  	return;
  }
fdda387f7   Patrick Caulfield   [DLM] Add support...
1202
  /* Send a message */
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1203
  static void send_to_sock(struct connection *con)
fdda387f7   Patrick Caulfield   [DLM] Add support...
1204
1205
1206
1207
1208
1209
  {
  	int ret = 0;
  	ssize_t(*sendpage) (struct socket *, struct page *, int, size_t, int);
  	const int msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL;
  	struct writequeue_entry *e;
  	int len, offset;
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
1210
  	mutex_lock(&con->sock_mutex);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
  	if (con->sock == NULL)
  		goto out_connect;
  
  	sendpage = con->sock->ops->sendpage;
  
  	spin_lock(&con->writequeue_lock);
  	for (;;) {
  		e = list_entry(con->writequeue.next, struct writequeue_entry,
  			       list);
  		if ((struct list_head *) e == &con->writequeue)
  			break;
  
  		len = e->len;
  		offset = e->offset;
  		BUG_ON(len == 0 && e->users == 0);
  		spin_unlock(&con->writequeue_lock);
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1227
  		kmap(e->page);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1228
1229
1230
1231
1232
  
  		ret = 0;
  		if (len) {
  			ret = sendpage(con->sock, e->page, offset, len,
  				       msg_flags);
d66f8277f   Patrick Caulfield   [DLM] Make dlm_se...
1233
1234
  			if (ret == -EAGAIN || ret == 0) {
  				cond_resched();
fdda387f7   Patrick Caulfield   [DLM] Add support...
1235
  				goto out;
d66f8277f   Patrick Caulfield   [DLM] Make dlm_se...
1236
  			}
fdda387f7   Patrick Caulfield   [DLM] Add support...
1237
1238
  			if (ret <= 0)
  				goto send_error;
d66f8277f   Patrick Caulfield   [DLM] Make dlm_se...
1239
  		}
fdda387f7   Patrick Caulfield   [DLM] Add support...
1240
  			/* Don't starve people filling buffers */
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1241
  			cond_resched();
fdda387f7   Patrick Caulfield   [DLM] Add support...
1242
1243
1244
1245
1246
1247
1248
  
  		spin_lock(&con->writequeue_lock);
  		e->offset += ret;
  		e->len -= ret;
  
  		if (e->len == 0 && e->users == 0) {
  			list_del(&e->list);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1249
  			kunmap(e->page);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1250
1251
1252
1253
1254
  			free_entry(e);
  			continue;
  		}
  	}
  	spin_unlock(&con->writequeue_lock);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1255
  out:
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
1256
  	mutex_unlock(&con->sock_mutex);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1257
  	return;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1258

ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1259
  send_error:
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
1260
  	mutex_unlock(&con->sock_mutex);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1261
  	close_connection(con, false);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1262
  	lowcomms_connect_sock(con);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1263
  	return;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1264

ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1265
  out_connect:
f1f1c1ccf   Patrick Caulfield   [DLM] Make sock_s...
1266
  	mutex_unlock(&con->sock_mutex);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1267
1268
  	if (!test_bit(CF_INIT_PENDING, &con->flags))
  		lowcomms_connect_sock(con);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1269
  	return;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
  }
  
  static void clean_one_writequeue(struct connection *con)
  {
  	struct list_head *list;
  	struct list_head *temp;
  
  	spin_lock(&con->writequeue_lock);
  	list_for_each_safe(list, temp, &con->writequeue) {
  		struct writequeue_entry *e =
  			list_entry(list, struct writequeue_entry, list);
  		list_del(&e->list);
  		free_entry(e);
  	}
  	spin_unlock(&con->writequeue_lock);
  }
  
  /* Called from recovery when it knows that a node has
     left the cluster */
  int dlm_lowcomms_close(int nodeid)
  {
  	struct connection *con;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1292
1293
1294
1295
  	log_print("closing connection to node %d", nodeid);
  	con = nodeid2con(nodeid, 0);
  	if (con) {
  		clean_one_writequeue(con);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1296
  		close_connection(con, true);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1297
1298
  	}
  	return 0;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1299
  }
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1300
  /* Receive workqueue function */
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1301
  static void process_recv_sockets(struct work_struct *work)
fdda387f7   Patrick Caulfield   [DLM] Add support...
1302
  {
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1303
1304
  	struct connection *con = container_of(work, struct connection, rwork);
  	int err;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1305

1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1306
1307
1308
1309
  	clear_bit(CF_READ_PENDING, &con->flags);
  	do {
  		err = con->rx_action(con);
  	} while (!err);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1310
  }
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1311
  /* Send workqueue function */
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1312
  static void process_send_sockets(struct work_struct *work)
fdda387f7   Patrick Caulfield   [DLM] Add support...
1313
  {
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1314
  	struct connection *con = container_of(work, struct connection, swork);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1315

1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1316
  	if (test_and_clear_bit(CF_CONNECT_PENDING, &con->flags)) {
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1317
  		con->connect_action(con);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1318
  	}
bd44e2b00   Patrick Caulfield   [DLM] fix lowcomm...
1319
1320
  	clear_bit(CF_WRITE_PENDING, &con->flags);
  	send_to_sock(con);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1321
1322
1323
1324
1325
1326
1327
  }
  
  
  /* Discard all entries on the write queues */
  static void clean_writequeues(void)
  {
  	int nodeid;
30d3a2373   Patrick Caulfield   [DLM] Lowcomms no...
1328
1329
  	for (nodeid = 1; nodeid <= max_nodeid; nodeid++) {
  		struct connection *con = __nodeid2con(nodeid, 0);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1330
1331
1332
1333
1334
  
  		if (con)
  			clean_one_writequeue(con);
  	}
  }
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1335
  static void work_stop(void)
fdda387f7   Patrick Caulfield   [DLM] Add support...
1336
  {
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1337
1338
  	destroy_workqueue(recv_workqueue);
  	destroy_workqueue(send_workqueue);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1339
  }
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1340
  static int work_start(void)
fdda387f7   Patrick Caulfield   [DLM] Add support...
1341
  {
fdda387f7   Patrick Caulfield   [DLM] Add support...
1342
  	int error;
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1343
1344
  	recv_workqueue = create_workqueue("dlm_recv");
  	error = IS_ERR(recv_workqueue);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1345
  	if (error) {
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1346
  		log_print("can't start dlm_recv %d", error);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1347
1348
  		return error;
  	}
fdda387f7   Patrick Caulfield   [DLM] Add support...
1349

1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1350
1351
  	send_workqueue = create_singlethread_workqueue("dlm_send");
  	error = IS_ERR(send_workqueue);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1352
  	if (error) {
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1353
1354
  		log_print("can't start dlm_send %d", error);
  		destroy_workqueue(recv_workqueue);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1355
1356
  		return error;
  	}
fdda387f7   Patrick Caulfield   [DLM] Add support...
1357
1358
1359
  
  	return 0;
  }
fdda387f7   Patrick Caulfield   [DLM] Add support...
1360
1361
1362
  void dlm_lowcomms_stop(void)
  {
  	int i;
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1363
  	struct connection *con;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1364

ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1365
  	/* Set all the flags to prevent any
fdda387f7   Patrick Caulfield   [DLM] Add support...
1366
1367
  	   socket activity.
  	*/
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
1368
  	mutex_lock(&connections_lock);
30d3a2373   Patrick Caulfield   [DLM] Lowcomms no...
1369
  	for (i = 0; i <= max_nodeid; i++) {
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1370
  		con = __nodeid2con(i, 0);
afb853fb4   Patrick Caulfield   [DLM] fix socket ...
1371
  		if (con) {
9e5f2825a   Patrick Caulfield   [DLM] More otherc...
1372
  			con->flags |= 0x0F;
afb853fb4   Patrick Caulfield   [DLM] fix socket ...
1373
1374
1375
  			if (con->sock)
  				con->sock->sk->sk_user_data = NULL;
  		}
fdda387f7   Patrick Caulfield   [DLM] Add support...
1376
  	}
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
1377
  	mutex_unlock(&connections_lock);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1378

1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1379
  	work_stop();
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1380

7a936ce71   Matthias Kaehlcke   dlm: convert conn...
1381
  	mutex_lock(&connections_lock);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1382
  	clean_writequeues();
30d3a2373   Patrick Caulfield   [DLM] Lowcomms no...
1383
  	for (i = 0; i <= max_nodeid; i++) {
2439fe507   Josef Bacik   [DLM] Fix dlm_low...
1384
  		con = __nodeid2con(i, 0);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1385
1386
  		if (con) {
  			close_connection(con, true);
39bd4177d   Patrick Caulfeld   dlm: close othercons
1387
1388
  			if (con->othercon)
  				kmem_cache_free(con_cache, con->othercon);
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1389
  			kmem_cache_free(con_cache, con);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1390
1391
  		}
  	}
30d3a2373   Patrick Caulfield   [DLM] Lowcomms no...
1392
  	max_nodeid = 0;
7a936ce71   Matthias Kaehlcke   dlm: convert conn...
1393
  	mutex_unlock(&connections_lock);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1394
  	kmem_cache_destroy(con_cache);
30d3a2373   Patrick Caulfield   [DLM] Lowcomms no...
1395
  	idr_init(&connections_idr);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1396
  }
fdda387f7   Patrick Caulfield   [DLM] Add support...
1397
1398
  int dlm_lowcomms_start(void)
  {
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1399
1400
  	int error = -EINVAL;
  	struct connection *con;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1401

6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1402
1403
  	init_local();
  	if (!dlm_local_count) {
617e82e10   David Teigland   [DLM] lowcomms style
1404
  		error = -ENOTCONN;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1405
  		log_print("no local IP address has been set");
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1406
  		goto out;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1407
  	}
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1408
  	error = -ENOMEM;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1409
  	con_cache = kmem_cache_create("dlm_conn", sizeof(struct connection),
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1410
  				      __alignof__(struct connection), 0,
20c2df83d   Paul Mundt   mm: Remove slab d...
1411
  				      NULL);
fdda387f7   Patrick Caulfield   [DLM] Add support...
1412
  	if (!con_cache)
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1413
  		goto out;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1414

fdda387f7   Patrick Caulfield   [DLM] Add support...
1415
  	/* Start listening */
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1416
1417
1418
1419
  	if (dlm_config.ci_protocol == 0)
  		error = tcp_listen_for_all();
  	else
  		error = sctp_listen_for_all();
fdda387f7   Patrick Caulfield   [DLM] Add support...
1420
1421
  	if (error)
  		goto fail_unlisten;
1d6e8131c   Patrick Caulfield   [DLM] Use workque...
1422
  	error = work_start();
fdda387f7   Patrick Caulfield   [DLM] Add support...
1423
1424
  	if (error)
  		goto fail_unlisten;
fdda387f7   Patrick Caulfield   [DLM] Add support...
1425
  	return 0;
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1426
  fail_unlisten:
6ed7257b4   Patrick Caulfield   [DLM] Consolidate...
1427
1428
1429
1430
1431
  	con = nodeid2con(0,0);
  	if (con) {
  		close_connection(con, false);
  		kmem_cache_free(con_cache, con);
  	}
fdda387f7   Patrick Caulfield   [DLM] Add support...
1432
  	kmem_cache_destroy(con_cache);
ac33d0710   Patrick Caulfield   [DLM] Clean up lo...
1433
  out:
fdda387f7   Patrick Caulfield   [DLM] Add support...
1434
1435
  	return error;
  }