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 | /* * SUCS NET3: * * Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top * of these would make sense. Not tonight however 8-). * This is used because UDP, RAW, PACKET and the to be released IPX layer all have identical select code and mostly * identical recvfrom() code. So we share it here. The select was shared before but buried in udp.c so I moved it. * * Authors: Alan Cox <iiitac@pyr.swan.ac.uk>. (datagram_select() from old udp.c code) * * Fixes: * Alan Cox : NULL return from skb_peek_copy() understood * Alan Cox : Rewrote skb_read_datagram to avoid the skb_peek_copy stuff. * Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but * AX.25 now works right, and SPX is feasible. * Alan Cox : Fixed write select of non IP protocol crash. * Florian La Roche: Changed for my new skbuff handling. * * Note: * A lot of this will change when the protocol/socket seperation * occurs. Using this will make things reasonably clean. */ #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> #include <asm/segment.h> #include <asm/system.h> #include <linux/mm.h> #include <linux/interrupt.h> #include <linux/in.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/inet.h> #include <linux/netdevice.h> #include "ip.h" #include "protocol.h" #include "route.h" #include "tcp.h" #include "udp.h" #include <linux/skbuff.h> #include "sock.h" /* * Get a datagram skbuff, understands the peeking, nonblocking wakeups and possible * races. This replaces identical code in packet,raw and udp, as well as the yet to * be released IPX support. It also finally fixes the long standing peek and read * race for datagram sockets. If you alter this routine remember it must be * re-entrant. */ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err) { struct sk_buff *skb; /* Socket is inuse - so the timer doesn't attack it */ restart: sk->inuse = 1; while(skb_peek(&sk->receive_queue) == NULL) /* No data */ { /* If we are shutdown then no more data is going to appear. We are done */ if (sk->shutdown & RCV_SHUTDOWN) { release_sock(sk); *err=0; return NULL; } if(sk->err) { release_sock(sk); *err=-sk->err; sk->err=0; return NULL; } /* Sequenced packets can come disconnected. If so we report the problem */ if(sk->type==SOCK_SEQPACKET && sk->state!=TCP_ESTABLISHED) { release_sock(sk); *err=-ENOTCONN; return NULL; } /* User doesn't want to wait */ if (noblock) { release_sock(sk); *err=-EAGAIN; return NULL; } release_sock(sk); /* Interrupts off so that no packet arrives before we begin sleeping. Otherwise we might miss our wake up */ cli(); if (skb_peek(&sk->receive_queue) == NULL) { interruptible_sleep_on(sk->sleep); /* Signals may need a restart of the syscall */ if (current->signal & ~current->blocked) { sti(); *err=-ERESTARTSYS; return(NULL); } if(sk->err != 0) /* Error while waiting for packet eg an icmp sent earlier by the peer has finaly turned up now */ { *err = -sk->err; sti(); sk->err=0; return NULL; } } sk->inuse = 1; sti(); } /* Again only user level code calls this function, so nothing interrupt level will suddenely eat the receive_queue */ if (!(flags & MSG_PEEK)) { skb=skb_dequeue(&sk->receive_queue); if(skb!=NULL) skb->users++; else goto restart; /* Avoid race if someone beats us to the data */ } else { cli(); skb=skb_peek(&sk->receive_queue); if(skb!=NULL) skb->users++; sti(); if(skb==NULL) /* shouldn't happen but .. */ *err=-EAGAIN; } return skb; } void skb_free_datagram(struct sk_buff *skb) { unsigned long flags; save_flags(flags); cli(); skb->users--; if(skb->users>0) { restore_flags(flags); return; } /* See if it needs destroying */ if(!skb->next && !skb->prev) /* Been dequeued by someone - ie its read */ kfree_skb(skb,FREE_READ); restore_flags(flags); } void skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size) { /* We will know all about the fraglist options to allow >4K receives but not this release */ memcpy_tofs(to,skb->h.raw+offset,size); } /* * Datagram select: Again totally generic. Moved from udp.c * Now does seqpacket. */ int datagram_select(struct sock *sk, int sel_type, select_table *wait) { select_wait(sk->sleep, wait); switch(sel_type) { case SEL_IN: if (sk->type==SOCK_SEQPACKET && sk->state==TCP_CLOSE) { /* Connection closed: Wake up */ return(1); } if (skb_peek(&sk->receive_queue) != NULL || sk->err != 0) { /* This appears to be consistent with other stacks */ return(1); } return(0); case SEL_OUT: if (sk->prot && sk->prot->wspace(sk) >= MIN_WRITE_SPACE) { return(1); } if (sk->prot==NULL && sk->sndbuf-sk->wmem_alloc >= MIN_WRITE_SPACE) { return(1); } return(0); case SEL_EX: if (sk->err) return(1); /* Socket has gone into error state (eg icmp error) */ return(0); } return(0); } |