Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
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
473
474
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
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
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
664
665
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
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
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
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
/*
 * linux/net/sunrpc/svcsock.c
 *
 * These are the RPC server socket internals.
 *
 * The server scheduling algorithm does not always distribute the load
 * evenly when servicing a single client. May need to modify the
 * svc_sock_enqueue procedure...
 *
 * TCP support is largely untested and may be a little slow. The problem
 * is that we currently do two separate recvfrom's, one for the 4-byte
 * record length, and the second for the actual record. This could possibly
 * be improved by always reading a minimum size of around 100 bytes and
 * tucking any superfluous bytes away in a temporary store. Still, that
 * leaves write requests out in the rain. An alternative may be to peek at
 * the first skb in the queue, and if it matches the next TCP sequence
 * number, to extract the record marker. Yuck.
 *
 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
 */

#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/udp.h>
#include <linux/version.h>
#include <linux/unistd.h>
#include <linux/malloc.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/ip.h>
#if LINUX_VERSION_CODE >= 0x020100
#include <asm/uaccess.h>
#endif

#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/stats.h>


#define RPCDBG_FACILITY	RPCDBG_SVCSOCK


static struct svc_sock *svc_setup_socket(struct svc_serv *, struct socket *,
					 int *errp, int pmap_reg);
static void		svc_udp_data_ready(struct sock *, int);
static int		svc_udp_recvfrom(struct svc_rqst *);
static int		svc_udp_sendto(struct svc_rqst *);


/*
 * Queue up an idle server thread.
 */
static inline void
svc_serv_enqueue(struct svc_serv *serv, struct svc_rqst *rqstp)
{
	rpc_append_list(&serv->sv_threads, rqstp);
}

/*
 * Dequeue an nfsd thread.
 */
static inline void
svc_serv_dequeue(struct svc_serv *serv, struct svc_rqst *rqstp)
{
	rpc_remove_list(&serv->sv_threads, rqstp);
}

/*
 * Release an skbuff after use
 */
static inline void
svc_release_skb(struct svc_rqst *rqstp)
{
	if (!rqstp->rq_skbuff)
		return;

	dprintk("svc: releasing skb %p\n", rqstp->rq_skbuff);
	skb_free_datagram(rqstp->rq_sock->sk_sk, rqstp->rq_skbuff);
	rqstp->rq_skbuff = NULL;
}

/*
 * Queue up a socket with data pending. If there are idle nfsd
 * processes, wake'em up.
 * When calling this function, you should make sure it can't be interrupted
 * by the network bottom half.
 */
static inline void
svc_sock_enqueue(struct svc_sock *svsk)
{
	struct svc_rqst	*rqstp;
	struct svc_serv	*serv;

	if (svsk->sk_busy) {
		/* Don't enqueue socket while daemon is receiving */
		dprintk("svc: socket %p not enqueued: busy\n", svsk->sk_sk);
		return;
	}

	/* Mark socket as busy. It will remain in this state until the
	 * server has processed all pending data and put the socket back
	 * on the idle list
	 */
	svsk->sk_busy = 1;

	serv = svsk->sk_server;
	if ((rqstp = serv->sv_threads) != NULL) {
		dprintk("svc: socket %p served by daemon %p\n",
						svsk->sk_sk, rqstp);
		svc_serv_dequeue(serv, rqstp);
		rqstp->rq_sock = svsk;
		svsk->sk_inuse++;
		wake_up(&rqstp->rq_wait);
	} else {
		dprintk("svc: socket %p put into queue\n", svsk->sk_sk);
		rpc_append_list(&serv->sv_sockets, svsk);
		svsk->sk_qued = 1;
	}
}

/*
 * Dequeue the first socket.
 */
static inline struct svc_sock *
svc_sock_dequeue(struct svc_serv *serv)
{
	struct svc_sock	*svsk;

	disable_bh(NET_BH);
	if ((svsk = serv->sv_sockets) != NULL)
		rpc_remove_list(&serv->sv_sockets, svsk);
	enable_bh(NET_BH);

	if (svsk) {
		dprintk("svc: socket %p dequeued\n", svsk->sk_sk);
		svsk->sk_qued = 0;
	}

	return svsk;
}

/*
 * Having read count bytes from a socket, check whether it
 * needs to be re-enqueued.
 */
