X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/388ca8bdac53637b23536f8f4129bf95680aed7e..5c723b90ceb41c125105d2a818e372ee31f2e522:/usr/src/sys/kern/uipc_socket2.c diff --git a/usr/src/sys/kern/uipc_socket2.c b/usr/src/sys/kern/uipc_socket2.c index 3a839988a4..5cffe304c4 100644 --- a/usr/src/sys/kern/uipc_socket2.c +++ b/usr/src/sys/kern/uipc_socket2.c @@ -1,19 +1,23 @@ -/* uipc_socket2.c 4.5 81/11/21 */ - -#include "../h/param.h" -#include "../h/systm.h" -#include "../h/dir.h" -#include "../h/user.h" -#include "../h/proc.h" -#include "../h/file.h" -#include "../h/inode.h" -#include "../h/buf.h" -#include "../h/mbuf.h" -#include "../h/protosw.h" -#include "../h/socket.h" -#include "../h/socketvar.h" -#include "../net/inet.h" -#include "../net/inet_systm.h" +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + * + * @(#)uipc_socket2.c 7.1 (Berkeley) %G% + */ + +#include "param.h" +#include "systm.h" +#include "dir.h" +#include "user.h" +#include "proc.h" +#include "file.h" +#include "inode.h" +#include "buf.h" +#include "mbuf.h" +#include "protosw.h" +#include "socket.h" +#include "socketvar.h" /* * Primitive routines for operating on sockets and socket buffers @@ -21,10 +25,36 @@ /* * Procedures to manipulate state flags of socket - * and do appropriate wakeups. + * and do appropriate wakeups. Normal sequence from the + * active (originating) side is that soisconnecting() is + * called during processing of connect() call, + * resulting in an eventual call to soisconnected() if/when the + * connection is established. When the connection is torn down + * soisdisconnecting() is called during processing of disconnect() call, + * and soisdisconnected() is called when the connection to the peer + * is totally severed. The semantics of these routines are such that + * connectionless protocols can call soisconnected() and soisdisconnected() + * only, bypassing the in-progress calls when setting up a ``connection'' + * takes no time. + * + * From the passive side, a socket is created with + * two queues of sockets: so_q0 for connections in progress + * and so_q for connections already made and awaiting user acceptance. + * As a protocol is preparing incoming connections, it creates a socket + * structure queued on so_q0 by calling sonewconn(). When the connection + * is established, soisconnected() is called, and transfers the + * socket structure to so_q, making it available to accept(). + * + * If a socket is closed with sockets on either + * so_q0 or so_q, these sockets are dropped. + * + * If higher level protocols are implemented in + * the kernel, the wakeups done here will sometimes + * cause software-interrupt process scheduling. */ + soisconnecting(so) - struct socket *so; + register struct socket *so; { so->so_state &= ~(SS_ISCONNECTED|SS_ISDISCONNECTING); @@ -33,25 +63,37 @@ soisconnecting(so) } soisconnected(so) - struct socket *so; + register struct socket *so; { - + register struct socket *head = so->so_head; + + if (head) { + if (soqremque(so, 0) == 0) + panic("soisconnected"); + soqinsque(head, so, 1); + sorwakeup(head); + wakeup((caddr_t)&head->so_timeo); + } so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING); so->so_state |= SS_ISCONNECTED; wakeup((caddr_t)&so->so_timeo); + sorwakeup(so); + sowwakeup(so); } soisdisconnecting(so) - struct socket *so; + register struct socket *so; { - so->so_state &= ~(SS_ISCONNECTED|SS_ISCONNECTING); + so->so_state &= ~SS_ISCONNECTING; so->so_state |= (SS_ISDISCONNECTING|SS_CANTRCVMORE|SS_CANTSENDMORE); wakeup((caddr_t)&so->so_timeo); + sowwakeup(so); + sorwakeup(so); } soisdisconnected(so) - struct socket *so; + register struct socket *so; { so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING); @@ -61,6 +103,100 @@ soisdisconnected(so) sorwakeup(so); } +/* + * When an attempt at a new connection is noted on a socket + * which accepts connections, sonewconn is called. If the + * connection is possible (subject to space constraints, etc.) + * then we allocate a new structure, propoerly linked into the + * data structure of the original socket, and return this. + */ +struct socket * +sonewconn(head) + register struct socket *head; +{ + register struct socket *so; + register struct mbuf *m; + + if (head->so_qlen + head->so_q0len > 3 * head->so_qlimit / 2) + goto bad; + m = m_getclr(M_DONTWAIT, MT_SOCKET); + if (m == NULL) + goto bad; + so = mtod(m, struct socket *); + so->so_type = head->so_type; + so->so_options = head->so_options &~ SO_ACCEPTCONN; + so->so_linger = head->so_linger; + so->so_state = head->so_state | SS_NOFDREF; + so->so_proto = head->so_proto; + so->so_timeo = head->so_timeo; + so->so_pgrp = head->so_pgrp; + soqinsque(head, so, 0); + if ((*so->so_proto->pr_usrreq)(so, PRU_ATTACH, + (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)) { + (void) soqremque(so, 0); + (void) m_free(m); + goto bad; + } + return (so); +bad: + return ((struct socket *)0); +} + +soqinsque(head, so, q) + register struct socket *head, *so; + int q; +{ + + so->so_head = head; + if (q == 0) { + head->so_q0len++; + so->so_q0 = head->so_q0; + head->so_q0 = so; + } else { + head->so_qlen++; + so->so_q = head->so_q; + head->so_q = so; + } +} + +soqremque(so, q) + register struct socket *so; + int q; +{ + register struct socket *head, *prev, *next; + + head = so->so_head; + prev = head; + for (;;) { + next = q ? prev->so_q : prev->so_q0; + if (next == so) + break; + if (next == head) + return (0); + prev = next; + } + if (q == 0) { + prev->so_q0 = next->so_q0; + head->so_q0len--; + } else { + prev->so_q = next->so_q; + head->so_qlen--; + } + next->so_q0 = next->so_q = 0; + next->so_head = 0; + return (1); +} + +/* + * Socantsendmore indicates that no more data will be sent on the + * socket; it would normally be applied to a socket when the user + * informs the system that no more data is to be sent, by the protocol + * code (in case PRU_SHUTDOWN). Socantrcvmore indicates that no more data + * will be received, and will normally be applied to the socket by a + * protocol when it detects that the peer will send no more data. + * Data queued for reading in the socket may yet be read. + */ + socantsendmore(so) struct socket *so; { @@ -78,25 +214,8 @@ socantrcvmore(so) } /* - * Select a socket. + * Socket select/wakeup routines. */ -soselect(so, flag) - register struct socket *so; - int flag; -{ - - if (flag & FREAD) { - if (soreadable(so)) - return (1); - sbselqueue(&so->so_rcv); - } - if (flag & FWRITE) { - if (sowriteable(so)) - return (1); - sbselqueue(&so->so_snd); - } - return (0); -} /* * Queue a process for a select on a socket buffer. @@ -127,7 +246,7 @@ sbwait(sb) * Wakeup processes waiting on a socket buffer. */ sbwakeup(sb) - struct sockbuf *sb; + register struct sockbuf *sb; { if (sb->sb_sel) { @@ -141,17 +260,87 @@ sbwakeup(sb) } } +/* + * Wakeup socket readers and writers. + * Do asynchronous notification via SIGIO + * if the socket has the SS_ASYNC flag set. + */ +sowakeup(so, sb) + register struct socket *so; + struct sockbuf *sb; +{ + register struct proc *p; + + sbwakeup(sb); + if (so->so_state & SS_ASYNC) { + if (so->so_pgrp < 0) + gsignal(-so->so_pgrp, SIGIO); + else if (so->so_pgrp > 0 && (p = pfind(so->so_pgrp)) != 0) + psignal(p, SIGIO); + } +} + +/* + * Socket buffer (struct sockbuf) utility routines. + * + * Each socket contains two socket buffers: one for sending data and + * one for receiving data. Each buffer contains a queue of mbufs, + * information about the number of mbufs and amount of data in the + * queue, and other fields allowing select() statements and notification + * on data availability to be implemented. + * + * Data stored in a socket buffer is maintained as a list of records. + * Each record is a list of mbufs chained together with the m_next + * field. Records are chained together with the m_act field. The upper + * level routine soreceive() expects the following conventions to be + * observed when placing information in the receive buffer: + * + * 1. If the protocol requires each message be preceded by the sender's + * name, then a record containing that name must be present before + * any associated data (mbuf's must be of type MT_SONAME). + * 2. If the protocol supports the exchange of ``access rights'' (really + * just additional data associated with the message), and there are + * ``rights'' to be received, then a record containing this data + * should be present (mbuf's must be of type MT_RIGHTS). + * 3. If a name or rights record exists, then it must be followed by + * a data record, perhaps of zero length. + * + * Before using a new socket structure it is first necessary to reserve + * buffer space to the socket, by calling sbreserve(). This commits + * some of the available buffer space in the system buffer pool for the + * socket. The space should be released by calling sbrelease() when the + * socket is destroyed. + */ + +soreserve(so, sndcc, rcvcc) + register struct socket *so; + int sndcc, rcvcc; +{ + + if (sbreserve(&so->so_snd, sndcc) == 0) + goto bad; + if (sbreserve(&so->so_rcv, rcvcc) == 0) + goto bad2; + return (0); +bad2: + sbrelease(&so->so_snd); +bad: + return (ENOBUFS); +} + /* * Allot mbufs to a sockbuf. + * Attempt to scale cc so that mbcnt doesn't become limiting + * if buffering efficiency is near the normal case. */ sbreserve(sb, cc) struct sockbuf *sb; { - if (m_reserve(cc) == 0) + if ((unsigned) cc > (unsigned)SB_MAX * CLBYTES / (2 * MSIZE + CLBYTES)) return (0); sb->sb_hiwat = cc; - sb->sb_mbmax = (cc*2)/MSIZE; + sb->sb_mbmax = MIN(cc * 2, SB_MAX); return (1); } @@ -163,35 +352,180 @@ sbrelease(sb) { sbflush(sb); - m_release(sb->sb_hiwat); sb->sb_hiwat = sb->sb_mbmax = 0; } /* - * Routines to add (at the end) and remove (from the beginning) - * data from a mbuf queue. + * Routines to add and remove + * data from an mbuf queue. + * + * The routines sbappend() or sbappendrecord() are normally called to + * append new mbufs to a socket buffer, after checking that adequate + * space is available, comparing the function sbspace() with the amount + * of data to be added. sbappendrecord() differs from sbappend() in + * that data supplied is treated as the beginning of a new record. + * To place a sender's address, optional access rights, and data in a + * socket receive buffer, sbappendaddr() should be used. To place + * access rights and data in a socket receive buffer, sbappendrights() + * should be used. In either case, the new data begins a new record. + * Note that unlike sbappend() and sbappendrecord(), these routines check + * for the caller that there will be enough space to store the data. + * Each fails if there is not enough space, or if it cannot find mbufs + * to store additional information in. + * + * Reliable protocols may use the socket send buffer to hold data + * awaiting acknowledgement. Data is normally copied from a socket + * send buffer in a protocol with m_copy for output to a peer, + * and then removing the data from the socket buffer with sbdrop() + * or sbdroprecord() when the data is acknowledged by the peer. */ /* - * Append mbuf queue m to sockbuf sb. + * Append mbuf chain m to the last record in the + * socket buffer sb. The additional space associated + * the mbuf chain is recorded in sb. Empty mbufs are + * discarded and mbufs are compacted where possible. */ sbappend(sb, m) + struct sockbuf *sb; + struct mbuf *m; +{ + register struct mbuf *n; + + if (m == 0) + return; + if (n = sb->sb_mb) { + while (n->m_act) + n = n->m_act; + while (n->m_next) + n = n->m_next; + } + sbcompress(sb, m, n); +} + +/* + * As above, except the mbuf chain + * begins a new record. + */ +sbappendrecord(sb, m0) + register struct sockbuf *sb; + register struct mbuf *m0; +{ register struct mbuf *m; + + if (m0 == 0) + return; + if (m = sb->sb_mb) + while (m->m_act) + m = m->m_act; + /* + * Put the first mbuf on the queue. + * Note this permits zero length records. + */ + sballoc(sb, m0); + if (m) + m->m_act = m0; + else + sb->sb_mb = m0; + m = m0->m_next; + m0->m_next = 0; + sbcompress(sb, m, m0); +} + +/* + * Append address and data, and optionally, rights + * to the receive queue of a socket. Return 0 if + * no space in sockbuf or insufficient mbufs. + */ +sbappendaddr(sb, asa, m0, rights0) register struct sockbuf *sb; + struct sockaddr *asa; + struct mbuf *rights0, *m0; { - register struct mbuf **np, *n; + register struct mbuf *m, *n; + int space = sizeof (*asa); - np = &sb->sb_mb; - n = 0; - while (*np) { - n = *np; - np = &n->m_next; + for (m = m0; m; m = m->m_next) + space += m->m_len; + if (rights0) + space += rights0->m_len; + if (space > sbspace(sb)) + return (0); + MGET(m, M_DONTWAIT, MT_SONAME); + if (m == 0) + return (0); + *mtod(m, struct sockaddr *) = *asa; + m->m_len = sizeof (*asa); + if (rights0 && rights0->m_len) { + m->m_next = m_copy(rights0, 0, rights0->m_len); + if (m->m_next == 0) { + m_freem(m); + return (0); + } + sballoc(sb, m->m_next); } + sballoc(sb, m); + if (n = sb->sb_mb) { + while (n->m_act) + n = n->m_act; + n->m_act = m; + } else + sb->sb_mb = m; + if (m->m_next) + m = m->m_next; + if (m0) + sbcompress(sb, m0, m); + return (1); +} + +sbappendrights(sb, m0, rights) + struct sockbuf *sb; + struct mbuf *rights, *m0; +{ + register struct mbuf *m, *n; + int space = 0; + + if (rights == 0) + panic("sbappendrights"); + for (m = m0; m; m = m->m_next) + space += m->m_len; + space += rights->m_len; + if (space > sbspace(sb)) + return (0); + m = m_copy(rights, 0, rights->m_len); + if (m == 0) + return (0); + sballoc(sb, m); + if (n = sb->sb_mb) { + while (n->m_act) + n = n->m_act; + n->m_act = m; + } else + sb->sb_mb = m; + if (m0) + sbcompress(sb, m0, m); + return (1); +} + +/* + * Compress mbuf chain m into the socket + * buffer sb following mbuf n. If n + * is null, the buffer is presumed empty. + */ +sbcompress(sb, m, n) + register struct sockbuf *sb; + register struct mbuf *m, *n; +{ + while (m) { + if (m->m_len == 0) { + m = m_free(m); + continue; + } if (n && n->m_off <= MMAXOFF && m->m_off <= MMAXOFF && - (int)n->m_act == 0 && (int)m->m_act == 0 && - (n->m_off + n->m_len + m->m_off) <= MMAXOFF) { - bcopy(mtod(m, caddr_t), mtod(n, caddr_t), + (n->m_off + n->m_len + m->m_len) <= MMAXOFF && + n->m_type == m->m_type) { + bcopy(mtod(m, caddr_t), mtod(n, caddr_t) + n->m_len, (unsigned)m->m_len); n->m_len += m->m_len; sb->sb_cc += m->m_len; @@ -199,78 +533,89 @@ sbappend(sb, m) continue; } sballoc(sb, m); - *np = m; + if (n) + n->m_next = m; + else + sb->sb_mb = m; n = m; - np = &n->m_next; m = m->m_next; + n->m_next = 0; } } -sbappendaddr(sb, asa, m0) - struct sockbuf *sb; - struct sockaddr *asa; - struct mbuf *m0; -{ - struct sockaddr *msa; - register struct mbuf *m; - register int len = sizeof (struct sockaddr); - - for (m = m0; m; m = m->m_next) - len += m->m_len; - if (len > sbspace(sb)) - return (0); - m = m_get(0); - if (m == 0) - return (0); - m->m_off = MMINOFF; - m->m_len = sizeof (struct sockaddr); - msa = mtod(m, struct sockaddr *); - *msa = *asa; - m->m_act = (struct mbuf *)1; - sbappend(sb, m); - m0->m_act = (struct mbuf *)1; - sbappend(sb, m0); - return (1); -} - /* - * Free all mbufs on a sockbuf mbuf chain. - * Check that resource allocations return to 0. + * Free all mbufs in a sockbuf. + * Check that all resources are reclaimed. */ sbflush(sb) - struct sockbuf *sb; + register struct sockbuf *sb; { if (sb->sb_flags & SB_LOCK) panic("sbflush"); - sbdrop(sb, sb->sb_cc); + while (sb->sb_mbcnt) + sbdrop(sb, (int)sb->sb_cc); if (sb->sb_cc || sb->sb_mbcnt || sb->sb_mb) panic("sbflush 2"); } /* - * Drop data from (the front of) a sockbuf chain. + * Drop data from (the front of) a sockbuf. */ sbdrop(sb, len) register struct sockbuf *sb; register int len; { - register struct mbuf *m = sb->sb_mb, *mn; + register struct mbuf *m, *mn; + struct mbuf *next; + next = (m = sb->sb_mb) ? m->m_act : 0; while (len > 0) { - if (m == 0) - panic("sbdrop"); - if (m->m_len <= len) { - len -= m->m_len; - sbfree(sb, m); - MFREE(m, mn); - m = mn; - } else { + if (m == 0) { + if (next == 0) + panic("sbdrop"); + m = next; + next = m->m_act; + continue; + } + if (m->m_len > len) { m->m_len -= len; m->m_off += len; sb->sb_cc -= len; break; } + len -= m->m_len; + sbfree(sb, m); + MFREE(m, mn); + m = mn; + } + while (m && m->m_len == 0) { + sbfree(sb, m); + MFREE(m, mn); + m = mn; + } + if (m) { + sb->sb_mb = m; + m->m_act = next; + } else + sb->sb_mb = next; +} + +/* + * Drop a record off the front of a sockbuf + * and move the next record to the front. + */ +sbdroprecord(sb) + register struct sockbuf *sb; +{ + register struct mbuf *m, *mn; + + m = sb->sb_mb; + if (m) { + sb->sb_mb = m->m_act; + do { + sbfree(sb, m); + MFREE(m, mn); + } while (m = mn); } - sb->sb_mb = m; }