📄 svcsock.c
字号:
/* * 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/inet.h>#include <linux/udp.h>#include <linux/tcp.h>#include <linux/unistd.h>#include <linux/slab.h>#include <linux/netdevice.h>#include <linux/skbuff.h>#include <linux/suspend.h>#include <net/sock.h>#include <net/checksum.h>#include <net/ip.h>#include <net/tcp.h>#include <asm/uaccess.h>#include <asm/ioctls.h>#include <linux/sunrpc/types.h>#include <linux/sunrpc/xdr.h>#include <linux/sunrpc/svcsock.h>#include <linux/sunrpc/stats.h>/* SMP locking strategy: * * svc_serv->sv_lock protects most stuff for that service. * * Some flags can be set to certain values at any time * providing that certain rules are followed: * * SK_BUSY can be set to 0 at any time. * svc_sock_enqueue must be called afterwards * SK_CONN, SK_DATA, can be set or cleared at any time. * after a set, svc_sock_enqueue must be called. * after a clear, the socket must be read/accepted * if this succeeds, it must be set again. * SK_CLOSE can set at any time. It is never cleared. * */#define RPCDBG_FACILITY RPCDBG_SVCSOCKstatic 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 *);static struct svc_deferred_req *svc_deferred_dequeue(struct svc_sock *svsk);static int svc_deferred_recv(struct svc_rqst *rqstp);static struct cache_deferred_req *svc_defer(struct cache_req *req);/* * Queue up an idle server thread. Must have serv->sv_lock held. * Note: this is really a stack rather than a queue, so that we only * use as many different threads as we need, and the rest don't polute * the cache. */static inline voidsvc_serv_enqueue(struct svc_serv *serv, struct svc_rqst *rqstp){ list_add(&rqstp->rq_list, &serv->sv_threads);}/* * Dequeue an nfsd thread. Must have serv->sv_lock held. */static inline voidsvc_serv_dequeue(struct svc_serv *serv, struct svc_rqst *rqstp){ list_del(&rqstp->rq_list);}/* * Release an skbuff after use */static inline voidsvc_release_skb(struct svc_rqst *rqstp){ struct sk_buff *skb = rqstp->rq_skbuff; struct svc_deferred_req *dr = rqstp->rq_deferred; if (skb) { rqstp->rq_skbuff = NULL; dprintk("svc: service %p, releasing skb %p\n", rqstp, skb); skb_free_datagram(rqstp->rq_sock->sk_sk, skb); } if (dr) { rqstp->rq_deferred = NULL; kfree(dr); }}/* * Any space to write? */static inline unsigned longsvc_sock_wspace(struct svc_sock *svsk){ int wspace; if (svsk->sk_sock->type == SOCK_STREAM) wspace = sk_stream_wspace(svsk->sk_sk); else wspace = sock_wspace(svsk->sk_sk); return wspace;}/* * Queue up a socket with data pending. If there are idle nfsd * processes, wake 'em up. * */static voidsvc_sock_enqueue(struct svc_sock *svsk){ struct svc_serv *serv = svsk->sk_server; struct svc_rqst *rqstp; if (!(svsk->sk_flags & ( (1<<SK_CONN)|(1<<SK_DATA)|(1<<SK_CLOSE)|(1<<SK_DEFERRED)) )) return; if (test_bit(SK_DEAD, &svsk->sk_flags)) return; spin_lock_bh(&serv->sv_lock); if (!list_empty(&serv->sv_threads) && !list_empty(&serv->sv_sockets)) printk(KERN_ERR "svc_sock_enqueue: threads and sockets both waiting??\n"); if (test_bit(SK_DEAD, &svsk->sk_flags)) { /* Don't enqueue dead sockets */ dprintk("svc: socket %p is dead, not enqueued\n", svsk->sk_sk); goto out_unlock; } if (test_bit(SK_BUSY, &svsk->sk_flags)) { /* Don't enqueue socket while daemon is receiving */ dprintk("svc: socket %p busy, not enqueued\n", svsk->sk_sk); goto out_unlock; } set_bit(SOCK_NOSPACE, &svsk->sk_sock->flags); if (((svsk->sk_reserved + serv->sv_bufsz)*2 > svc_sock_wspace(svsk)) && !test_bit(SK_CLOSE, &svsk->sk_flags) && !test_bit(SK_CONN, &svsk->sk_flags)) { /* Don't enqueue while not enough space for reply */ dprintk("svc: socket %p no space, %d*2 > %ld, not enqueued\n", svsk->sk_sk, svsk->sk_reserved+serv->sv_bufsz, svc_sock_wspace(svsk)); goto out_unlock; } clear_bit(SOCK_NOSPACE, &svsk->sk_sock->flags); /* 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. */ set_bit(SK_BUSY, &svsk->sk_flags); if (!list_empty(&serv->sv_threads)) { rqstp = list_entry(serv->sv_threads.next, struct svc_rqst, rq_list); 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++; rqstp->rq_reserved = serv->sv_bufsz; svsk->sk_reserved += rqstp->rq_reserved; wake_up(&rqstp->rq_wait); } else { dprintk("svc: socket %p put into queue\n", svsk->sk_sk); list_add_tail(&svsk->sk_ready, &serv->sv_sockets); }out_unlock: spin_unlock_bh(&serv->sv_lock);}/* * Dequeue the first socket. Must be called with the serv->sv_lock held. */static inline struct svc_sock *svc_sock_dequeue(struct svc_serv *serv){ struct svc_sock *svsk; if (list_empty(&serv->sv_sockets)) return NULL; svsk = list_entry(serv->sv_sockets.next, struct svc_sock, sk_ready); list_del_init(&svsk->sk_ready); dprintk("svc: socket %p dequeued, inuse=%d\n", svsk->sk_sk, svsk->sk_inuse); return svsk;}/* * Having read something from a socket, check whether it * needs to be re-enqueued. * Note: SK_DATA only gets cleared when a read-attempt finds * no (or insufficient) data. */static inline voidsvc_sock_received(struct svc_sock *svsk){ clear_bit(SK_BUSY, &svsk->sk_flags); svc_sock_enqueue(svsk);}/** * svc_reserve - change the space reserved for the reply to a request. * @rqstp: The request in question * @space: new max space to reserve * * Each request reserves some space on the output queue of the socket * to make sure the reply fits. This function reduces that reserved * space to be the amount of space used already, plus @space. * */void svc_reserve(struct svc_rqst *rqstp, int space){ space += rqstp->rq_res.head[0].iov_len; if (space < rqstp->rq_reserved) { struct svc_sock *svsk = rqstp->rq_sock; spin_lock_bh(&svsk->sk_server->sv_lock); svsk->sk_reserved -= (rqstp->rq_reserved - space); rqstp->rq_reserved = space; spin_unlock_bh(&svsk->sk_server->sv_lock); svc_sock_enqueue(svsk); }}/* * Release a socket after use. */static inline voidsvc_sock_put(struct svc_sock *svsk){ struct svc_serv *serv = svsk->sk_server; spin_lock_bh(&serv->sv_lock); if (!--(svsk->sk_inuse) && test_bit(SK_DEAD, &svsk->sk_flags)) { spin_unlock_bh(&serv->sv_lock); dprintk("svc: releasing dead socket\n"); sock_release(svsk->sk_sock); kfree(svsk); } else spin_unlock_bh(&serv->sv_lock);}static voidsvc_sock_release(struct svc_rqst *rqstp){ struct svc_sock *svsk = rqstp->rq_sock; svc_release_skb(rqstp); svc_free_allpages(rqstp); rqstp->rq_res.page_len = 0; rqstp->rq_res.page_base = 0; /* Reset response buffer and release * the reservation. * But first, check that enough space was reserved * for the reply, otherwise we have a bug! */ if ((rqstp->rq_res.len) > rqstp->rq_reserved) printk(KERN_ERR "RPC request reserved %d but used %d\n", rqstp->rq_reserved, rqstp->rq_res.len); rqstp->rq_res.head[0].iov_len = 0; svc_reserve(rqstp, 0); rqstp->rq_sock = NULL; svc_sock_put(svsk);}/* * External function to wake up a server waiting for data */voidsvc_wake_up(struct svc_serv *serv){ struct svc_rqst *rqstp; spin_lock_bh(&serv->sv_lock); if (!list_empty(&serv->sv_threads)) { rqstp = list_entry(serv->sv_threads.next, struct svc_rqst, rq_list); dprintk("svc: daemon %p woken up.\n", rqstp); /* svc_serv_dequeue(serv, rqstp); rqstp->rq_sock = NULL; */ wake_up(&rqstp->rq_wait); } spin_unlock_bh(&serv->sv_lock);}/* * Generic sendto routine */static intsvc_sendto(struct svc_rqst *rqstp, struct xdr_buf *xdr){ struct svc_sock *svsk = rqstp->rq_sock; struct socket *sock = svsk->sk_sock; int slen; char buffer[CMSG_SPACE(sizeof(struct in_pktinfo))]; struct cmsghdr *cmh = (struct cmsghdr *)buffer; struct in_pktinfo *pki = (struct in_pktinfo *)CMSG_DATA(cmh); int len = 0; int result; int size; struct page **ppage = xdr->pages; size_t base = xdr->page_base; unsigned int pglen = xdr->page_len; unsigned int flags = MSG_MORE; slen = xdr->len; if (rqstp->rq_prot == IPPROTO_UDP) { /* set the source and destination */ struct msghdr msg; msg.msg_name = &rqstp->rq_addr; msg.msg_namelen = sizeof(rqstp->rq_addr); msg.msg_iov = NULL; msg.msg_iovlen = 0; msg.msg_flags = MSG_MORE; msg.msg_control = cmh; msg.msg_controllen = sizeof(buffer); cmh->cmsg_len = CMSG_LEN(sizeof(*pki)); cmh->cmsg_level = SOL_IP; cmh->cmsg_type = IP_PKTINFO; pki->ipi_ifindex = 0; pki->ipi_spec_dst.s_addr = rqstp->rq_daddr; if (sock_sendmsg(sock, &msg, 0) < 0) goto out; } /* send head */ if (slen == xdr->head[0].iov_len) flags = 0; len = sock->ops->sendpage(sock, rqstp->rq_respages[0], 0, xdr->head[0].iov_len, flags); if (len != xdr->head[0].iov_len) goto out; slen -= xdr->head[0].iov_len; if (slen == 0) goto out; /* send page data */ size = PAGE_SIZE - base < pglen ? PAGE_SIZE - base : pglen; while (pglen > 0) { if (slen == size) flags = 0; result = sock->ops->sendpage(sock, *ppage, base, size, flags); if (result > 0) len += result; if (result != size) goto out; slen -= size; pglen -= size; size = PAGE_SIZE < pglen ? PAGE_SIZE : pglen; base = 0; ppage++; } /* send tail */ if (xdr->tail[0].iov_len) { result = sock->ops->sendpage(sock, rqstp->rq_respages[rqstp->rq_restailpage], ((unsigned long)xdr->tail[0].iov_base)& (PAGE_SIZE-1), xdr->tail[0].iov_len, 0); if (result > 0) len += result; }out: dprintk("svc: socket %p sendto([%p %Zu... ], %d) = %d (addr %x)\n", rqstp->rq_sock, xdr->head[0].iov_base, xdr->head[0].iov_len, xdr->len, len, rqstp->rq_addr.sin_addr.s_addr); return len;}/* * Check input queue length */static intsvc_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 intsvc_recvfrom(struct svc_rqst *rqstp, struct kvec *iov, int nr, int buflen){ struct msghdr msg; struct socket *sock; int len, alen; 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_control = NULL; msg.msg_controllen = 0; msg.msg_flags = MSG_DONTWAIT; len = kernel_recvmsg(sock, &msg, iov, nr, buflen, MSG_DONTWAIT); /* sock_recvmsg doesn't fill in the name/namelen, so we must.. * possibly we should cache this in the svc_sock structure * at accept time. FIXME */ alen = sizeof(rqstp->rq_addr); sock->ops->getname(sock, (struct sockaddr *)&rqstp->rq_addr, &alen, 1); dprintk("svc: socket %p recvfrom(%p, %Zu) = %d\n", rqstp->rq_sock, iov[0].iov_base, iov[0].iov_len, len); return len;}/* * Set socket snd and rcv buffer lengths */static inline voidsvc_sock_setbufsize(struct socket *sock, unsigned int snd, unsigned int rcv){#if 0 mm_segment_t oldfs; oldfs = get_fs(); set_fs(KERNEL_DS); sock_setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&snd, sizeof(snd)); sock_setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&rcv, sizeof(rcv));#else /* sock_setsockopt limits use to sysctl_?mem_max, * which isn't acceptable. Until that is made conditional * on not having CAP_SYS_RESOURCE or similar, we go direct... * DaveM said I could! */ lock_sock(sock->sk); sock->sk->sk_sndbuf = snd * 2; sock->sk->sk_rcvbuf = rcv * 2; sock->sk->sk_userlocks |= SOCK_SNDBUF_LOCK|SOCK_RCVBUF_LOCK; release_sock(sock->sk);#endif}/* * INET callback when data has been received on the socket. */static voidsvc_udp_data_ready(struct sock *sk, int count){ struct svc_sock *svsk = (struct svc_sock *)(sk->sk_user_data); if (!svsk) goto out; dprintk("svc: socket %p(inet %p), count=%d, busy=%d\n", svsk, sk, count, test_bit(SK_BUSY, &svsk->sk_flags)); set_bit(SK_DATA, &svsk->sk_flags); svc_sock_enqueue(svsk); out: if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) wake_up_interruptible(sk->sk_sleep);}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -