diff -u --recursive --new-file linux-2.4.15-viro1/include/linux/sunrpc/svc.h linux-2.4.15-tcp_svc/include/linux/sunrpc/svc.h --- linux-2.4.15-viro1/include/linux/sunrpc/svc.h Mon Nov 26 14:38:41 2001 +++ linux-2.4.15-tcp_svc/include/linux/sunrpc/svc.h Sun Nov 25 14:37:54 2001 @@ -26,8 +26,9 @@ * We currently do not support more than one RPC program per daemon. */ struct svc_serv { - struct svc_rqst * sv_threads; /* idle server threads */ - struct svc_sock * sv_sockets; /* pending sockets */ + struct list_head sv_biod; /* idle server threads */ + struct list_head sv_rqst; /* idle requests */ + struct list_head sv_socket; /* pending sockets */ struct svc_program * sv_program; /* RPC program */ struct svc_stat * sv_stats; /* RPC statistics */ spinlock_t sv_lock; @@ -35,12 +36,24 @@ unsigned int sv_bufsz; /* datagram buffer size */ unsigned int sv_xdrsize; /* XDR buffer size */ - struct svc_sock * sv_allsocks; /* all sockets */ + struct list_head sv_allsocks; /* all sockets */ char * sv_name; /* service name */ }; /* + * An RPC service I/O thread. + */ +struct svc_biod { + struct list_head io_list; /* Idle list */ + struct list_head io_svsk; /* Pending request */ + struct svc_serv * io_serv; /* RPC service */ + struct svc_rqst * io_rqst; /* The current request */ + wait_queue_t io_wait; /* Synchronization */ + struct completion io_completion; /* Initialization completion */ +}; + +/* * Maximum payload size supported by a kernel RPC server. * This is use to determine the max number of pages nfsd is * willing to return in a single READ operation. @@ -70,14 +83,14 @@ #define RPCSVC_MAXIOV ((RPCSVC_MAXPAYLOAD+PAGE_SIZE-1)/PAGE_SIZE + 1) struct svc_buf { u32 * area; /* allocated memory */ + unsigned int buflen; /* total length of allocated memory */ u32 * base; /* base of RPC datagram */ - int buflen; /* total length of buffer */ u32 * buf; /* read/write pointer */ - int len; /* current end of buffer */ + unsigned int len; /* current end of buffer */ /* iovec for zero-copy NFS READs */ struct iovec iov[RPCSVC_MAXIOV]; - int nriov; + unsigned int nriov; }; #define svc_getlong(argp, val) { (val) = *(argp)->buf++; (argp)->len--; } #define svc_putlong(resp, val) { *(resp)->buf++ = (val); (resp)->len++; } @@ -88,8 +101,7 @@ * NOTE: First two items must be prev/next. */ struct svc_rqst { - struct svc_rqst * rq_prev; /* idle list */ - struct svc_rqst * rq_next; + struct list_head rq_list; struct svc_sock * rq_sock; /* socket */ struct sockaddr_in rq_addr; /* peer address */ int rq_addrlen; @@ -109,7 +121,8 @@ unsigned short rq_verfed : 1, /* reply has verifier */ rq_userset : 1, /* auth->setuser OK */ rq_secure : 1, /* secure port */ - rq_auth : 1; /* check client */ + rq_auth : 1, /* check client */ + rq_private : 1; /* Owned by a socket */ void * rq_argp; /* decoded arguments */ void * rq_resp; /* xdr'd results */ @@ -118,7 +131,8 @@ struct svc_client * rq_client; /* RPC peer info */ struct svc_cacherep * rq_cacherep; /* cache info */ - wait_queue_head_t rq_wait; /* synchronozation */ + /* Bottom half function */ + int (*rq_action)(struct svc_rqst *); }; /* @@ -180,4 +194,28 @@ int svc_register(struct svc_serv *, int, unsigned short); void svc_wake_up(struct svc_serv *); +/* + * Enqueue a request. Must have serv->sv_lock held. + */ +static inline void +svc_enqueue_request(struct svc_serv *serv, struct svc_rqst *rqst) +{ + list_add_tail(&rqst->rq_list, &serv->sv_rqst); +} + +/* + * Dequeue a request. Must have serv->sv_lock held. + */ +static inline struct svc_rqst * +svc_dequeue_request(struct svc_serv *serv) +{ + struct svc_rqst *rqst; + + if (list_empty(&sv_rqst)) + return NULL; + rqst = list_entry(serv->sv_rqst.next, struct svc_serv, sv_rqst); + list_del_init(&rqst->rq_list); + return rqst; +} + #endif /* SUNRPC_SVC_H */ diff -u --recursive --new-file linux-2.4.15-viro1/include/linux/sunrpc/svcsock.h linux-2.4.15-tcp_svc/include/linux/sunrpc/svcsock.h --- linux-2.4.15-viro1/include/linux/sunrpc/svcsock.h Mon Nov 26 14:38:50 2001 +++ linux-2.4.15-tcp_svc/include/linux/sunrpc/svcsock.h Sun Nov 25 13:03:37 2001 @@ -16,24 +16,25 @@ * NOTE: First two items must be prev/next. */ struct svc_sock { - struct svc_sock * sk_prev; /* list of ready sockets */ - struct svc_sock * sk_next; - struct svc_sock * sk_list; /* list of all sockets */ + struct list_head sk_list; + struct list_head sk_allsocks; /* Queue of idle sockets */ + struct list_head sk_rqsts; /* List of pending requests */ + struct list_head sk_idlerqst; /* List of idle requests */ struct socket * sk_sock; /* berkeley socket layer */ struct sock * sk_sk; /* INET layer */ spinlock_t sk_lock; + struct sockaddr_in sk_addr; /* Incoming connection address */ + int sk_addrlen; struct svc_serv * sk_server; /* service for this socket */ - unsigned char sk_inuse; /* use count */ - unsigned char sk_busy; /* enqueued/receiving */ + unsigned long sk_flags; /* socket flags */ unsigned char sk_conn; /* conn pending */ unsigned char sk_close; /* dead or dying */ int sk_data; /* data pending */ unsigned int sk_temp : 1, /* temp socket */ sk_qued : 1, /* on serv->sk_sockets */ sk_dead : 1; /* socket closed */ - int (*sk_recvfrom)(struct svc_rqst *rqstp); - int (*sk_sendto)(struct svc_rqst *rqstp); + struct svc_sockops *sk_ops; /* We keep the old state_change and data_ready CB's here */ void (*sk_ostate)(struct sock *); @@ -47,12 +48,19 @@ struct svc_rqst * sk_rqstp; }; +struct svc_sockops { + int (*sock_create)(struct sockaddr *, unsigned, struct socket **); + int (*init)(struct svc_sock *); + int (*recvfrom)(struct svc_sock *, struct svc_rqst *); + int (*sendto)(struct svc_sock *, struct svc_rqst *); +}; + /* * Function prototypes. */ int svc_makesock(struct svc_serv *, int, unsigned short); void svc_delete_socket(struct svc_sock *); -int svc_recv(struct svc_serv *, struct svc_rqst *, long); +int svc_recvmsg(struct svc_biod *, struct svc_rqst **, long); int svc_send(struct svc_rqst *); void svc_drop(struct svc_rqst *); diff -u --recursive --new-file linux-2.4.15-viro1/net/sunrpc/svc.c linux-2.4.15-tcp_svc/net/sunrpc/svc.c --- linux-2.4.15-viro1/net/sunrpc/svc.c Fri Sep 7 19:48:39 2001 +++ linux-2.4.15-tcp_svc/net/sunrpc/svc.c Sun Nov 25 21:43:21 2001 @@ -95,10 +95,8 @@ bufp->len = 0; bufp->buflen = size >> 2; - bufp->iov[0].iov_base = bufp->area; - bufp->iov[0].iov_len = size; - bufp->nriov = 1; - + bufp->iovec[0].iov_base = bufp->base; + bufp->iovec[0].iov_len = size; return 1; } @@ -113,56 +111,96 @@ } /* + * Create an RPC server request buffer + */ +struct svc_rqst * +svc_alloc_rqst(unsigned arg_sz, unsigned res_sz, unsigned bufsz) +{ + struct svc_rqst *rqstp; + + rqstp = kmalloc(sizeof(*rqstp), GFP_KERNEL); + if (!rqstp) + goto out_err; + memset(rqstp, 0, sizeof(*rqstp)); + INIT_LIST_HEAD(&rqstp->rq_list); + init_waitqueue_head(&rqstp->rq_wait); + + rqstp->rq_argp = (u32 *) kmalloc(arg_sz, GFP_KERNEL); + rqstp->rq_resp = (u32 *) kmalloc(res_sz, GFP_KERNEL); + if (!rqstp->rq_argp || !rqstp->rq_resp) + goto out_free; + if (!svc_init_buffer(&rqstp->rq_defbuf, bufsz)) + goto out_free; + return rqstp; + out_free: + if (rqstp->rq_argp) + kfree(rqstp->rq_argp); + if (rqstp->rq_resp) + kfree(rqstp->rq_resp); + kfree(rqstp); + out_err: + return NULL; +} + +/* + * Free an RPC server request buffer + */ +void +svc_free_rqst(struct svc_rqst *rqstp) +{ + if (!list_empty(&rqstp->rq_list)) + BUG(); + svc_release_buffer(&rqstp->rq_defbuf); + kfree(rqstp->rq_resp); + kfree(rqstp->rq_argp); + kfree(rqstp); +} + +/* * Create a server thread */ int svc_create_thread(svc_thread_fn func, struct svc_serv *serv) { - struct svc_rqst *rqstp; + struct svc_biod *biodp; int error = -ENOMEM; - rqstp = kmalloc(sizeof(*rqstp), GFP_KERNEL); + rqstp = kmalloc(sizeof(*biodp), GFP_KERNEL); if (!rqstp) goto out; - memset(rqstp, 0, sizeof(*rqstp)); - init_waitqueue_head(&rqstp->rq_wait); - - if (!(rqstp->rq_argp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL)) - || !(rqstp->rq_resp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL)) - || !svc_init_buffer(&rqstp->rq_defbuf, serv->sv_bufsz)) - goto out_thread; + INIT_LIST_HEAD(&biodp->io_list); + INIT_LIST_HEAD(&biodp->io_svsk); + biodp->io_serv = serv; + init_waitqueue_head(&biodp->io_wait); + init_completion(&biodp->io_completion); serv->sv_nrthreads++; - rqstp->rq_server = serv; - error = kernel_thread((int (*)(void *)) func, rqstp, 0); + error = kernel_thread((int (*)(void *)) func, biodp, 0); if (error < 0) goto out_thread; - error = 0; + return 0; +out_thread: + svc_exit_thread(biodp); out: return error; -out_thread: - svc_exit_thread(rqstp); - goto out; } /* * Destroy an RPC server thread */ void -svc_exit_thread(struct svc_rqst *rqstp) +svc_exit_thread(struct svc_biod *biod) { - struct svc_serv *serv = rqstp->rq_server; - - svc_release_buffer(&rqstp->rq_defbuf); - if (rqstp->rq_resp) - kfree(rqstp->rq_resp); - if (rqstp->rq_argp) - kfree(rqstp->rq_argp); - kfree(rqstp); + struct svc_serv *serv = biod->io_serv; + if (biod->io_rqst) + BUG(); /* Release the server */ + if (!list_empty(&biod->io_list)) + list_del(&biod->io_list); + kfree(biod); if (serv) svc_destroy(serv); } diff -u --recursive --new-file linux-2.4.15-viro1/net/sunrpc/svcsock.c linux-2.4.15-tcp_svc/net/sunrpc/svcsock.c --- linux-2.4.15-viro1/net/sunrpc/svcsock.c Wed Jul 4 20:50:38 2001 +++ linux-2.4.15-tcp_svc/net/sunrpc/svcsock.c Sun Nov 25 14:37:47 2001 @@ -55,27 +55,41 @@ 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. Must have serv->sv_lock held. */ static inline void -svc_serv_enqueue(struct svc_serv *serv, struct svc_rqst *rqstp) +svc_enqueue_biod(struct svc_serv *serv, struct svc_biod *biod) +{ + list_add(&biod->io_list, &serv->sv_biod); +} + +/* + * Dequeue an nfsd thread. Must have serv->sv_lock held. + */ +static inline struct svc_biod * +svc_dequeue_biod(struct svc_serv *serv) { - rpc_append_list(&serv->sv_threads, rqstp); + struct svc_biod *biod; + + if (list_empty(&serv->sv_biod)) + return NULL; + biod = list_entry(serv->sv_biod.next, struct svc_serv, sv_biod); + list_del_init(&biod->io_list); + return biod; } /* - * Dequeue an nfsd thread. Must have serv->sv_lock held. + * Notify an nfsd thread that some action is pending. */ static inline void -svc_serv_dequeue(struct svc_serv *serv, struct svc_rqst *rqstp) +svc_wakeup_biod(struct svc_serv *serv) { - rpc_remove_list(&serv->sv_threads, rqstp); + struct svc_biod *biod = svc_dequeue_biod(serv); + + if (biod) + wake_up(&biod->io_wait); } /* @@ -94,6 +108,23 @@ skb_free_datagram(rqstp->rq_sock->sk_sk, skb); } +static void +svsk_enqueue_request(struct svc_sock *svsk, struct svc_rqst *rqst) +{ + list_add_tail(&rqst->rq_list, &svsk->sk_idlerqst); +} + +static struct svc_rqst * +svsk_dequeue_request(struct svc_sock *svsk) +{ + struct svc_rqst *rqst; + + if (list_empty(&svsk->idlerqst)) + return NULL; + rqst = list_entry(svsk->sk_idlerqst.next, struct svc_sock, sk_idlerqst); + return rqst; +} + /* * Queue up a socket with data pending. If there are idle nfsd * processes, wake 'em up. @@ -124,23 +155,10 @@ * on the idle list. */ svsk->sk_busy = 1; + svsk->sk_inuse++; - if ((rqstp = serv->sv_threads) != NULL) { - dprintk("svc: socket %p served by daemon %p\n", - svsk->sk_sk, rqstp); - svc_serv_dequeue(serv, rqstp); - if (rqstp->rq_sock) - printk(KERN_ERR - "svc_sock_enqueue: server %p, rq_sock=%p!\n", - rqstp, rqstp->rq_sock); - 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; - } + list_add_tail(&svsk->sk_list, &serv->sv_socket); + svc_wakeup_biod(serv); out_unlock: spin_unlock(&serv->sv_lock); @@ -154,14 +172,13 @@ { struct svc_sock *svsk; - if ((svsk = serv->sv_sockets) != NULL) - rpc_remove_list(&serv->sv_sockets, svsk); - - if (svsk) { - dprintk("svc: socket %p dequeued, inuse=%d\n", - svsk->sk_sk, svsk->sk_inuse); - svsk->sk_qued = 0; - } + if (list_empty(&serv->sv_socket)) + return NULL; + svsk = list_entry(serv->sv_socket.next, struct svc_serv, sv_socket); + list_del_init(&svsk->sk_list); + dprintk("svc: socket %p dequeued, inuse=%d\n", + svsk->sk_sk, svsk->sk_inuse); + svsk->sk_busy = 1; return svsk; } @@ -223,8 +240,7 @@ dprintk("svc: releasing dead socket\n"); sock_release(svsk->sk_sock); kfree(svsk); - } - else + } else spin_unlock_bh(&serv->sv_lock); } @@ -237,14 +253,7 @@ struct svc_rqst *rqstp; spin_lock_bh(&serv->sv_lock); - 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); - } + svc_wakeup_biod(serv); spin_unlock_bh(&serv->sv_lock); } @@ -252,10 +261,9 @@ * Generic sendto routine */ static int -svc_sendto(struct svc_rqst *rqstp, struct iovec *iov, int nr) +svc_sendto(struct svc_sock *svsk, struct svc_rqst *rqstp, struct iovec *iov, int nr) { mm_segment_t oldfs; - struct svc_sock *svsk = rqstp->rq_sock; struct socket *sock = svsk->sk_sock; struct msghdr msg; int i, buflen, len; @@ -292,8 +300,8 @@ 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); + oldfs = get_fs(); set_fs(get_ds()); + err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail); set_fs(oldfs); return (err >= 0)? avail : err; @@ -364,9 +372,8 @@ * Receive a datagram from a UDP socket. */ static int -svc_udp_recvfrom(struct svc_rqst *rqstp) +svc_udp_recvfrom(struct svc_sock *svsk, struct svc_rqst *rqstp) { - struct svc_sock *svsk = rqstp->rq_sock; struct svc_serv *serv = svsk->sk_server; struct sk_buff *skb; u32 *data; @@ -427,7 +434,7 @@ } static int -svc_udp_sendto(struct svc_rqst *rqstp) +svc_udp_sendto(struct svc_sock *svsk, struct svc_rqst *rqstp) { struct svc_buf *bufp = &rqstp->rq_resbuf; int error; @@ -440,10 +447,10 @@ bufp->iov[0].iov_base = bufp->base; bufp->iov[0].iov_len = bufp->len << 2; - error = svc_sendto(rqstp, bufp->iov, bufp->nriov); + error = svc_sendto(svsk, rqstp, bufp->iov, bufp->nriov); if (error == -ECONNREFUSED) /* ICMP error on earlier request. */ - error = svc_sendto(rqstp, bufp->iov, bufp->nriov); + error = svc_sendto(svsk, rqstp, bufp->iov, bufp->nriov); else if (error == -EAGAIN) /* Ignore and wait for re-xmit */ error = 0; @@ -455,8 +462,6 @@ 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; } @@ -592,6 +597,8 @@ if (!(newsvsk = svc_setup_socket(serv, newsock, &err, 0))) goto failed; + memcpy(&newsock->sk_addr, &sin, sizeof(newsock->sk_addr)); + newsock->sk_addrlen = slen; /* Precharge. Data may have arrived on the socket before we * installed the data_ready callback. @@ -616,9 +623,8 @@ * Receive data from a TCP socket. */ static int -svc_tcp_recvfrom(struct svc_rqst *rqstp) +svc_tcp_recvfrom(struct svc_sock *svsk, 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, used; @@ -741,7 +747,7 @@ * a daemon on a dead client. Requires write queue maintenance. */ static int -svc_tcp_sendto(struct svc_rqst *rqstp) +svc_tcp_sendto(struct svc_sock *svsk, struct svc_rqst *rqstp) { struct svc_buf *bufp = &rqstp->rq_resbuf; int sent; @@ -754,7 +760,7 @@ bufp->iov[0].iov_len = bufp->len << 2; bufp->base[0] = htonl(0x80000000|((bufp->len << 2) - 4)); - sent = svc_sendto(rqstp, bufp->iov, bufp->nriov); + sent = svc_sendto(svsk, rqstp, bufp->iov, bufp->nriov); if (sent != bufp->len<<2) { printk(KERN_NOTICE "rpc-srv/tcp: %s: sent only %d bytes of %d - should shutdown socket\n", rqstp->rq_sock->sk_server->sv_name, @@ -772,9 +778,6 @@ { 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->data_ready = svc_tcp_listen_data_ready; @@ -790,68 +793,51 @@ return 0; } -/* - * Receive the next request on any socket. - */ -int -svc_recv(struct svc_serv *serv, struct svc_rqst *rqstp, long timeout) +static int +svc_poll(struct svc_biod *biod, struct svc_sock **svsk, long timeout) { - struct svc_sock *svsk; - int len; + struct svc_serv *serv = biod->io_serv; DECLARE_WAITQUEUE(wait, current); - dprintk("svc: server %p waiting for data (to = %ld)\n", - rqstp, timeout); - - if (rqstp->rq_sock) - printk(KERN_ERR - "svc_recv: service %p, socket not NULL!\n", - rqstp); - if (waitqueue_active(&rqstp->rq_wait)) - printk(KERN_ERR - "svc_recv: service %p, wait queue active!\n", - rqstp); - - /* Initialize the buffers */ - rqstp->rq_argbuf = rqstp->rq_defbuf; - rqstp->rq_resbuf = rqstp->rq_defbuf; - - if (signalled()) - return -EINTR; - spin_lock_bh(&serv->sv_lock); - if ((svsk = svc_sock_dequeue(serv)) != NULL) { - rqstp->rq_sock = svsk; - svsk->sk_inuse++; - } else { + for (;(*svsk = svc_sock_dequeue(serv)) == NULL && timeout != 0;) { + dprintk("svc: server %p waiting for data (to = %ld)\n", + biod, timeout); /* No data pending. Go to sleep */ - svc_serv_enqueue(serv, rqstp); + svc_enqueue_biod(serv, biod); /* * We have to be able to interrupt this wait * to bring down the daemons ... */ set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&rqstp->rq_wait, &wait); + add_wait_queue(&biod->io_wait, &wait); spin_unlock_bh(&serv->sv_lock); - schedule_timeout(timeout); + timeout = schedule_timeout(timeout); spin_lock_bh(&serv->sv_lock); - remove_wait_queue(&rqstp->rq_wait, &wait); - - if (!(svsk = rqstp->rq_sock)) { - svc_serv_dequeue(serv, rqstp); - spin_unlock_bh(&serv->sv_lock); - dprintk("svc: server %p, no data yet\n", rqstp); - return signalled()? -EINTR : -EAGAIN; - } + remove_wait_queue(&biod->io_wait, &wait); } spin_unlock_bh(&serv->sv_lock); + if (*svsk) + return 0; + dprintk("svc: server %p, no data yet\n", rqstp); + return signalled()? -EINTR : -EAGAIN; +} + +static int +svc_recv(struct svc_rqst *rqstp) +{ + struct svc_sock svsk = rqstp->rq_sock; + int len; dprintk("svc: server %p, socket %p, inuse=%d\n", rqstp, svsk, svsk->sk_inuse); - len = svsk->sk_recvfrom(rqstp); + /* Initialize the buffers */ + rqstp->rq_argbuf = rqstp->rq_defbuf; + rqstp->rq_resbuf = rqstp->rq_defbuf; + len = svsk->sk_ops->recvfrom(rqstp); dprintk("svc: got len=%d\n", len); /* No data, incomplete (TCP) read, or accept() */ @@ -875,6 +861,54 @@ return len; } +static struct svc_rqst * +svsk_recvmsg(struct svc_sock *svsk) +{ + struct svc_serv *serv; + struct svc_rqst *rqst; + + if (!list_empty(&sk_rqsts)) { + rqst = list_entry(&svsk->sk_rqsts, struct svc_sock, sk_rqsts); + goto out; + } + serv = svsk->sk_serv; + rqst = svc_dequeue_request(serv); + if (!rqst) { + rqst = svsk_dequeue_request(svsk); + if (!rqst) + goto out; + } + rqst->rq_sock = svsk; + rqst->rq_server = serv; + rqst->action = svc_recv; + out: + return rqst; +} + +/* + * Receive the next request on any socket. + */ +int +svc_recvmsg(struct svc_biod *biod, struct svc_rqst **rqstp, long timeout) +{ + struct svc_sock *svsk; + struct svc_rqst *rqst; + int res; + + *rqstp = NULL; + for (;;) { + res = svc_poll(biod, &svsk, timeout); + if (res < 0) + break; + if ((rqst = svsk_recvmsg(svsk)) != NULL) { + if ((res = rqst->rq_action(rqst)) > 0) + *rqstp = rqst; + break; + } + } + return res; +} + /* * Drop request */ @@ -903,7 +937,7 @@ /* release the receive skb before sending the reply */ svc_release_skb(rqstp); - len = svsk->sk_sendto(rqstp); + len = svsk->sk_ops->sendto(svsk, rqstp); svc_sock_release(rqstp); if (len == -ECONNREFUSED || len == -ENOTCONN || len == -EAGAIN) @@ -917,17 +951,23 @@ */ static struct svc_sock * svc_setup_socket(struct svc_serv *serv, struct socket *sock, - int *errp, int pmap_register) + struct svc_sockops *svsk_ops, int *errp, int pmap_register) { struct svc_sock *svsk; struct sock *inet; + struct svc_rqst *rqst; + *errp = -ENOMEM; dprintk("svc: svc_setup_socket %p\n", sock); - if (!(svsk = kmalloc(sizeof(*svsk), GFP_KERNEL))) { - *errp = -ENOMEM; - return NULL; - } + if (!(svsk = kmalloc(sizeof(*svsk), GFP_KERNEL))) + goto out_err; memset(svsk, 0, sizeof(*svsk)); + INIT_LIST_HEAD(&svsk->sk_queue); + INIT_LIST_HEAD(&svsk->sk_list); + INIT_LIST_HEAD(&svsk->sk_rqsts); + INIT_LIST_HEAD(&svsk->sk_reply); + INIT_LIST_HEAD(&svsk->sk_idlerqst); + svsk->sk_ops = svsk_ops; inet = sock->sk; inet->user_data = svsk; @@ -937,14 +977,17 @@ svsk->sk_odata = inet->data_ready; svsk->sk_server = serv; spin_lock_init(&svsk->sk_lock); + rqst = svc_alloc_rqst(serv->sv_xdrsize, serv->sv_xdrsize, serv->sv_bufsz); + if (!rqst) + goto out_dealloc; + rqst->rq_private = 1; + rqst->rq_sock = svsk; + svsk_enqueue_request(svsk, rqst); /* 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"); + *errp = svsk_ops->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) @@ -952,25 +995,62 @@ if (*errp < 0) { inet->user_data = NULL; - kfree(svsk); - return NULL; + goto out_dealloc; } spin_lock_bh(&serv->sv_lock); - svsk->sk_list = serv->sv_allsocks; - serv->sv_allsocks = svsk; + list_add(&svsk->sk_allsocks, &serv->sv_allsocks); spin_unlock_bh(&serv->sv_lock); dprintk("svc: svc_setup_socket created %p (inet %p)\n", svsk, svsk->sk_sk); return svsk; + out_dealloc: + if (rqst) + svc_free_rqst(rqst); + kfree(svsk); + out_err: + dprintk("svc: svc_setup_socket failed (error %d)\n", -*errp); + return NULL; +} + +static int +svc_udp_sock_create(struct sockaddr *sin, unsigned slen, struct socket **sock) +{ + int error; + + error = sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, sock); + if (error < 0) + return error; + error = sock->ops->bind(sock, sin, slen); + if (error < 0) + return error; + return 0; +} + +static int +svc_tcp_sock_create(struct sockaddr *sin, unsigned slen, struct socket **sock) +{ + int error; + + error = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, sock); + if (error < 0) + return error; + error = sock->ops->bind(sock, sin, slen); + if (error < 0) + return error; + error = sock->ops->listen(sock, 5); + if (error < 0) + return error; + return 0; } /* * Create socket for RPC service. */ static int -svc_create_socket(struct svc_serv *serv, int protocol, struct sockaddr_in *sin) +svc_create_socket(struct svc_serv *serv, struct svc_sockops *svsk_ops, + struct sockaddr *sin, unsigned int addrlen) { struct svc_sock *svsk; struct socket *sock; @@ -982,27 +1062,9 @@ NIPQUAD(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(PF_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; - } + error = svsk_ops->sock_create(sin, addrlen, &sock); + if (error < 0) + goto bummer; if ((svsk = svc_setup_socket(serv, sock, &error, 1)) != NULL) return 0; @@ -1021,6 +1083,7 @@ { struct svc_sock **rsk; struct svc_serv *serv; + struct svc_rqst *rqst; struct sock *sk; dprintk("svc: svc_delete_socket(%p)\n", svsk); @@ -1033,32 +1096,38 @@ spin_lock_bh(&serv->sv_lock); - for (rsk = &serv->sv_allsocks; *rsk; rsk = &(*rsk)->sk_list) { - if (*rsk == svsk) - break; - } - if (!*rsk) { - spin_unlock_bh(&serv->sv_lock); - return; - } - *rsk = svsk->sk_list; - if (svsk->sk_qued) - rpc_remove_list(&serv->sv_sockets, svsk); - + list_del(&svsk->sk_allsocks); + if (!list_empty(&svsk->sk_list)) + list_del(&svsk->sk_list); svsk->sk_dead = 1; if (!svsk->sk_inuse) { spin_unlock_bh(&serv->sv_lock); + while ((rqst = svsk_dequeue_request(svsk)) != NULL) + svc_free_rqst(rqst); sock_release(svsk->sk_sock); kfree(svsk); } else { spin_unlock_bh(&serv->sv_lock); printk(KERN_NOTICE "svc: server socket destroy delayed\n"); - /* svsk->sk_server = NULL; */ } } +static struct svc_sockops { + sock_create: svc_tcp_sock_create; + init: svc_udp_init; + recvfrom: svc_udp_recvfrom; + sendto: svc_udp_sendto; +} svc_udp_ops; + +static struct svc_sockops { + sock_create: svc_udp_sock_create; + init: svc_tcp_init; + recvfrom: svc_tcp_recvfrom; + sendto: svc_tcp_sendto; +} svc_tcp_ops; + /* * Make a socket for nfsd and lockd */ @@ -1066,11 +1135,25 @@ svc_makesock(struct svc_serv *serv, int protocol, unsigned short port) { struct sockaddr_in sin; + struct svc_sockops *svsk_ops; dprintk("svc: creating socket proto = %d\n", protocol); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(port); - return svc_create_socket(serv, protocol, &sin); + switch (protocol) { + case IPPROTO_TCP: + svsk_ops = &svc_tcp_ops; + break; + case IPPROTO_UDP: + svsk_ops = &svc_udp_ops; + break; + default: + printk(KERN_WARNING "svc: only UDP and TCP " + "sockets supported\n"); + return -EINVAL; + } + return svc_create_socket(serv, svsk_ops, (struct sockaddr *)&sin, + sizeof(sin)); }