Blame view

net/llc/llc_conn.c 27.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * llc_conn.c - Driver routines for connection component.
   *
   * Copyright (c) 1997 by Procom Technology, Inc.
   *		 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
   *
   * This program can be redistributed or modified under the terms of the
   * GNU General Public License as published by the Free Software Foundation.
   * This program is distributed without any warranty or implied warranty
   * of merchantability or fitness for a particular purpose.
   *
   * See the GNU General Public License for more details.
   */
  
  #include <linux/init.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
16
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
  #include <net/llc_sap.h>
  #include <net/llc_conn.h>
  #include <net/sock.h>
c752f0739   Arnaldo Carvalho de Melo   [TCP]: Move the t...
20
  #include <net/tcp_states.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  #include <net/llc_c_ev.h>
  #include <net/llc_c_ac.h>
  #include <net/llc_c_st.h>
  #include <net/llc_pdu.h>
  
  #if 0
  #define dprintk(args...) printk(KERN_DEBUG args)
  #else
  #define dprintk(args...)
  #endif
  
  static int llc_find_offset(int state, int ev_type);
  static void llc_conn_send_pdus(struct sock *sk);
  static int llc_conn_service(struct sock *sk, struct sk_buff *skb);
  static int llc_exec_conn_trans_actions(struct sock *sk,
  				       struct llc_conn_state_trans *trans,
  				       struct sk_buff *ev);
  static struct llc_conn_state_trans *llc_qualify_conn_ev(struct sock *sk,
  							struct sk_buff *skb);
  
  /* Offset table on connection states transition diagram */
  static int llc_offset_table[NBR_CONN_STATES][NBR_CONN_EV];
590232a71   Arnaldo Carvalho de Melo   [LLC]: Add sysctl...
43
44
45
46
  int sysctl_llc2_ack_timeout = LLC2_ACK_TIME * HZ;
  int sysctl_llc2_p_timeout = LLC2_P_TIME * HZ;
  int sysctl_llc2_rej_timeout = LLC2_REJ_TIME * HZ;
  int sysctl_llc2_busy_timeout = LLC2_BUSY_TIME * HZ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