static inline void
svc_sock_received(struct svc_sock *svsk, int count)
{
	disable_bh(NET_BH);
	if ((svsk->sk_data -= count) < 0) {
		printk(KERN_NOTICE "svc: sk_data negative!\n");
		svsk->sk_data = 0;
	}
	svsk->sk_rqstp = NULL; /* XXX */
	svsk->sk_busy = 0;
	if (svsk->sk_conn || svsk->sk_data || svsk->sk_close) {
		dprintk("svc: socket %p re-enqueued after receive\n",
						svsk->sk_sk);
		svc_sock_enqueue(svsk);
	}
	enable_bh(NET_BH);
}

/*
 * Dequeue a new connection.
 */
static inline void
svc_sock_accepted(struct svc_sock *svsk)
{
	disable_bh(NET_BH);
        svsk->sk_busy = 0;
        svsk->sk_conn--;
        if (svsk->sk_conn || svsk->sk_data || svsk->sk_close) {
                dprintk("svc: socket %p re-enqueued after accept\n",
						svsk->sk_sk);
                svc_sock_enqueue(svsk);
        }
	enable_bh(NET_BH);
}

/*
 * Release a socket after use.
 */
static inline void
svc_sock_release(struct svc_rqst *rqstp)
{
	struct svc_sock	*svsk = rqstp->rq_sock;

	if (!svsk)
		return;
	svc_release_skb(rqstp);
	rqstp->rq_sock = NULL;
	if (!--(svsk->sk_inuse) && svsk->sk_dead) {
		dprintk("svc: releasing dead socket\n");
		sock_release(svsk->sk_sock);
		kfree(svsk);
	}
}

/*
 * External function to wake up a server waiting for data
 */
void
svc_wake_up(struct svc_serv *serv)
{
	struct svc_rqst	*rqstp;

	if ((rqstp = serv->sv_threads) != NULL) {
		dprintk("svc: daemon %p woken up.\n", rqstp);
		/*
		svc_serv_dequeue(serv, rqstp);
		rqstp->rq_sock = NULL;
		 */
		wake_up(&rqstp->rq_wait);
	}
}

/*
 * Generic sendto routine
 */
static int
svc_sendto(struct svc_rqst *rqstp, struct iovec *iov, int nr)
{
	mm_segment_t	oldfs;
	struct socket	*sock = rqstp->rq_sock->sk_sock;
	struct msghdr	msg;
	int		i, buflen, len;

	for (i = buflen = 0; i < nr; i++)
		buflen += iov[i].iov_len;

	msg.msg_name    = &rqstp->rq_addr;
	msg.msg_namelen = sizeof(rqstp->rq_addr);
	msg.msg_iov     = iov;
	msg.msg_iovlen  = nr;
	msg.msg_control = 0;

#if LINUX_VERSION_CODE >= 0x020100
	msg.msg_flags	= MSG_DONTWAIT;

	oldfs = get_fs(); set_fs(KERNEL_DS);
	len = sock_sendmsg(sock, &msg, buflen);
	set_fs(oldfs);
#else
	msg.msg_flags	= 0;

	oldfs = get_fs(); set_fs(KERNEL_DS);
	len = sock->ops->sendmsg(sock, &msg, buflen, 1, 0);
	set_fs(oldfs);
#endif

	dprintk("svc: socket %p sendto([%p %d... ], %d, %d) = %d\n",
			rqstp->rq_sock,
			iov[0].iov_base, iov[0].iov_len, nr,
			buflen, len);

	return len;
}

/*
 * Check input queue length
 */
static int
svc_recv_available(struct svc_sock *svsk)
{
	mm_segment_t	oldfs;
	struct socket	*sock = svsk->sk_sock;
	int		avail, err;

	oldfs = get_fs(); set_fs(KERNEL_DS);
	err = sock->ops->ioctl(sock, TIOCINQ, (unsigned long) &avail);
	set_fs(oldfs);

	return (err >= 0)? avail : err;
}

/*
 * Generic recvfrom routine.
 */
