X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/c64d826cdc61c33bb0f79bf3c96f2f285541671a..5b519e941140a2be657d68865fade9f5aa577b79:/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 e2aef07871..e4b215a985 100644 --- a/usr/src/sys/kern/uipc_socket2.c +++ b/usr/src/sys/kern/uipc_socket2.c @@ -1,19 +1,29 @@ -/* uipc_socket2.c 4.17 81/12/22 */ - -#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/in.h" -#include "../net/in_systm.h" +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of California at Berkeley. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + * + * @(#)uipc_socket2.c 7.2 (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,8 +31,9 @@ /* * Procedures to manipulate state flags of socket - * and do appropriate wakeups. Normal sequence is that - * soisconnecting() is called during processing of connect() call, + * 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, @@ -32,13 +43,24 @@ * only, bypassing the in-progress calls when setting up a ``connection'' * takes no time. * - * When higher level protocols are implemented in + * 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 - * be implemented as software-interrupt process scheduling. + * cause software-interrupt process scheduling. */ soisconnecting(so) - struct socket *so; + register struct socket *so; { so->so_state &= ~(SS_ISCONNECTED|SS_ISDISCONNECTING); @@ -47,16 +69,26 @@ 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_ISCONNECTING; @@ -67,7 +99,7 @@ soisdisconnecting(so) } soisdisconnected(so) - struct socket *so; + register struct socket *so; { so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING); @@ -77,6 +109,90 @@ 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 @@ -107,28 +223,6 @@ socantrcvmore(so) * Socket select/wakeup routines. */ -/* - * Interface routine to select() system - * call for sockets. - */ -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. */ @@ -158,7 +252,7 @@ sbwait(sb) * Wakeup processes waiting on a socket buffer. */ sbwakeup(sb) - struct sockbuf *sb; + register struct sockbuf *sb; { if (sb->sb_sel) { @@ -172,6 +266,26 @@ 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. * @@ -181,46 +295,58 @@ sbwakeup(sb) * 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 + * 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. The space should be released by calling sbrelease() when the * socket is destroyed. - * - * The routine sbappend() is normally called to append new mbufs - * to a socket buffer, after checking that adequate space is available - * comparing the function spspace() with the amount of data to be added. - * Data is normally removed from a socket buffer in a protocol by - * first calling m_copy on the socket buffer mbuf chain and sending this - * to a peer, and then removing the data from the socket buffer with - * sbdrop when the data is acknowledged by the peer (or immediately - * in the case of unreliable protocols.) - * - * Protocols which do not require connections place both source address - * and data information in socket buffer queues. The source addresses - * are stored in single mbufs after each data item, and are easily found - * as the data items are all marked with end of record markers. The - * sbappendaddr() routine stores a datum and associated address in - * a socket buffer. Note that, unlike sbappend(), this routine checks - * for the caller that there will be enough space to store the data. - * It fails if there is not enough space, or if it cannot find - * a mbuf to store the address in. - * - * The higher-level routines sosend and soreceive (in socket.c) - * also add data to, and remove data from socket buffers repectively. */ +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*2)/MSIZE) == 0) + if ((unsigned) cc > (unsigned)SB_MAX * CLBYTES / (2 * MSIZE + CLBYTES)) return (0); sb->sb_hiwat = cc; - sb->sb_mbmax = cc*2; + sb->sb_mbmax = MIN(cc * 2, SB_MAX); return (1); } @@ -232,38 +358,179 @@ sbrelease(sb) { sbflush(sb); - m_release(sb->sb_mbmax/MSIZE); 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) - register struct mbuf *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 **np, *n; + 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); +} - np = &sb->sb_mb; - n = 0; - while (*np) { - n = *np; - np = &n->m_next; +/* + * 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 *m, *n; + int space = sizeof (*asa); + + 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 && (int)m->m_act == 0) { + 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_len) <= MMAXOFF) { + (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; @@ -272,81 +539,51 @@ 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; - } -} - -/* - * Append data and address. - * Return 0 if no space in sockbuf or if - * can't get mbuf to stuff address in. - */ -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); - - m = m0; - if (m == 0) - panic("sbappendaddr"); - for (;;) { - len += m->m_len; - if (m->m_next == 0) { - m->m_act = (struct mbuf *)1; - break; - } m = m->m_next; + n->m_next = 0; } - 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); - 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"); - if (sb->sb_cc) - 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 == 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; @@ -358,20 +595,33 @@ sbdrop(sb, len) MFREE(m, mn); m = mn; } - sb->sb_mb = m; + 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; } /* -printm(m) - struct mbuf *m; + * Drop a record off the front of a sockbuf + * and move the next record to the front. + */ +sbdroprecord(sb) + register struct sockbuf *sb; { - - printf("<"); - while (m) { - printf("%d,", m->m_len); - m = m->m_next; + 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); } - printf(">"); - printf("\n"); } -*/