52
53
54
55
56
57
58
59
  /**
   *	llc_conn_state_process - sends event to connection state machine
   *	@sk: connection
   *	@skb: occurred event
   *
   *	Sends an event to connection state machine. After processing event
   *	(executing it's actions and changing state), upper layer will be
   *	indicated or confirmed, if needed. Returns 0 for success, 1 for
   *	failure. The socket lock has to be held before calling this function.
   */
  int llc_conn_state_process(struct sock *sk, struct sk_buff *skb)
  {
  	int rc;
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
60
  	struct llc_sock *llc = llc_sk(skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
64
65
66
67
68
69
  	struct llc_conn_state_ev *ev = llc_conn_ev(skb);
  
  	/*
  	 * We have to hold the skb, because llc_conn_service will kfree it in
  	 * the sending path and we need to look at the skb->cb, where we encode
  	 * llc_conn_state_ev.
  	 */
  	skb_get(skb);
  	ev->ind_prim = ev->cfm_prim = 0;
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
70
71
72
73
  	/*
  	 * Send event to state machine
  	 */
  	rc = llc_conn_service(skb->sk, skb);
af426d327   Arnaldo Carvalho de Melo   [LLC]: Help the c...
74
  	if (unlikely(rc != 0)) {
0dc47877a   Harvey Harrison   net: replace rema...
75
76
  		printk(KERN_ERR "%s: llc_conn_service failed
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
  		goto out_kfree_skb;
  	}
af426d327   Arnaldo Carvalho de Melo   [LLC]: Help the c...
79
  	if (unlikely(!ev->ind_prim && !ev->cfm_prim)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
  		/* indicate or confirm not required */
8728b834b   David S. Miller   [NET]: Kill skb->...
81
  		if (!skb->next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
  			goto out_kfree_skb;
  		goto out_skb_put;
  	}
af426d327   Arnaldo Carvalho de Melo   [LLC]: Help the c...
85
  	if (unlikely(ev->ind_prim && ev->cfm_prim)) /* Paranoia */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
89
  		skb_get(skb);
  
  	switch (ev->ind_prim) {
  	case LLC_DATA_PRIM:
04e4223f4   Arnaldo Carvalho de Melo   [LLC]: Do better ...
90
91
  		llc_save_primitive(sk, skb, LLC_DATA_PRIM);
  		if (unlikely(sock_queue_rcv_skb(sk, skb))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
95
96
  			/*
  			 * shouldn't happen
  			 */
  			printk(KERN_ERR "%s: sock_queue_rcv_skb failed!
  ",
0dc47877a   Harvey Harrison   net: replace rema...
97
  			       __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
  			kfree_skb(skb);
  		}
  		break;
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
101
  	case LLC_CONN_PRIM:
04e4223f4   Arnaldo Carvalho de Melo   [LLC]: Do better ...
102
  		/*
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
103
104
105
  		 * Can't be sock_queue_rcv_skb, because we have to leave the
  		 * skb->sk pointing to the newly created struct sock in
  		 * llc_conn_handler. -acme
04e4223f4   Arnaldo Carvalho de Melo   [LLC]: Do better ...
106
  		 */
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
107
108
  		skb_queue_tail(&sk->sk_receive_queue, skb);
  		sk->sk_state_change(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
115
116
117
  		break;
  	case LLC_DISC_PRIM:
  		sock_hold(sk);
  		if (sk->sk_type == SOCK_STREAM &&
  		    sk->sk_state == TCP_ESTABLISHED) {
  			sk->sk_shutdown       = SHUTDOWN_MASK;
  			sk->sk_socket->state  = SS_UNCONNECTED;
  			sk->sk_state          = TCP_CLOSE;
  			if (!sock_flag(sk, SOCK_DEAD)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
  				sock_set_flag(sk, SOCK_DEAD);
8420e1b54   Arnaldo Carvalho de Melo   [LLC]: fix llc_ui...
119
  				sk->sk_state_change(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
123
124
125
126
127
128
129
  			}
  		}
  		kfree_skb(skb);
  		sock_put(sk);
  		break;
  	case LLC_RESET_PRIM:
  		/*
  		 * FIXME:
  		 * RESET is not being notified to upper layers for now
  		 */
0dc47877a   Harvey Harrison   net: replace rema...
130
131
  		printk(KERN_INFO "%s: received a reset ind!
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
135
136
137
  		kfree_skb(skb);
  		break;
  	default:
  		if (ev->ind_prim) {
  			printk(KERN_INFO "%s: received unknown %d prim!
  ",
0dc47877a   Harvey Harrison   net: replace rema...
138
  				__func__, ev->ind_prim);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  			kfree_skb(skb);
  		}
  		/* No indication */
  		break;
  	}
  
  	switch (ev->cfm_prim) {
  	case LLC_DATA_PRIM:
  		if (!llc_data_accept_state(llc->state))
  			sk->sk_write_space(sk);
  		else
  			rc = llc->failed_data_req = 1;
  		break;
  	case LLC_CONN_PRIM:
  		if (sk->sk_type == SOCK_STREAM &&
  		    sk->sk_state == TCP_SYN_SENT) {
  			if (ev->status) {
  				sk->sk_socket->state = SS_UNCONNECTED;
  				sk->sk_state         = TCP_CLOSE;
  			} else {
  				sk->sk_socket->state = SS_CONNECTED;
  				sk->sk_state         = TCP_ESTABLISHED;
  			}
  			sk->sk_state_change(sk);
  		}
  		break;
  	case LLC_DISC_PRIM:
  		sock_hold(sk);
  		if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSING) {
  			sk->sk_socket->state = SS_UNCONNECTED;
  			sk->sk_state         = TCP_CLOSE;
  			sk->sk_state_change(sk);
  		}
  		sock_put(sk);
  		break;
  	case LLC_RESET_PRIM:
  		/*
  		 * FIXME:
  		 * RESET is not being notified to upper layers for now
  		 */
0dc47877a   Harvey Harrison   net: replace rema...
179
180
  		printk(KERN_INFO "%s: received a reset conf!
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
184
185
  		break;
  	default:
  		if (ev->cfm_prim) {
  			printk(KERN_INFO "%s: received unknown %d prim!
  ",
0dc47877a   Harvey Harrison   net: replace rema...
186
  					__func__, ev->cfm_prim);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
  			break;
  		}
  		goto out_skb_put; /* No confirmation */
  	}
  out_kfree_skb:
  	kfree_skb(skb);
  out_skb_put:
  	kfree_skb(skb);
  	return rc;
  }
  
  void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb)
  {
  	/* queue PDU to send to MAC layer */
  	skb_queue_tail(&sk->sk_write_queue, skb);
  	llc_conn_send_pdus(sk);
  }
  
  /**
   *	llc_conn_rtn_pdu - sends received data pdu to upper layer
   *	@sk: Active connection
   *	@skb: Received data frame
   *
   *	Sends received data pdu to upper layer (by using indicate function).
   *	Prepares service parameters (prim and prim_data). calling indication
   *	function will be done in llc_conn_state_process.
   */
  void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb)
  {
  	struct llc_conn_state_ev *ev = llc_conn_ev(skb);
  
  	ev->ind_prim = LLC_DATA_PRIM;
  }
  
  /**
   *	llc_conn_resend_i_pdu_as_cmd - resend all all unacknowledged I PDUs
   *	@sk: active connection
   *	@nr: NR
   *	@first_p_bit: p_bit value of first pdu
   *
   *	Resend all unacknowledged I PDUs, starting with the NR; send first as
   *	command PDU with P bit equal first_p_bit; if more than one send
   *	subsequent as command PDUs with P bit equal zero (0).
   */
  void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit)
  {
  	struct sk_buff *skb;
  	struct llc_pdu_sn *pdu;
  	u16 nbr_unack_pdus;
  	struct llc_sock *llc;
  	u8 howmany_resend = 0;
  
  	llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);
  	if (!nbr_unack_pdus)
  		goto out;
  	/*
  	 * Process unack PDUs only if unack queue is not empty; remove
  	 * appropriate PDUs, fix them up, and put them on mac_pdu_q.
  	 */
  	llc = llc_sk(sk);
  
  	while ((skb = skb_dequeue(&llc->pdu_unack_q)) != NULL) {
  		pdu = llc_pdu_sn_hdr(skb);
  		llc_pdu_set_cmd_rsp(skb, LLC_PDU_CMD);
  		llc_pdu_set_pf_bit(skb, first_p_bit);
  		skb_queue_tail(&sk->sk_write_queue, skb);
  		first_p_bit = 0;
  		llc->vS = LLC_I_GET_NS(pdu);
  		howmany_resend++;
  	}
  	if (howmany_resend > 0)
  		llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;
  	/* any PDUs to re-send are queued up; start sending to MAC */
  	llc_conn_send_pdus(sk);
  out:;
  }
  
  /**
   *	llc_conn_resend_i_pdu_as_rsp - Resend all unacknowledged I PDUs
   *	@sk: active connection.
   *	@nr: NR
   *	@first_f_bit: f_bit value of first pdu.
   *
   *	Resend all unacknowledged I PDUs, starting with the NR; send first as
   *	response PDU with F bit equal first_f_bit; if more than one send
   *	subsequent as response PDUs with F bit equal zero (0).
   */
  void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit)
  {
  	struct sk_buff *skb;
  	u16 nbr_unack_pdus;
  	struct llc_sock *llc = llc_sk(sk);
  	u8 howmany_resend = 0;
  
  	llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);
  	if (!nbr_unack_pdus)
  		goto out;
  	/*
  	 * Process unack PDUs only if unack queue is not empty; remove
  	 * appropriate PDUs, fix them up, and put them on mac_pdu_q
  	 */
  	while ((skb = skb_dequeue(&llc->pdu_unack_q)) != NULL) {
  		struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
  
  		llc_pdu_set_cmd_rsp(skb, LLC_PDU_RSP);
  		llc_pdu_set_pf_bit(skb, first_f_bit);
  		skb_queue_tail(&sk->sk_write_queue, skb);
  		first_f_bit = 0;
  		llc->vS = LLC_I_GET_NS(pdu);
  		howmany_resend++;
  	}
  	if (howmany_resend > 0)
  		llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;
  	/* any PDUs to re-send are queued up; start sending to MAC */
  	llc_conn_send_pdus(sk);
  out:;
  }
  
  /**
   *	llc_conn_remove_acked_pdus - Removes acknowledged pdus from tx queue
   *	@sk: active connection
   *	nr: NR
   *	how_many_unacked: size of pdu_unack_q after removing acked pdus
   *
   *	Removes acknowledged pdus from transmit queue (pdu_unack_q). Returns
   *	the number of pdus that removed from queue.
   */
  int llc_conn_remove_acked_pdus(struct sock *sk, u8 nr, u16 *how_many_unacked)
  {
  	int pdu_pos, i;
  	struct sk_buff *skb;
  	struct llc_pdu_sn *pdu;
  	int nbr_acked = 0;
  	struct llc_sock *llc = llc_sk(sk);
  	int q_len = skb_queue_len(&llc->pdu_unack_q);
  
  	if (!q_len)
  		goto out;
  	skb = skb_peek(&llc->pdu_unack_q);
  	pdu = llc_pdu_sn_hdr(skb);
  
  	/* finding position of last acked pdu in queue */
  	pdu_pos = ((int)LLC_2_SEQ_NBR_MODULO + (int)nr -
  			(int)LLC_I_GET_NS(pdu)) % LLC_2_SEQ_NBR_MODULO;
  
  	for (i = 0; i < pdu_pos && i < q_len; i++) {
  		skb = skb_dequeue(&llc->pdu_unack_q);
c3431ea71   Wei Yongjun   llc: remove some ...
334
  		kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
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
414
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
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
  		nbr_acked++;
  	}
  out:
  	*how_many_unacked = skb_queue_len(&llc->pdu_unack_q);
  	return nbr_acked;
  }
  
  /**
   *	llc_conn_send_pdus - Sends queued PDUs
   *	@sk: active connection
   *
   *	Sends queued pdus to MAC layer for transmission.
   */
  static void llc_conn_send_pdus(struct sock *sk)
  {
  	struct sk_buff *skb;
  
  	while ((skb = skb_dequeue(&sk->sk_write_queue)) != NULL) {
  		struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
  
  		if (LLC_PDU_TYPE_IS_I(pdu) &&
  		    !(skb->dev->flags & IFF_LOOPBACK)) {
  			struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
  
  			skb_queue_tail(&llc_sk(sk)->pdu_unack_q, skb);
  			if (!skb2)
  				break;
  			skb = skb2;
  		}
  		dev_queue_xmit(skb);
  	}
  }
  
  /**
   *	llc_conn_service - finds transition and changes state of connection
   *	@sk: connection
   *	@skb: happened event
   *
   *	This function finds transition that matches with happened event, then
   *	executes related actions and finally changes state of connection.
   *	Returns 0 for success, 1 for failure.
   */
  static int llc_conn_service(struct sock *sk, struct sk_buff *skb)
  {
  	int rc = 1;
  	struct llc_sock *llc = llc_sk(sk);
  	struct llc_conn_state_trans *trans;
  
  	if (llc->state > NBR_CONN_STATES)
  		goto out;
  	rc = 0;
  	trans = llc_qualify_conn_ev(sk, skb);
  	if (trans) {
  		rc = llc_exec_conn_trans_actions(sk, trans, skb);
  		if (!rc && trans->next_state != NO_STATE_CHANGE) {
  			llc->state = trans->next_state;
  			if (!llc_data_accept_state(llc->state))
  				sk->sk_state_change(sk);
  		}
  	}
  out:
  	return rc;
  }
  
  /**
   *	llc_qualify_conn_ev - finds transition for event
   *	@sk: connection
   *	@skb: happened event
   *
   *	This function finds transition that matches with happened event.
   *	Returns pointer to found transition on success, %NULL otherwise.
   */
  static struct llc_conn_state_trans *llc_qualify_conn_ev(struct sock *sk,
  							struct sk_buff *skb)
  {
  	struct llc_conn_state_trans **next_trans;
  	llc_conn_ev_qfyr_t *next_qualifier;
  	struct llc_conn_state_ev *ev = llc_conn_ev(skb);
  	struct llc_sock *llc = llc_sk(sk);
  	struct llc_conn_state *curr_state =
  					&llc_conn_state_table[llc->state - 1];
  
  	/* search thru events for this state until
  	 * list exhausted or until no more
  	 */
  	for (next_trans = curr_state->transitions +
  		llc_find_offset(llc->state - 1, ev->type);
  	     (*next_trans)->ev; next_trans++) {
  		if (!((*next_trans)->ev)(sk, skb)) {
  			/* got POSSIBLE event match; the event may require
  			 * qualification based on the values of a number of
  			 * state flags; if all qualifications are met (i.e.,
  			 * if all qualifying functions return success, or 0,
  			 * then this is THE event we're looking for
  			 */
  			for (next_qualifier = (*next_trans)->ev_qualifiers;
  			     next_qualifier && *next_qualifier &&
  			     !(*next_qualifier)(sk, skb); next_qualifier++)
  				/* nothing */;
  			if (!next_qualifier || !*next_qualifier)
  				/* all qualifiers executed successfully; this is
  				 * our transition; return it so we can perform
  				 * the associated actions & change the state
  				 */
  				return *next_trans;
  		}
  	}
  	return NULL;
  }
  
  /**
   *	llc_exec_conn_trans_actions - executes related actions
   *	@sk: connection
   *	@trans: transition that it's actions must be performed
   *	@skb: event
   *
   *	Executes actions that is related to happened event. Returns 0 for
   *	success, 1 to indicate failure of at least one action.
   */
  static int llc_exec_conn_trans_actions(struct sock *sk,
  				       struct llc_conn_state_trans *trans,
  				       struct sk_buff *skb)
  {
  	int rc = 0;
  	llc_conn_action_t *next_action;
  
  	for (next_action = trans->ev_actions;
  	     next_action && *next_action; next_action++) {
  		int rc2 = (*next_action)(sk, skb);
  
  		if (rc2 == 2) {
  			rc = rc2;
  			break;
  		} else if (rc2)
  			rc = 1;
  	}
  	return rc;
  }
b76f5a842   Octavian Purdila   llc: convert the ...
473
474
475
476
477
478
479
480
481
482
483
484
  static inline bool llc_estab_match(const struct llc_sap *sap,
  				   const struct llc_addr *daddr,
  				   const struct llc_addr *laddr,
  				   const struct sock *sk)
  {
  	struct llc_sock *llc = llc_sk(sk);
  
  	return llc->laddr.lsap == laddr->lsap &&
  		llc->daddr.lsap == daddr->lsap &&
  		llc_mac_match(llc->laddr.mac, laddr->mac) &&
  		llc_mac_match(llc->daddr.mac, daddr->mac);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
  /**
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
486
   *	__llc_lookup_established - Finds connection for the remote/local sap/mac
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
488
489
490
491
492
493
   *	@sap: SAP
   *	@daddr: address of remote LLC (MAC + SAP)
   *	@laddr: address of local LLC (MAC + SAP)
   *
   *	Search connection list of the SAP and finds connection using the remote
   *	mac, remote sap, local mac, and local sap. Returns pointer for
   *	connection found, %NULL otherwise.
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
494
   *	Caller has to make sure local_bh is disabled.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
   */
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
496
497
498
  static struct sock *__llc_lookup_established(struct llc_sap *sap,
  					     struct llc_addr *daddr,
  					     struct llc_addr *laddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
  {
  	struct sock *rc;
b76f5a842   Octavian Purdila   llc: convert the ...
501
  	struct hlist_nulls_node *node;
52d58aef5   Octavian Purdila   llc: replace the ...
502
503
  	int slot = llc_sk_laddr_hashfn(sap, laddr);
  	struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
b76f5a842   Octavian Purdila   llc: convert the ...
504
505
506
  
  	rcu_read_lock();
  again:
52d58aef5   Octavian Purdila   llc: replace the ...
507
  	sk_nulls_for_each_rcu(rc, node, laddr_hb) {
b76f5a842   Octavian Purdila   llc: convert the ...
508
509
510
511
512
513
514
515
516
  		if (llc_estab_match(sap, daddr, laddr, rc)) {
  			/* Extra checks required by SLAB_DESTROY_BY_RCU */
  			if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
  				goto again;
  			if (unlikely(llc_sk(rc)->sap != sap ||
  				     !llc_estab_match(sap, daddr, laddr, rc))) {
  				sock_put(rc);
  				continue;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
518
519
520
  			goto found;
  		}
  	}
  	rc = NULL;
52d58aef5   Octavian Purdila   llc: replace the ...
521
522
523
524
525
526
527
  	/*
  	 * if the nulls value we got at the end of this lookup is
  	 * not the expected one, we must restart lookup.
  	 * We probably met an item that was moved to another chain.
  	 */
  	if (unlikely(get_nulls_value(node) != slot))
  		goto again;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
  found:
b76f5a842   Octavian Purdila   llc: convert the ...
529
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
531
  	return rc;
  }
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
532
533
534
535
536
537
538
539
540
541
542
  struct sock *llc_lookup_established(struct llc_sap *sap,
  				    struct llc_addr *daddr,
  				    struct llc_addr *laddr)
  {
  	struct sock *sk;
  
  	local_bh_disable();
  	sk = __llc_lookup_established(sap, daddr, laddr);
  	local_bh_enable();
  	return sk;
  }
b76f5a842   Octavian Purdila   llc: convert the ...
543
544
545
546
547
548
549
550
  static inline bool llc_listener_match(const struct llc_sap *sap,
  				      const struct llc_addr *laddr,
  				      const struct sock *sk)
  {
  	struct llc_sock *llc = llc_sk(sk);
  
  	return sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN &&
  		llc->laddr.lsap == laddr->lsap &&
52d58aef5   Octavian Purdila   llc: replace the ...
551
  		llc_mac_match(llc->laddr.mac, laddr->mac);
b76f5a842   Octavian Purdila   llc: convert the ...
552
  }
52d58aef5   Octavian Purdila   llc: replace the ...
553
554
  static struct sock *__llc_lookup_listener(struct llc_sap *sap,
  					  struct llc_addr *laddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
556
  {
  	struct sock *rc;
b76f5a842   Octavian Purdila   llc: convert the ...
557
  	struct hlist_nulls_node *node;
52d58aef5   Octavian Purdila   llc: replace the ...
558
559
  	int slot = llc_sk_laddr_hashfn(sap, laddr);
  	struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
b76f5a842   Octavian Purdila   llc: convert the ...
560
561
562
  
  	rcu_read_lock();
  again:
52d58aef5   Octavian Purdila   llc: replace the ...
563
  	sk_nulls_for_each_rcu(rc, node, laddr_hb) {
b76f5a842   Octavian Purdila   llc: convert the ...
564
565
566
567
568
569
570
571
572
  		if (llc_listener_match(sap, laddr, rc)) {
  			/* Extra checks required by SLAB_DESTROY_BY_RCU */
  			if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
  				goto again;
  			if (unlikely(llc_sk(rc)->sap != sap ||
  				     !llc_listener_match(sap, laddr, rc))) {
  				sock_put(rc);
  				continue;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
574
575
576
  			goto found;
  		}
  	}
  	rc = NULL;
52d58aef5   Octavian Purdila   llc: replace the ...
577
578
579
580
581
582
583
  	/*
  	 * if the nulls value we got at the end of this lookup is
  	 * not the expected one, we must restart lookup.
  	 * We probably met an item that was moved to another chain.
  	 */
  	if (unlikely(get_nulls_value(node) != slot))
  		goto again;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
  found:
b76f5a842   Octavian Purdila   llc: convert the ...
585
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
587
  	return rc;
  }
52d58aef5   Octavian Purdila   llc: replace the ...
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
  /**
   *	llc_lookup_listener - Finds listener for local MAC + SAP
   *	@sap: SAP
   *	@laddr: address of local LLC (MAC + SAP)
   *
   *	Search connection list of the SAP and finds connection listening on
   *	local mac, and local sap. Returns pointer for parent socket found,
   *	%NULL otherwise.
   *	Caller has to make sure local_bh is disabled.
   */
  static struct sock *llc_lookup_listener(struct llc_sap *sap,
  					struct llc_addr *laddr)
  {
  	static struct llc_addr null_addr;
  	struct sock *rc = __llc_lookup_listener(sap, laddr);
  
  	if (!rc)
  		rc = __llc_lookup_listener(sap, &null_addr);
  
  	return rc;
  }
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
609
610
611
612
613
614
615
616
  static struct sock *__llc_lookup(struct llc_sap *sap,
  				 struct llc_addr *daddr,
  				 struct llc_addr *laddr)
  {
  	struct sock *sk = __llc_lookup_established(sap, daddr, laddr);
  
  	return sk ? : llc_lookup_listener(sap, laddr);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
619
620
621
622
623
624
625
626
627
628
629
  /**
   *	llc_data_accept_state - designates if in this state data can be sent.
   *	@state: state of connection.
   *
   *	Returns 0 if data can be sent, 1 otherwise.
   */
  u8 llc_data_accept_state(u8 state)
  {
  	return state != LLC_CONN_STATE_NORMAL && state != LLC_CONN_STATE_BUSY &&
  	       state != LLC_CONN_STATE_REJ;
  }
  
  /**
0eb801724   Arnaldo Carvalho de Melo   [LLC]: Mark llc_f...
630
   *	llc_find_next_offset - finds offset for next category of transitions
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
633
634
635
636
   *	@state: state table.
   *	@offset: start offset.
   *
   *	Finds offset of next category of transitions in transition table.
   *	Returns the start index of next category.
   */
0eb801724   Arnaldo Carvalho de Melo   [LLC]: Mark llc_f...
637
  static u16 __init llc_find_next_offset(struct llc_conn_state *state, u16 offset)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
  {
  	u16 cnt = 0;
  	struct llc_conn_state_trans **next_trans;
  
  	for (next_trans = state->transitions + offset;
  	     (*next_trans)->ev; next_trans++)
  		++cnt;
  	return cnt;
  }
  
  /**
   *	llc_build_offset_table - builds offset table of connection
   *
   *	Fills offset table of connection state transition table
   *	(llc_offset_table).
   */
  void __init llc_build_offset_table(void)
  {
  	struct llc_conn_state *curr_state;
  	int state, ev_type, next_offset;
  
  	for (state = 0; state < NBR_CONN_STATES; state++) {
  		curr_state = &llc_conn_state_table[state];
  		next_offset = 0;
  		for (ev_type = 0; ev_type < NBR_CONN_EV; ev_type++) {
  			llc_offset_table[state][ev_type] = next_offset;
0eb801724   Arnaldo Carvalho de Melo   [LLC]: Mark llc_f...
664
665
  			next_offset += llc_find_next_offset(curr_state,
  							    next_offset) + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
  		}
  	}
  }
  
  /**
   *	llc_find_offset - finds start offset of category of transitions
   *	@state: state of connection
   *	@ev_type: type of happened event
   *
   *	Finds start offset of desired category of transitions. Returns the
   *	desired start offset.
   */
  static int llc_find_offset(int state, int ev_type)
  {
  	int rc = 0;
  	/* at this stage, llc_offset_table[..][2] is not important. it is for
  	 * init_pf_cycle and I don't know what is it.
  	 */
  	switch (ev_type) {
  	case LLC_CONN_EV_TYPE_PRIM:
  		rc = llc_offset_table[state][0]; break;
  	case LLC_CONN_EV_TYPE_PDU:
  		rc = llc_offset_table[state][4]; break;
  	case LLC_CONN_EV_TYPE_SIMPLE:
  		rc = llc_offset_table[state][1]; break;
  	case LLC_CONN_EV_TYPE_P_TMR:
  	case LLC_CONN_EV_TYPE_ACK_TMR:
  	case LLC_CONN_EV_TYPE_REJ_TMR:
  	case LLC_CONN_EV_TYPE_BUSY_TMR:
  		rc = llc_offset_table[state][3]; break;
  	}
  	return rc;
  }
  
  /**
   *	llc_sap_add_socket - adds a socket to a SAP
   *	@sap: SAP
   *	@sk: socket
   *
52d58aef5   Octavian Purdila   llc: replace the ...
705
   *	This function adds a socket to the hash tables of a SAP.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706
707
708
   */
  void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk)
  {
6d2e3ea28   Octavian Purdila   llc: use a device...
709
710
  	struct llc_sock *llc = llc_sk(sk);
  	struct hlist_head *dev_hb = llc_sk_dev_hash(sap, llc->dev->ifindex);
52d58aef5   Octavian Purdila   llc: replace the ...
711
  	struct hlist_nulls_head *laddr_hb = llc_sk_laddr_hash(sap, &llc->laddr);
6d2e3ea28   Octavian Purdila   llc: use a device...
712

6e2144b76   Arnaldo Carvalho de Melo   [LLC]: Use refcou...
713
  	llc_sap_hold(sap);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
  	llc_sk(sk)->sap = sap;
6d2e3ea28   Octavian Purdila   llc: use a device...
715
716
  
  	spin_lock_bh(&sap->sk_lock);
52d58aef5   Octavian Purdila   llc: replace the ...
717
718
  	sap->sk_count++;
  	sk_nulls_add_node_rcu(sk, laddr_hb);
6d2e3ea28   Octavian Purdila   llc: use a device...
719
  	hlist_add_head(&llc->dev_hash_node, dev_hb);
b76f5a842   Octavian Purdila   llc: convert the ...
720
  	spin_unlock_bh(&sap->sk_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721
722
723
724
725
726
727
  }
  
  /**
   *	llc_sap_remove_socket - removes a socket from SAP
   *	@sap: SAP
   *	@sk: socket
   *
52d58aef5   Octavian Purdila   llc: replace the ...
728
   *	This function removes a connection from the hash tables of a SAP if
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
729
730
731
732
   *	the connection was in this list.
   */
  void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk)
  {
6d2e3ea28   Octavian Purdila   llc: use a device...
733
  	struct llc_sock *llc = llc_sk(sk);
b76f5a842   Octavian Purdila   llc: convert the ...
734
735
  	spin_lock_bh(&sap->sk_lock);
  	sk_nulls_del_node_init_rcu(sk);
6d2e3ea28   Octavian Purdila   llc: use a device...
736
  	hlist_del(&llc->dev_hash_node);
52d58aef5   Octavian Purdila   llc: replace the ...
737
  	sap->sk_count--;
b76f5a842   Octavian Purdila   llc: convert the ...
738
  	spin_unlock_bh(&sap->sk_lock);
6e2144b76   Arnaldo Carvalho de Melo   [LLC]: Use refcou...
739
  	llc_sap_put(sap);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
741
742
743
744
745
746
747
748
749
750
751
  }
  
  /**
   *	llc_conn_rcv - sends received pdus to the connection state machine
   *	@sk: current connection structure.
   *	@skb: received frame.
   *
   *	Sends received pdus to the connection state machine.
   */
  static int llc_conn_rcv(struct sock* sk, struct sk_buff *skb)
  {
  	struct llc_conn_state_ev *ev = llc_conn_ev(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
753
754
755
756
  	ev->type   = LLC_CONN_EV_TYPE_PDU;
  	ev->reason = 0;
  	return llc_conn_state_process(sk, skb);
  }
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
757
758
759
760
761
  static struct sock *llc_create_incoming_sock(struct sock *sk,
  					     struct net_device *dev,
  					     struct llc_addr *saddr,
  					     struct llc_addr *daddr)
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
762
  	struct sock *newsk = llc_sk_alloc(sock_net(sk), sk->sk_family, GFP_ATOMIC,
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
  					  sk->sk_prot);
  	struct llc_sock *newllc, *llc = llc_sk(sk);
  
  	if (!newsk)
  		goto out;
  	newllc = llc_sk(newsk);
  	memcpy(&newllc->laddr, daddr, sizeof(newllc->laddr));
  	memcpy(&newllc->daddr, saddr, sizeof(newllc->daddr));
  	newllc->dev = dev;
  	dev_hold(dev);
  	llc_sap_add_socket(llc->sap, newsk);
  	llc_sap_hold(llc->sap);
  out:
  	return newsk;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
779
780
781
782
783
784
785
786
  void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb)
  {
  	struct llc_addr saddr, daddr;
  	struct sock *sk;
  
  	llc_pdu_decode_sa(skb, saddr.mac);
  	llc_pdu_decode_ssap(skb, &saddr.lsap);
  	llc_pdu_decode_da(skb, daddr.mac);
  	llc_pdu_decode_dsap(skb, &daddr.lsap);
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
  	sk = __llc_lookup(sap, &saddr, &daddr);
  	if (!sk)
  		goto drop;
  
  	bh_lock_sock(sk);
  	/*
  	 * This has to be done here and not at the upper layer ->accept
  	 * method because of the way the PROCOM state machine works:
  	 * it needs to set several state variables (see, for instance,
  	 * llc_adm_actions_2 in net/llc/llc_c_st.c) and send a packet to
  	 * the originator of the new connection, and this state has to be
  	 * in the newly created struct sock private area. -acme
  	 */
  	if (unlikely(sk->sk_state == TCP_LISTEN)) {
  		struct sock *newsk = llc_create_incoming_sock(sk, skb->dev,
  							      &saddr, &daddr);
  		if (!newsk)
  			goto drop_unlock;
  		skb_set_owner_r(skb, newsk);
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
  		/*
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
808
809
810
811
812
  		 * Can't be skb_set_owner_r, this will be done at the
  		 * llc_conn_state_process function, later on, when we will use
  		 * skb_queue_rcv_skb to send it to upper layers, this is
  		 * another trick required to cope with how the PROCOM state
  		 * machine works. -acme
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
813
  		 */
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
814
  		skb->sk = sk;
04e4223f4   Arnaldo Carvalho de Melo   [LLC]: Do better ...
815
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
817
818
  	if (!sock_owned_by_user(sk))
  		llc_conn_rcv(sk, skb);
  	else {
0dc47877a   Harvey Harrison   net: replace rema...
819
820
  		dprintk("%s: adding to backlog...
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821
  		llc_set_backlog_type(skb, LLC_PACKET);
a3a858ff1   Zhu Yi   net: backlog func...
822
  		if (sk_add_backlog(sk, skb))
79545b681   Zhu Yi   llc: use limited ...
823
  			goto drop_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
824
  	}
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
825
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
827
828
829
830
  	bh_unlock_sock(sk);
  	sock_put(sk);
  	return;
  drop:
  	kfree_skb(skb);
d389424e0   Arnaldo Carvalho de Melo   [LLC]: Fix the ac...
831
832
833
834
  	return;
  drop_unlock:
  	kfree_skb(skb);
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
835
836
837
838
839
840
841
842
  }
  
  #undef LLC_REFCNT_DEBUG
  #ifdef LLC_REFCNT_DEBUG
  static atomic_t llc_sock_nr;
  #endif
  
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
844
845
846
847
848
849
850
851
852
853
854
855
   *	llc_backlog_rcv - Processes rx frames and expired timers.
   *	@sk: LLC sock (p8022 connection)
   *	@skb: queued rx frame or event
   *
   *	This function processes frames that has received and timers that has
   *	expired during sending an I pdu (refer to data_req_handler).  frames
   *	queue by llc_rcv function (llc_mac.c) and timers queue by timer
   *	callback functions(llc_c_ac.c).
   */
  static int llc_backlog_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	int rc = 0;
  	struct llc_sock *llc = llc_sk(sk);
af426d327   Arnaldo Carvalho de Melo   [LLC]: Help the c...
856
857
  	if (likely(llc_backlog_type(skb) == LLC_PACKET)) {
  		if (likely(llc->state > 1)) /* not closed */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
859
860
861
862
  			rc = llc_conn_rcv(sk, skb);
  		else
  			goto out_kfree_skb;
  	} else if (llc_backlog_type(skb) == LLC_EVENT) {
  		/* timer expiration event */
af426d327   Arnaldo Carvalho de Melo   [LLC]: Help the c...
863
  		if (likely(llc->state > 1))  /* not closed */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
865
866
867
  			rc = llc_conn_state_process(sk, skb);
  		else
  			goto out_kfree_skb;
  	} else {
0dc47877a   Harvey Harrison   net: replace rema...
868
869
  		printk(KERN_ERR "%s: invalid skb in backlog
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
  		goto out_kfree_skb;
  	}
  out:
  	return rc;
  out_kfree_skb:
  	kfree_skb(skb);
  	goto out;
  }
  
  /**
   *     llc_sk_init - Initializes a socket with default llc values.
   *     @sk: socket to initialize.
   *
   *     Initializes a socket with default llc values.
   */
  static void llc_sk_init(struct sock* sk)
  {
  	struct llc_sock *llc = llc_sk(sk);
  
  	llc->state    = LLC_CONN_STATE_ADM;
  	llc->inc_cntr = llc->dec_cntr = 2;
  	llc->dec_step = llc->connect_step = 1;
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
892
893
  	setup_timer(&llc->ack_timer.timer, llc_conn_ack_tmr_cb,
  			(unsigned long)sk);
590232a71   Arnaldo Carvalho de Melo   [LLC]: Add sysctl...
894
  	llc->ack_timer.expire	      = sysctl_llc2_ack_timeout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
895

b24b8a247   Pavel Emelyanov   [NET]: Convert in...
896
897
  	setup_timer(&llc->pf_cycle_timer.timer, llc_conn_pf_cycle_tmr_cb,
  			(unsigned long)sk);
590232a71   Arnaldo Carvalho de Melo   [LLC]: Add sysctl...
898
  	llc->pf_cycle_timer.expire	   = sysctl_llc2_p_timeout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899

b24b8a247   Pavel Emelyanov   [NET]: Convert in...
900
901
  	setup_timer(&llc->rej_sent_timer.timer, llc_conn_rej_tmr_cb,
  			(unsigned long)sk);
590232a71   Arnaldo Carvalho de Melo   [LLC]: Add sysctl...
902
  	llc->rej_sent_timer.expire	   = sysctl_llc2_rej_timeout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
903

b24b8a247   Pavel Emelyanov   [NET]: Convert in...
904
905
  	setup_timer(&llc->busy_state_timer.timer, llc_conn_busy_tmr_cb,
  			(unsigned long)sk);
590232a71   Arnaldo Carvalho de Melo   [LLC]: Add sysctl...
906
  	llc->busy_state_timer.expire	     = sysctl_llc2_busy_timeout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907
908
909
910
  
  	llc->n2 = 2;   /* max retransmit */
  	llc->k  = 2;   /* tx win size, will adjust dynam */
  	llc->rw = 128; /* rx win size (opt and equal to
d57b1869b   YOSHIFUJI Hideaki   [NET] LLC: Fix wh...
911
  			* tx_win of remote LLC) */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
912
913
914
915
916
917
918
919
920
921
922
923
  	skb_queue_head_init(&llc->pdu_unack_q);
  	sk->sk_backlog_rcv = llc_backlog_rcv;
  }
  
  /**
   *	llc_sk_alloc - Allocates LLC sock
   *	@family: upper layer protocol family
   *	@priority: for allocation (%GFP_KERNEL, %GFP_ATOMIC, etc)
   *
   *	Allocates a LLC sock and initializes it. Returns the new LLC sock
   *	or %NULL if there's no memory available for one
   */
1b8d7ae42   Eric W. Biederman   [NET]: Make socke...
924
  struct sock *llc_sk_alloc(struct net *net, int family, gfp_t priority, struct proto *prot)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925
  {
6257ff217   Pavel Emelyanov   [NET]: Forget the...
926
  	struct sock *sk = sk_alloc(net, family, priority, prot);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
927
928
929
930
931
932
933
934
935
  
  	if (!sk)
  		goto out;
  	llc_sk_init(sk);
  	sock_init_data(NULL, sk);
  #ifdef LLC_REFCNT_DEBUG
  	atomic_inc(&llc_sock_nr);
  	printk(KERN_DEBUG "LLC socket %p created in %s, now we have %d alive
  ", sk,
0dc47877a   Harvey Harrison   net: replace rema...
936
  		__func__, atomic_read(&llc_sock_nr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
  #endif
  out:
  	return sk;
  }
  
  /**
   *	llc_sk_free - Frees a LLC socket
   *	@sk - socket to free
   *
   *	Frees a LLC socket
   */
  void llc_sk_free(struct sock *sk)
  {
  	struct llc_sock *llc = llc_sk(sk);
  
  	llc->state = LLC_CONN_OUT_OF_SVC;
  	/* Stop all (possibly) running timers */
  	llc_conn_ac_stop_all_timers(sk, NULL);
  #ifdef DEBUG_LLC_CONN_ALLOC
0dc47877a   Harvey Harrison   net: replace rema...
956
957
  	printk(KERN_INFO "%s: unackq=%d, txq=%d
  ", __func__,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
959
960
961
962
963
964
965
966
967
  		skb_queue_len(&llc->pdu_unack_q),
  		skb_queue_len(&sk->sk_write_queue));
  #endif
  	skb_queue_purge(&sk->sk_receive_queue);
  	skb_queue_purge(&sk->sk_write_queue);
  	skb_queue_purge(&llc->pdu_unack_q);
  #ifdef LLC_REFCNT_DEBUG
  	if (atomic_read(&sk->sk_refcnt) != 1) {
  		printk(KERN_DEBUG "Destruction of LLC sock %p delayed in %s, cnt=%d
  ",
0dc47877a   Harvey Harrison   net: replace rema...
968
  			sk, __func__, atomic_read(&sk->sk_refcnt));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969
970
971
972
973
974
975
  		printk(KERN_DEBUG "%d LLC sockets are still alive
  ",
  			atomic_read(&llc_sock_nr));
  	} else {
  		atomic_dec(&llc_sock_nr);
  		printk(KERN_DEBUG "LLC socket %p released in %s, %d are still alive
  ", sk,
0dc47877a   Harvey Harrison   net: replace rema...
976
  			__func__, atomic_read(&llc_sock_nr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
  	}
  #endif
  	sock_put(sk);
  }
  
  /**
   *	llc_sk_reset - resets a connection
   *	@sk: LLC socket to reset
   *
   *	Resets a connection to the out of service state. Stops its timers
   *	and frees any frames in the queues of the connection.
   */
  void llc_sk_reset(struct sock *sk)
  {
  	struct llc_sock *llc = llc_sk(sk);
  
  	llc_conn_ac_stop_all_timers(sk, NULL);
  	skb_queue_purge(&sk->sk_write_queue);
  	skb_queue_purge(&llc->pdu_unack_q);
  	llc->remote_busy_flag	= 0;
  	llc->cause_flag		= 0;
  	llc->retry_count	= 0;
  	llc_conn_set_p_flag(sk, 0);
  	llc->f_flag		= 0;
  	llc->s_flag		= 0;
  	llc->ack_pf		= 0;
  	llc->first_pdu_Ns	= 0;
  	llc->ack_must_be_send	= 0;
  	llc->dec_step		= 1;
  	llc->inc_cntr		= 2;
  	llc->dec_cntr		= 2;
  	llc->X			= 0;
  	llc->failed_data_req	= 0 ;
  	llc->last_nr		= 0;
  }