static int
svc_recvfrom(struct svc_rqst *rqstp, struct iovec *iov, int nr, int buflen)
{
	mm_segment_t	oldfs;
	struct msghdr	msg;
	struct socket	*sock;
	int		len;

	rqstp->rq_addrlen = sizeof(rqstp->rq_addr);
	sock = rqstp->rq_sock->sk_sock;

	msg.msg_name    = &rqstp->rq_addr;
	msg.msg_namelen = sizeof(rqstp->rq_addr);
	msg.msg_iov     = iov;
	msg.msg_iovlen  = nr;
	msg.msg_control = 0;

#if LINUX_VERSION_CODE >= 0x020100
	msg.msg_flags	= MSG_DONTWAIT;

	oldfs = get_fs(); set_fs(KERNEL_DS);
	len = sock_recvmsg(sock, &msg, buflen, MSG_DONTWAIT);
	set_fs(oldfs);
#else
	msg.msg_flags	= 0;

	oldfs = get_fs(); set_fs(KERNEL_DS);
	len = sock->ops->recvmsg(sock, &msg, buflen, 0, 1, &rqstp->rq_addrlen);
	set_fs(oldfs);
#endif

	dprintk("svc: socket %p recvfrom(%p, %d) = %d\n", rqstp->rq_sock,
				iov[0].iov_base, iov[0].iov_len, len);

	return len;
}

/*
 * INET callback when data has been received on the socket.
 */
static void
svc_udp_data_ready(struct sock *sk, int count)
{
	struct svc_sock	*svsk;

	dprintk("svc: socket %p data ready (inet %p)\n", sk->user_data, sk);

	svsk = (struct svc_sock *)(sk->user_data);
	if (!svsk)
		return;
	svsk->sk_data = 1;
	svc_sock_enqueue(svsk);
}

/*
 * Receive a datagram from a UDP socket.
 */
