/net/decnet/dn_nsp_out.c
C | 720 lines | 450 code | 140 blank | 130 comment | 85 complexity | 1c7e438216416c4449c6818afc41c74b MD5 | raw file
Possible License(s): CC-BY-SA-3.0, GPL-2.0, LGPL-2.0, AGPL-1.0
- /*
- * DECnet An implementation of the DECnet protocol suite for the LINUX
- * operating system. DECnet is implemented using the BSD Socket
- * interface as the means of communication with the user level.
- *
- * DECnet Network Services Protocol (Output)
- *
- * Author: Eduardo Marcelo Serrat <emserrat@geocities.com>
- *
- * Changes:
- *
- * Steve Whitehouse: Split into dn_nsp_in.c and dn_nsp_out.c from
- * original dn_nsp.c.
- * Steve Whitehouse: Updated to work with my new routing architecture.
- * Steve Whitehouse: Added changes from Eduardo Serrat's patches.
- * Steve Whitehouse: Now conninits have the "return" bit set.
- * Steve Whitehouse: Fixes to check alloc'd skbs are non NULL!
- * Moved output state machine into one function
- * Steve Whitehouse: New output state machine
- * Paul Koning: Connect Confirm message fix.
- * Eduardo Serrat: Fix to stop dn_nsp_do_disc() sending malformed packets.
- * Steve Whitehouse: dn_nsp_output() and friends needed a spring clean
- * Steve Whitehouse: Moved dn_nsp_send() in here from route.h
- */
- /******************************************************************************
- (c) 1995-1998 E.M. Serrat emserrat@geocities.com
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- *******************************************************************************/
- #include <linux/errno.h>
- #include <linux/types.h>
- #include <linux/socket.h>
- #include <linux/in.h>
- #include <linux/kernel.h>
- #include <linux/timer.h>
- #include <linux/string.h>
- #include <linux/sockios.h>
- #include <linux/net.h>
- #include <linux/netdevice.h>
- #include <linux/inet.h>
- #include <linux/route.h>
- #include <linux/slab.h>
- #include <net/sock.h>
- #include <asm/system.h>
- #include <linux/fcntl.h>
- #include <linux/mm.h>
- #include <linux/termios.h>
- #include <linux/interrupt.h>
- #include <linux/proc_fs.h>
- #include <linux/stat.h>
- #include <linux/init.h>
- #include <linux/poll.h>
- #include <linux/if_packet.h>
- #include <net/neighbour.h>
- #include <net/dst.h>
- #include <net/flow.h>
- #include <net/dn.h>
- #include <net/dn_nsp.h>
- #include <net/dn_dev.h>
- #include <net/dn_route.h>
- static int nsp_backoff[NSP_MAXRXTSHIFT + 1] = { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
- static void dn_nsp_send(struct sk_buff *skb)
- {
- struct sock *sk = skb->sk;
- struct dn_scp *scp = DN_SK(sk);
- struct dst_entry *dst;
- struct flowi fl;
- skb_reset_transport_header(skb);
- scp->stamp = jiffies;
- dst = sk_dst_check(sk, 0);
- if (dst) {
- try_again:
- skb_dst_set(skb, dst);
- dst_output(skb);
- return;
- }
- memset(&fl, 0, sizeof(fl));
- fl.oif = sk->sk_bound_dev_if;
- fl.fld_src = dn_saddr2dn(&scp->addr);
- fl.fld_dst = dn_saddr2dn(&scp->peer);
- dn_sk_ports_copy(&fl, scp);
- fl.proto = DNPROTO_NSP;
- if (dn_route_output_sock(&sk->sk_dst_cache, &fl, sk, 0) == 0) {
- dst = sk_dst_get(sk);
- sk->sk_route_caps = dst->dev->features;
- goto try_again;
- }
- sk->sk_err = EHOSTUNREACH;
- if (!sock_flag(sk, SOCK_DEAD))
- sk->sk_state_change(sk);
- }
- /*
- * If sk == NULL, then we assume that we are supposed to be making
- * a routing layer skb. If sk != NULL, then we are supposed to be
- * creating an skb for the NSP layer.
- *
- * The eventual aim is for each socket to have a cached header size
- * for its outgoing packets, and to set hdr from this when sk != NULL.
- */
- struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri)
- {
- struct sk_buff *skb;
- int hdr = 64;
- if ((skb = alloc_skb(size + hdr, pri)) == NULL)
- return NULL;
- skb->protocol = htons(ETH_P_DNA_RT);
- skb->pkt_type = PACKET_OUTGOING;
- if (sk)
- skb_set_owner_w(skb, sk);
- skb_reserve(skb, hdr);
- return skb;
- }
- /*
- * Calculate persist timer based upon the smoothed round
- * trip time and the variance. Backoff according to the
- * nsp_backoff[] array.
- */
- unsigned long dn_nsp_persist(struct sock *sk)
- {
- struct dn_scp *scp = DN_SK(sk);
- unsigned long t = ((scp->nsp_srtt >> 2) + scp->nsp_rttvar) >> 1;
- t *= nsp_backoff[scp->nsp_rxtshift];
- if (t < HZ) t = HZ;
- if (t > (600*HZ)) t = (600*HZ);
- if (scp->nsp_rxtshift < NSP_MAXRXTSHIFT)
- scp->nsp_rxtshift++;
- /* printk(KERN_DEBUG "rxtshift %lu, t=%lu\n", scp->nsp_rxtshift, t); */
- return t;
- }
- /*
- * This is called each time we get an estimate for the rtt
- * on the link.
- */
- static void dn_nsp_rtt(struct sock *sk, long rtt)
- {
- struct dn_scp *scp = DN_SK(sk);
- long srtt = (long)scp->nsp_srtt;
- long rttvar = (long)scp->nsp_rttvar;
- long delta;
- /*
- * If the jiffies clock flips over in the middle of timestamp
- * gathering this value might turn out negative, so we make sure
- * that is it always positive here.
- */
- if (rtt < 0)
- rtt = -rtt;
- /*
- * Add new rtt to smoothed average
- */
- delta = ((rtt << 3) - srtt);
- srtt += (delta >> 3);
- if (srtt >= 1)
- scp->nsp_srtt = (unsigned long)srtt;
- else
- scp->nsp_srtt = 1;
- /*
- * Add new rtt varience to smoothed varience
- */
- delta >>= 1;
- rttvar += ((((delta>0)?(delta):(-delta)) - rttvar) >> 2);
- if (rttvar >= 1)
- scp->nsp_rttvar = (unsigned long)rttvar;
- else
- scp->nsp_rttvar = 1;
- /* printk(KERN_DEBUG "srtt=%lu rttvar=%lu\n", scp->nsp_srtt, scp->nsp_rttvar); */
- }
- /**
- * dn_nsp_clone_and_send - Send a data packet by cloning it
- * @skb: The packet to clone and transmit
- * @gfp: memory allocation flag
- *
- * Clone a queued data or other data packet and transmit it.
- *
- * Returns: The number of times the packet has been sent previously
- */
- static inline unsigned dn_nsp_clone_and_send(struct sk_buff *skb,
- gfp_t gfp)
- {
- struct dn_skb_cb *cb = DN_SKB_CB(skb);
- struct sk_buff *skb2;
- int ret = 0;
- if ((skb2 = skb_clone(skb, gfp)) != NULL) {
- ret = cb->xmit_count;
- cb->xmit_count++;
- cb->stamp = jiffies;
- skb2->sk = skb->sk;
- dn_nsp_send(skb2);
- }
- return ret;
- }
- /**
- * dn_nsp_output - Try and send something from socket queues
- * @sk: The socket whose queues are to be investigated
- *
- * Try and send the packet on the end of the data and other data queues.
- * Other data gets priority over data, and if we retransmit a packet we
- * reduce the window by dividing it in two.
- *
- */
- void dn_nsp_output(struct sock *sk)
- {
- struct dn_scp *scp = DN_SK(sk);
- struct sk_buff *skb;
- unsigned reduce_win = 0;
- /*
- * First we check for otherdata/linkservice messages