static int
svc_udp_recvfrom(struct svc_rqst *rqstp)
{
	struct svc_sock	*svsk = rqstp->rq_sock;
	struct svc_serv	*serv = svsk->sk_server;
	struct sk_buff	*skb;
	u32		*data;
	int		err, len;

	svsk->sk_data = 0;
	while ((skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) {
		svc_sock_received(svsk, 0);
		if (err == -EAGAIN)
			return err;
		/* possibly an icmp error */
		dprintk("svc: recvfrom returned error %d\n", -err);
	}

	/* There may be more data */
	svsk->sk_data = 1;

	len  = skb->len - sizeof(struct udphdr);
	data = (u32 *) (skb->h.raw + sizeof(struct udphdr));

	rqstp->rq_skbuff      = skb;
	rqstp->rq_argbuf.base = data;
	rqstp->rq_argbuf.buf  = data;
	rqstp->rq_argbuf.len  = (len >> 2);
	/* rqstp->rq_resbuf      = rqstp->rq_defbuf; */
	rqstp->rq_prot        = IPPROTO_UDP;

	/* Get sender address */
	rqstp->rq_addr.sin_family = AF_INET;
	rqstp->rq_addr.sin_port = skb->h.uh->source;
#if LINUX_VERSION_CODE >= 0x020100
	rqstp->rq_addr.sin_addr.s_addr = skb->nh.iph->saddr;
#else
	rqstp->rq_addr.sin_addr.s_addr = skb->saddr;
#endif

	if (serv->sv_stats)
		serv->sv_stats->netudpcnt++;

	/* One down, maybe more to go... */
	svsk->sk_sk->stamp = skb->stamp;
	svc_sock_received(svsk, 0);

	return len;
}

static int
svc_udp_sendto(struct svc_rqst *rqstp)
{
	struct svc_buf	*bufp = &rqstp->rq_resbuf;
	int		error;

	/* Set up the first element of the reply iovec.
	 * Any other iovecs that may be in use have been taken
	 * care of by the server implementation itself.
	 */
	/* bufp->base = bufp->area; */
	bufp->iov[0].iov_base = bufp->base;
	bufp->iov[0].iov_len  = bufp->len << 2;

	error = svc_sendto(rqstp, bufp->iov, bufp->nriov);
	if (error == -ECONNREFUSED)
		/* ICMP error on earlier request. */
		error = svc_sendto(rqstp, bufp->iov, bufp->nriov);
	else if (error == -EAGAIN)
		/* Ignore and wait for re-xmit */
		error = 0;

	return error;
}

static int
svc_udp_init(struct svc_sock *svsk)
{
	svsk->sk_sk->data_ready = svc_udp_data_ready;
	svsk->sk_recvfrom = svc_udp_recvfrom;
	svsk->sk_sendto = svc_udp_sendto;

	return 0;
}

/*
 * A state change on a listening socket means there's a connection
 * pending.
 */
static void
svc_tcp_state_change1(struct sock *sk)
{
	struct svc_sock	*svsk;

	dprintk("svc: socket %p TCP (listen) state change %d\n",
			sk, sk->state);

	if  (sk->state != TCP_ESTABLISHED) {
		/* Aborted connection, SYN_RECV or whatever... */
		return;
	}
	if (!(svsk = (struct svc_sock *) sk->user_data)) {
		printk("svc: socket %p: no user data\n", sk);
		return;
	}
	svsk->sk_conn++;
	svc_sock_enqueue(svsk);
}

/*
 * A state change on a connected socket means it's dying or dead.
 */
static void
svc_tcp_state_change2(struct sock *sk)
{
	struct svc_sock	*svsk;

	dprintk("svc: socket %p TCP (connected) state change %d (svsk %p)\n",
			sk, sk->state, sk->user_data);

	if (!(svsk = (struct svc_sock *) sk->user_data)) {
		printk("svc: socket %p: no user data\n", sk);
		return;
	}
	svsk->sk_close = 1;
	svc_sock_enqueue(svsk);
}

static void
svc_tcp_data_ready(struct sock *sk, int count)
{
	struct svc_sock *	svsk;

	/* Disconnect signalled through data_ready?!? */
	if (sk->state != TCP_ESTABLISHED) {
		svc_tcp_state_change2(sk);
		return;
	}

	dprintk("svc: socket %p TCP data ready (svsk %p)\n",
			sk, sk->user_data);
	if (!(svsk = (struct svc_sock *)(sk->user_data)))
		return;
	svsk->sk_data++;
	svc_sock_enqueue(svsk);
}

/*
 * Accept a TCP connection
 */
static void
svc_tcp_accept(struct svc_sock *svsk)
{
	struct sockaddr_in sin;
	struct svc_serv	*serv = svsk->sk_server;
	struct socket	*sock = svsk->sk_sock;
	struct socket	*newsock;
	struct proto_ops *ops;
	struct svc_sock	*newsvsk;
	int		err, slen;

	dprintk("svc: tcp_accept %p sock %p\n", svsk, sock);
	if (!sock)
		return;

	if (!(newsock = sock_alloc())) {
		printk(KERN_WARNING "%s: no more sockets!\n", serv->sv_name);
		return;
	}
	dprintk("svc: tcp_accept %p allocated\n", newsock);

	newsock->type = sock->type;
	if ((err = sock->ops->dup(newsock, sock)) < 0) {
		printk(KERN_WARNING "%s: socket dup failed (err %d)!\n",
					serv->sv_name, -err);
		goto failed;
	}

	ops = newsock->ops;
	if ((err = ops->accept(sock, newsock, O_NONBLOCK)) < 0) {
		printk(KERN_WARNING "%s: accept failed (err %d)!\n",
					serv->sv_name, -err);
		goto failed;		/* aborted connection or whatever */
	}

	slen = sizeof(sin);
	err = ops->getname(newsock, (struct sockaddr *) &sin, &slen, 1);
	if (err < 0) {
		printk(KERN_WARNING "%s: peername failed (err %d)!\n",
					serv->sv_name, -err);
		goto failed;		/* aborted connection or whatever */
	}

	/* Ideally, we would want to reject connections from unauthorized
	 * hosts here, but we have no generic client tables. For now,
	 * we just punt connects from unprivileged ports. */
	if (ntohs(sin.sin_port) >= 1024) {
		printk(KERN_WARNING
			"%s: connect from unprivileged port: %08lx:%d",
			serv->sv_name, 
			ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port));
		goto failed;
	}

	dprintk("%s: connect from %08lx:%04x\n", serv->sv_name,
			ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port));

	if (!(newsvsk = svc_setup_socket(serv, newsock, &err, 0)))
		goto failed;

	/* Precharge. Data may have arrived on the socket before we
	 * installed the data_ready callback. 
	 */
	newsvsk->sk_data = 1;
	newsvsk->sk_temp = 1;
	svc_sock_enqueue(newsvsk);

	if (serv->sv_stats)
		serv->sv_stats->nettcpconn++;

	return;

failed:
	sock_release(newsock);
	return;
}

/*
 * Receive data from a TCP socket.
 */
static int
svc_tcp_recvfrom(struct svc_rqst *rqstp)
{
	struct svc_sock	*svsk = rqstp->rq_sock;
	struct svc_serv	*serv = svsk->sk_server;
	struct svc_buf	*bufp = &rqstp->rq_argbuf;
	int		len, ready;

	dprintk("svc: tcp_recv %p data %d conn %d close %d\n",
			svsk, svsk->sk_data, svsk->sk_conn, svsk->sk_close);

	if (svsk->sk_close) {
		svc_delete_socket(svsk);
		return 0;
	}

	if (svsk->sk_conn) {
		svc_tcp_accept(svsk);
		svc_sock_accepted(svsk);
		return 0;
	}

	ready = svsk->sk_data;

	/* Receive data. If we haven't got the record length yet, get
	 * the next four bytes. Otherwise try to gobble up as much as
	 * possible up to the complete record length.
	 */
	if (svsk->sk_tcplen < 4) {
		unsigned long	want = 4 - svsk->sk_tcplen;
		struct iovec	iov;

		iov.iov_base = ((u32 *) &svsk->sk_reclen) + svsk->sk_tcplen;
		iov.iov_len  = want;
		if ((len = svc_recvfrom(rqstp, &iov, 1, want)) < 0)
			goto error;
		svsk->sk_tcplen += len;

		svsk->sk_reclen = ntohl(svsk->sk_reclen);
		if (!(svsk->sk_reclen & 0x80000000)) {
			/* FIXME: shutdown socket */
			printk(KERN_NOTICE "RPC: bad TCP reclen %08lx",
					(unsigned long) svsk->sk_reclen);
			return -EIO;
		}
		svsk->sk_reclen &= 0x7fffffff;
		dprintk("svc: TCP record, %ld bytes\n", svsk->sk_reclen);
	}

	/* Check whether enough data is available */
	len = svc_recv_available(svsk);
	if (len < 0)
		goto error;

	if (len < svsk->sk_reclen) {
		dprintk("svc: incomplete TCP record (%d of %ld)\n",
				len, svsk->sk_reclen);
		svc_sock_received(svsk, ready);
		len = -EAGAIN;	/* record not complete */
	}

	/* Frob argbuf */
	bufp->iov[0].iov_base += 4;
	bufp->iov[0].iov_len  -= 4;

	/* Now receive data */
	len = svc_recvfrom(rqstp, bufp->iov, bufp->nriov, svsk->sk_reclen);
	if (len < 0)
		goto error;

	dprintk("svc: TCP complete record (%d bytes)\n", len);

	/* Position reply write pointer immediately after
	 * record length */
	rqstp->rq_resbuf.buf += 1;
	rqstp->rq_resbuf.len  = 1;

	rqstp->rq_skbuff      = 0;
	rqstp->rq_argbuf.buf += 1;
	rqstp->rq_argbuf.len  = (len >> 2);
	rqstp->rq_prot	      = IPPROTO_TCP;

	/* Reset TCP read info */
	svsk->sk_reclen = 0;
	svsk->sk_tcplen = 0;

	svc_sock_received(svsk, 1);
	if (serv->sv_stats)
		serv->sv_stats->nettcpcnt++;

	return len;

error:
	if (len == -EAGAIN) {
		dprintk("RPC: TCP recvfrom got EAGAIN\n");
		svc_sock_received(svsk, ready); /* Clear data ready */
	} else {
		printk(KERN_NOTICE "%s: recvfrom returned errno %d\n",
					svsk->sk_server->sv_name, -len);
		svc_sock_received(svsk, 0);
	}

	return len;
}

/*
 * Send out data on TCP socket.
 * FIXME: Make the sendto call non-blocking in order not to hang
 * a daemon on a a dead client. Requires write queue maintenance.
 */
static int
svc_tcp_sendto(struct svc_rqst *rqstp)
{
	struct svc_buf	*bufp = &rqstp->rq_resbuf;

	/* Set up the first element of the reply iovec.
	 * Any other iovecs that may be in use have been taken
	 * care of by the server implementation itself.
	 */
	bufp->iov[0].iov_base = bufp->base;
	bufp->iov[0].iov_len  = bufp->len << 2;
	bufp->base[0] = htonl(0x80000000|((bufp->len << 2) - 4));

	return svc_sendto(rqstp, bufp->iov, bufp->nriov);
}

static int
svc_tcp_init(struct svc_sock *svsk)
{
	struct sock	*sk = svsk->sk_sk;

	svsk->sk_recvfrom = svc_tcp_recvfrom;
	svsk->sk_sendto = svc_tcp_sendto;

	if (sk->state == TCP_LISTEN) {
		dprintk("setting up TCP socket for listening\n");
		sk->state_change = svc_tcp_state_change1;
	} else {
		dprintk("setting up TCP socket for reading\n");
		sk->state_change = svc_tcp_state_change2;
		sk->data_ready = svc_tcp_data_ready;

		svsk->sk_reclen = 0;
		svsk->sk_tcplen = 0;
	}

	return 0;
}

/*
 * Receive the next request on any socket.
 */
int
svc_recv(struct svc_serv *serv, struct svc_rqst *rqstp)
{
	struct wait_queue	wait = { current, NULL };
	struct svc_sock		*svsk;
	int			len;

	dprintk("svc: server %p waiting for data (to = %ld)\n",
					rqstp, current->timeout);

again:
	/* Initialize the buffers */
	rqstp->rq_argbuf = rqstp->rq_defbuf;
	rqstp->rq_resbuf = rqstp->rq_defbuf;

	if (signalled())
		return -EINTR;

	disable_bh(NET_BH);
	if ((svsk = svc_sock_dequeue(serv)) != NULL) {
		enable_bh(NET_BH);
		rqstp->rq_sock = svsk;
		svsk->sk_inuse++; /* N.B. where is this decremented? */
	} else {
		/* No data pending. Go to sleep */
		rqstp->rq_sock = NULL;
		rqstp->rq_wait = NULL;
		svc_serv_enqueue(serv, rqstp);

		/*
		 * We have to be able to interrupt this wait
		 * to bring down the daemons ...
		 */
		current->state = TASK_INTERRUPTIBLE;
		add_wait_queue(&rqstp->rq_wait, &wait);
		enable_bh(NET_BH);
		schedule();

		if (!(svsk = rqstp->rq_sock)) {
			svc_serv_dequeue(serv, rqstp);
			if (!(svsk = rqstp->rq_sock))
				return signalled()? -EINTR : -EAGAIN;
		}
	}

	dprintk("svc: server %p servicing socket %p\n", rqstp, svsk);
	len = svsk->sk_recvfrom(rqstp);

	/* No data, incomplete (TCP) read, or accept() */
	if (len == 0 || len == -EAGAIN) {
		svc_sock_release(rqstp);
		goto again;
	}

	rqstp->rq_secure  = ntohs(rqstp->rq_addr.sin_port) < 1024;
	rqstp->rq_userset = 0;
	rqstp->rq_verfed  = 0;

	svc_getlong(&rqstp->rq_argbuf, rqstp->rq_xid);
	svc_putlong(&rqstp->rq_resbuf, rqstp->rq_xid);

	/* Assume that the reply consists of a single buffer. */
	rqstp->rq_resbuf.nriov = 1;

	if (serv->sv_stats)
		serv->sv_stats->netcnt++;
	return len;
}

/* 
 * Drop request
 */
void
svc_drop(struct svc_rqst *rqstp)
{
	dprintk("svc: socket %p dropped request\n", rqstp->rq_sock);
	svc_sock_release(rqstp);
}

/*
 * Return reply to client.
 */
int
svc_send(struct svc_rqst *rqstp)
{
	struct svc_sock	*svsk;
	int		len;

	if ((svsk = rqstp->rq_sock) == NULL) {
		printk(KERN_WARNING "NULL socket pointer in %s:%d\n",
				__FILE__, __LINE__);
		return -EFAULT;
	}

	/* release the receive skb before sending the reply */
	svc_release_skb(rqstp);

	len = svsk->sk_sendto(rqstp);
	svc_sock_release(rqstp);

	if (len == -ECONNREFUSED || len == -ENOTCONN || len == -EAGAIN)
		return 0;
	return len;
}

/*
 * Initialize socket for RPC use and create svc_sock struct
 * XXX: May want to setsockopt SO_SNDBUF and SO_RCVBUF.
 */
static struct svc_sock *
svc_setup_socket(struct svc_serv *serv, struct socket *sock,
					int *errp, int pmap_register)
{
	struct svc_sock	*svsk;
	struct sock	*inet;

	dprintk("svc: svc_setup_socket %p\n", sock);
	if (!(svsk = kmalloc(sizeof(*svsk), GFP_KERNEL))) {
		*errp = -ENOMEM;
		return NULL;
	}
	memset(svsk, 0, sizeof(*svsk));

#if LINUX_VERSION_CODE >= 0x020100
	inet = sock->sk;
#else
	inet = (struct sock *) sock->data;
#endif
	inet->user_data = svsk;
	svsk->sk_sock = sock;
	svsk->sk_sk = inet;
	svsk->sk_ostate = inet->state_change;
	svsk->sk_odata = inet->data_ready;
	svsk->sk_server = serv;

	/* Initialize the socket */
	if (sock->type == SOCK_DGRAM)
		*errp = svc_udp_init(svsk);
	else
		*errp = svc_tcp_init(svsk);
if (svsk->sk_sk == NULL)
	printk(KERN_WARNING "svsk->sk_sk == NULL after svc_prot_init!\n");

	/* Register socket with portmapper */
	if (*errp >= 0 && pmap_register)
		*errp = svc_register(serv, inet->protocol,
					ntohs(inet->dummy_th.source));

	if (*errp < 0) {
		inet->user_data = NULL;
		kfree(svsk);
		return NULL;
	}

	svsk->sk_list = serv->sv_allsocks;
	serv->sv_allsocks = svsk;

	dprintk("svc: svc_setup_socket created %p (inet %p)\n",
				svsk, svsk->sk_sk);
	return svsk;
}

/*
 * Create socket for RPC service.
 */
int
svc_create_socket(struct svc_serv *serv, int protocol, struct sockaddr_in *sin)
{
	struct svc_sock	*svsk;
	struct socket	*sock;
	int		error;
	int		type;

	dprintk("svc: svc_create_socket(%s, %d, %08lx:%d)\n",
				serv->sv_program->pg_name, protocol,
				ntohl(sin->sin_addr.s_addr),
				ntohs(sin->sin_port));

	if (protocol != IPPROTO_UDP && protocol != IPPROTO_TCP) {
		printk(KERN_WARNING "svc: only UDP and TCP "
				"sockets supported\n");
		return -EINVAL;
	}
	type = (protocol == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM;

	if ((error = sock_create(AF_INET, type, protocol, &sock)) < 0)
		return error;

	if (sin != NULL) {
		error = sock->ops->bind(sock, (struct sockaddr *) sin,
						sizeof(*sin));
		if (error < 0)
			goto bummer;
	}

	if (protocol == IPPROTO_TCP) {
		if ((error = sock->ops->listen(sock, 5)) < 0)
			goto bummer;
		sock->flags |= SO_ACCEPTCON;
	}

	if ((svsk = svc_setup_socket(serv, sock, &error, 1)) != NULL)
		return 0;

bummer:
	dprintk("svc: svc_create_socket error = %d\n", -error);
	sock_release(sock);
	return error;
}

/*
 * Remove a dead socket
 */
void
svc_delete_socket(struct svc_sock *svsk)
{
	struct svc_sock	**rsk;
	struct svc_serv	*serv;
	struct sock	*sk;

	dprintk("svc: svc_delete_socket(%p)\n", svsk);

	serv = svsk->sk_server;
	sk = svsk->sk_sk;

	sk->state_change = svsk->sk_ostate;
	sk->data_ready = svsk->sk_odata;

	for (rsk = &serv->sv_allsocks; *rsk; rsk = &(*rsk)->sk_list) {
		if (*rsk == svsk)
			break;
	}
	if (!*rsk)
		return;
	*rsk = svsk->sk_list;

	if (svsk->sk_qued)
		rpc_remove_list(&serv->sv_sockets, svsk);
	svsk->sk_dead = 1;

	if (!svsk->sk_inuse) {
		sock_release(svsk->sk_sock);
		kfree(svsk);
	} else {
		printk(KERN_NOTICE "svc: server socket destroy delayed\n");
		/* svsk->sk_server = NULL; */
	}
}