Commit fce6cff4 authored by Kai Germaschewski's avatar Kai Germaschewski

ISDN: Move generic xmit/recv handling into isdn_net_lib.c

Last step in separating generic / interface-type specific code,
the former is now all in isdn_net_lib.c, the specific code in
isdn_net.c / isdn_ppp.c / isdn_concap.c
parent 83fd84ce
...@@ -33,402 +33,8 @@ ...@@ -33,402 +33,8 @@
#include "isdn_concap.h" #include "isdn_concap.h"
#include "isdn_ciscohdlck.h" #include "isdn_ciscohdlck.h"
#define ISDN_NET_MAX_QUEUE_LENGTH 2
/*
* is this particular channel busy?
*/
static inline int
isdn_net_dev_busy(isdn_net_dev *idev)
{
return idev->frame_cnt >= ISDN_NET_MAX_QUEUE_LENGTH;
}
/*
* find out if the net_device which this mlp is belongs to is busy.
* It's busy iff all channels are busy.
* must hold mlp->xmit_lock
* FIXME: Use a mlp->frame_cnt instead of loop?
*/
static inline int
isdn_net_local_busy(isdn_net_local *mlp)
{
isdn_net_dev *idev;
list_for_each_entry(idev, &mlp->online, online) {
if (!isdn_net_dev_busy(idev))
return 0;
}
return 1;
}
/*
* For the given net device, this will get a non-busy channel out of the
* corresponding bundle.
*/
static inline isdn_net_dev *
isdn_net_get_xmit_dev(isdn_net_local *mlp)
{
isdn_net_dev *idev;
list_for_each_entry(idev, &mlp->online, online) {
if (!isdn_net_dev_busy(idev)) {
/* point the head to next online channel */
list_del(&mlp->online);
list_add(&mlp->online, &idev->online);
return idev;
}
}
return NULL;
}
/* mlp->xmit_lock must be held */
static inline void
isdn_net_inc_frame_cnt(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
if (isdn_net_local_busy(mlp))
isdn_BUG();
idev->frame_cnt++;
if (isdn_net_local_busy(mlp))
netif_stop_queue(&mlp->dev);
}
/* mlp->xmit_lock must be held */
static inline void
isdn_net_dec_frame_cnt(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
int was_busy;
was_busy = isdn_net_local_busy(mlp);
idev->frame_cnt--;
if (isdn_net_local_busy(mlp))
isdn_BUG();
if (!was_busy)
return;
if (!skb_queue_empty(&idev->super_tx_queue))
tasklet_schedule(&idev->tlet);
else
netif_wake_queue(&mlp->dev);
}
/* Prototypes */
int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg);
char *isdn_net_revision = "$Revision: 1.140.6.11 $"; char *isdn_net_revision = "$Revision: 1.140.6.11 $";
/* A packet has successfully been sent out. */
int
isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c)
{
isdn_net_local *mlp = idev->mlp;
unsigned long flags;
spin_lock_irqsave(&mlp->xmit_lock, flags);
isdn_net_dec_frame_cnt(idev);
spin_unlock_irqrestore(&mlp->xmit_lock, flags);
mlp->stats.tx_packets++;
mlp->stats.tx_bytes += c->parm.length;
return 1;
}
static void
isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
{
u_short proto = ntohs(skb->protocol);
printk(KERN_DEBUG "isdn_net: %s: %s, signalling dst_link_failure %s\n",
dev->name,
(reason != NULL) ? reason : "unknown",
(proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : "");
dst_link_failure(skb);
}
static void
isdn_net_log_skb(struct sk_buff *skb, isdn_net_dev *idev)
{
unsigned char *p = skb->nh.raw; /* hopefully, this was set correctly */
unsigned short proto = ntohs(skb->protocol);
int data_ofs;
struct ip_ports {
unsigned short source;
unsigned short dest;
} *ipp;
char addinfo[100];
data_ofs = ((p[0] & 15) * 4);
switch (proto) {
case ETH_P_IP:
switch (p[9]) {
case IPPROTO_ICMP:
strcpy(addinfo, "ICMP");
break;
case IPPROTO_TCP:
case IPPROTO_UDP:
ipp = (struct ip_ports *) (&p[data_ofs]);
sprintf(addinfo, "%s, port: %d -> %d",
p[9] == IPPROTO_TCP ? "TCP" : "UDP",
ntohs(ipp->source), ntohs(ipp->dest));
break;
default:
sprintf(addinfo, "type %d", p[9]);
}
printk(KERN_INFO
"OPEN: %u.%u.%u.%u -> %u.%u.%u.%u %s\n",
NIPQUAD(*(u32 *)(p + 12)), NIPQUAD(*(u32 *)(p + 16)),
addinfo);
break;
case ETH_P_ARP:
printk(KERN_INFO
"OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
NIPQUAD(*(u32 *)(p + 14)), NIPQUAD(*(u32 *)(p + 24)));
break;
default:
printk(KERN_INFO "OPEN: unknown proto %#x\n", proto);
}
}
/*
* this function is used to send supervisory data, i.e. data which was
* not received from the network layer, but e.g. frames from ipppd, CCP
* reset frames etc.
* must hold mlp->xmit_lock
*/
void
isdn_net_write_super(isdn_net_dev *idev, struct sk_buff *skb)
{
if (!isdn_net_dev_busy(idev))
isdn_net_writebuf_skb(idev, skb);
else
skb_queue_tail(&idev->super_tx_queue, skb);
}
/*
* all frames sent from the (net) LL to a HL driver should go via this function
* it's serialized by the caller holding the idev->xmit_lock spinlock
* must hold mlp->xmit_lock
*/
void
isdn_net_writebuf_skb(isdn_net_dev *idev, struct sk_buff *skb)
{
isdn_net_local *mlp = idev->mlp;
int ret;
int len = skb->len; /* save len */
/* before obtaining the lock the caller should have checked that
the lp isn't busy */
if (isdn_net_dev_busy(idev)) {
isdn_BUG();
goto error;
}
if (!isdn_net_online(idev)) {
isdn_BUG();
goto error;
}
ret = isdn_slot_write(idev->isdn_slot, skb);
if (ret != len) {
/* we should never get here */
printk(KERN_WARNING "%s: HL driver queue full\n", idev->name);
goto error;
}
idev->transcount += len;
isdn_net_inc_frame_cnt(idev);
return;
error:
dev_kfree_skb(skb);
mlp->stats.tx_errors++;
}
static void
isdn_net_dial_slave(isdn_net_local *mlp)
{
isdn_net_dev *idev;
list_for_each_entry(idev, &mlp->slaves, slaves) {
if (!isdn_net_bound(idev)) {
isdn_net_dial(idev);
break;
}
}
}
/*
* Based on cps-calculation, check if device is overloaded.
* If so, and if a slave exists, trigger dialing for it.
* If any slave is online, deliver packets using a simple round robin
* scheme.
*
* Return: 0 on success, !0 on failure.
*/
int
isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
isdn_net_dev *idev;
isdn_net_local *mlp = ndev->priv;
unsigned long flags;
int retval;
ndev->trans_start = jiffies;
spin_lock_irqsave(&mlp->xmit_lock, flags);
if (list_empty(&mlp->online)) {
retval = isdn_net_autodial(skb, ndev);
goto out;
}
idev = isdn_net_get_xmit_dev(mlp);
if (!idev) {
printk(KERN_INFO "%s: all channels busy - requeuing!\n", ndev->name);
netif_stop_queue(ndev);
retval = 1;
goto out;
}
isdn_net_writebuf_skb(idev, skb);
/* the following stuff is here for backwards compatibility.
* in future, start-up and hangup of slaves (based on current load)
* should move to userspace and get based on an overall cps
* calculation
*/
if (jiffies != idev->last_jiffies) {
idev->cps = idev->transcount * HZ / (jiffies - idev->last_jiffies);
idev->last_jiffies = jiffies;
idev->transcount = 0;
}
if (dev->net_verbose > 3)
printk(KERN_DEBUG "%s: %d bogocps\n", idev->name, idev->cps);
if (idev->cps > mlp->triggercps) {
if (!idev->sqfull) {
/* First time overload: set timestamp only */
idev->sqfull = 1;
idev->sqfull_stamp = jiffies;
} else {
/* subsequent overload: if slavedelay exceeded, start dialing */
if (time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay)) {
isdn_net_dial_slave(mlp);
}
}
} else {
if (idev->sqfull && time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay + 10 * HZ)) {
idev->sqfull = 0;
}
/* this is a hack to allow auto-hangup for slaves on moderate loads */
list_del(&mlp->online);
list_add_tail(&mlp->online, &idev->online);
}
retval = 0;
out:
spin_unlock_irqrestore(&mlp->xmit_lock, flags);
return retval;
}
int
isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev)
{
isdn_net_local *mlp = ndev->priv;
isdn_net_dev *idev = list_entry(mlp->slaves.next, isdn_net_dev, slaves);
/* are we dialing already? */
if (isdn_net_bound(idev))
goto stop_queue;
if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
goto discard;
if (isdn_net_dial(idev) < 0)
goto discard;
/* Log packet, which triggered dialing */
if (dev->net_verbose)
isdn_net_log_skb(skb, idev);
stop_queue:
netif_stop_queue(ndev);
return 1;
discard:
isdn_net_unreachable(ndev, skb, "dial rejected");
dev_kfree_skb(skb);
return 0;
}
/*
* Got a packet from ISDN-Channel.
*/
static void
isdn_net_receive(isdn_net_dev *idev, struct sk_buff *skb)
{
isdn_net_local *mlp = idev->mlp;
idev->transcount += skb->len;
mlp->stats.rx_packets++;
mlp->stats.rx_bytes += skb->len;
skb->dev = &mlp->dev;
skb->pkt_type = PACKET_HOST;
skb->mac.raw = skb->data;
isdn_dumppkt("R:", skb->data, skb->len, 40);
mlp->ops->receive(mlp, idev, skb);
}
/*
* A packet arrived via ISDN. Search interface-chain for a corresponding
* interface. If found, deliver packet to receiver-function and return 1,
* else return 0.
*/
int
isdn_net_rcv_skb(int idx, struct sk_buff *skb)
{
isdn_net_dev *idev = isdn_slot_idev(idx);
if (!idev) {
HERE;
return 0;
}
if (!isdn_net_online(idev))
return 0;
isdn_net_receive(idev, skb);
return 0;
}
/*
* This is called from certain upper protocol layers (multilink ppp
* and x25iface encapsulation module) that want to initiate dialing
* themselves.
*/
int
isdn_net_dial_req(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
/* is there a better error code? */
if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
return -EBUSY;
return isdn_net_dial(idev);
}
// ISDN_NET_ENCAP_IPTYP // ISDN_NET_ENCAP_IPTYP
// ethernet type field // ethernet type field
// ====================================================================== // ======================================================================
...@@ -571,6 +177,8 @@ static struct isdn_netif_ops ether_ops = { ...@@ -571,6 +177,8 @@ static struct isdn_netif_ops ether_ops = {
void void
isdn_net_init(void) isdn_net_init(void)
{ {
isdn_net_lib_init();
register_isdn_netif(ISDN_NET_ENCAP_ETHER, &ether_ops); register_isdn_netif(ISDN_NET_ENCAP_ETHER, &ether_ops);
register_isdn_netif(ISDN_NET_ENCAP_RAWIP, &rawip_ops); register_isdn_netif(ISDN_NET_ENCAP_RAWIP, &rawip_ops);
register_isdn_netif(ISDN_NET_ENCAP_IPTYP, &iptyp_ops); register_isdn_netif(ISDN_NET_ENCAP_IPTYP, &iptyp_ops);
...@@ -583,7 +191,11 @@ isdn_net_init(void) ...@@ -583,7 +191,11 @@ isdn_net_init(void)
#ifdef CONFIG_ISDN_PPP #ifdef CONFIG_ISDN_PPP
register_isdn_netif(ISDN_NET_ENCAP_SYNCPPP, &isdn_ppp_ops); register_isdn_netif(ISDN_NET_ENCAP_SYNCPPP, &isdn_ppp_ops);
#endif #endif
}
isdn_net_lib_init(); void
isdn_net_exit(void)
{
isdn_net_lib_exit();
} }
...@@ -32,29 +32,26 @@ ...@@ -32,29 +32,26 @@
#define CISCO_SLARP_REPLY 1 #define CISCO_SLARP_REPLY 1
#define CISCO_SLARP_KEEPALIVE 2 #define CISCO_SLARP_KEEPALIVE 2
extern void isdn_net_init(void); void isdn_net_init(void);
extern void isdn_net_exit(void); void isdn_net_exit(void);
extern void isdn_net_lib_init(void); void isdn_net_lib_init(void);
extern void isdn_net_lib_exit(void); void isdn_net_lib_exit(void);
extern void isdn_net_hangup_all(void); void isdn_net_hangup_all(void);
extern int isdn_net_ioctl(struct inode *, struct file *, uint, ulong); int isdn_net_ioctl(struct inode *, struct file *, uint, ulong);
extern int register_isdn_netif(int encap, struct isdn_netif_ops *ops); int register_isdn_netif(int encap, struct isdn_netif_ops *ops);
extern int isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev); int isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev);
extern int isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev); void isdn_net_online(isdn_net_dev *idev);
void isdn_net_offline(isdn_net_dev *idev);
extern int isdn_net_dial(isdn_net_dev *idev);
int isdn_net_stat_callback(int, isdn_ctrl *);
extern int isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c); int isdn_net_find_icall(int, int, int, setup_parm *);
int isdn_net_rcv_skb(int, struct sk_buff *);
extern int isdn_net_stat_callback(int, isdn_ctrl *);
extern int isdn_net_find_icall(int, int, int, setup_parm *); int isdn_net_hangup(isdn_net_dev *);
extern int isdn_net_hangup(isdn_net_dev *); int isdn_net_dial_req(isdn_net_dev *);
extern int isdn_net_rcv_skb(int, struct sk_buff *); void isdn_net_writebuf_skb(isdn_net_dev *, struct sk_buff *skb);
extern int isdn_net_dial_req(isdn_net_dev *); void isdn_net_write_super(isdn_net_dev *, struct sk_buff *skb);
extern void isdn_net_writebuf_skb(isdn_net_dev *, struct sk_buff *skb);
extern void isdn_net_write_super(isdn_net_dev *, struct sk_buff *skb);
extern int isdn_net_online(isdn_net_dev *);
enum { enum {
ST_CHARGE_NULL, ST_CHARGE_NULL,
......
...@@ -80,12 +80,13 @@ lp_put(isdn_net_local *lp) ...@@ -80,12 +80,13 @@ lp_put(isdn_net_local *lp)
isdn_BUG(); isdn_BUG();
} }
int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg); /* FIXME */ static int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg);
static void isdn_net_tasklet(unsigned long data); static void isdn_net_tasklet(unsigned long data);
static void isdn_net_dial_timer(unsigned long data); static void isdn_net_dial_timer(unsigned long data);
static int isdn_init_netif(struct net_device *ndev); static int isdn_init_netif(struct net_device *ndev);
static void isdn_net_dev_debug(struct fsm_inst *fi, char *fmt, ...); static void isdn_net_dev_debug(struct fsm_inst *fi, char *fmt, ...);
static int isdn_net_dial(isdn_net_dev *idev);
static int isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c);
static struct fsm isdn_net_fsm; static struct fsm isdn_net_fsm;
...@@ -1007,7 +1008,7 @@ isdn_net_hangup_all(void) ...@@ -1007,7 +1008,7 @@ isdn_net_hangup_all(void)
* Remove all network-interfaces * Remove all network-interfaces
*/ */
void void
isdn_net_exit(void) isdn_net_cleanup(void)
{ {
isdn_net_dev *idev; isdn_net_dev *idev;
int retval; int retval;
...@@ -1025,9 +1026,6 @@ isdn_net_exit(void) ...@@ -1025,9 +1026,6 @@ isdn_net_exit(void)
isdn_BUG(); isdn_BUG();
} }
up(&sem); up(&sem);
// FIXME
isdn_net_lib_exit();
} }
/* ====================================================================== */ /* ====================================================================== */
...@@ -1146,30 +1144,13 @@ isdn_init_netif(struct net_device *ndev) ...@@ -1146,30 +1144,13 @@ isdn_init_netif(struct net_device *ndev)
return 0; return 0;
} }
/* ====================================================================== */
static void
isdn_net_tasklet(unsigned long data)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
isdn_net_local *mlp = idev->mlp;
struct sk_buff *skb;
unsigned long flags;
spin_lock_irqsave(&mlp->xmit_lock, flags);
while (!isdn_net_dev_busy(idev) &&
(skb = skb_dequeue(&idev->super_tx_queue))) {
isdn_net_writebuf_skb(idev, skb);
}
spin_unlock_irqrestore(&mlp->xmit_lock, flags);
}
/* ====================================================================== */ /* ====================================================================== */
/* call control state machine */ /* call control state machine */
/* ====================================================================== */ /* ====================================================================== */
// FIXME // FIXME
int isdn_net_online(isdn_net_dev *idev) static int
isdn_net_is_connected(isdn_net_dev *idev)
{ {
return idev->fi.state == ST_ACTIVE; return idev->fi.state == ST_ACTIVE;
} }
...@@ -1234,7 +1215,7 @@ isdn_net_bind_channel(isdn_net_dev *idev, int slot) ...@@ -1234,7 +1215,7 @@ isdn_net_bind_channel(isdn_net_dev *idev, int slot)
return retval; return retval;
} }
int static int
isdn_net_dial(isdn_net_dev *idev) isdn_net_dial(isdn_net_dev *idev)
{ {
int retval; int retval;
...@@ -1250,6 +1231,127 @@ isdn_net_dial(isdn_net_dev *idev) ...@@ -1250,6 +1231,127 @@ isdn_net_dial(isdn_net_dev *idev)
return retval; return retval;
} }
static void
isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
{
u_short proto = ntohs(skb->protocol);
printk(KERN_DEBUG "isdn_net: %s: %s, signalling dst_link_failure %s\n",
dev->name,
(reason != NULL) ? reason : "unknown",
(proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : "");
dst_link_failure(skb);
}
/*
* This is called from certain upper protocol layers (multilink ppp
* and x25iface encapsulation module) that want to initiate dialing
* themselves.
*/
int
isdn_net_dial_req(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
/* is there a better error code? */
if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
return -EBUSY;
return isdn_net_dial(idev);
}
static void
isdn_net_log_skb(struct sk_buff *skb, isdn_net_dev *idev)
{
unsigned char *p = skb->nh.raw; /* hopefully, this was set correctly */
unsigned short proto = ntohs(skb->protocol);
int data_ofs;
struct ip_ports {
unsigned short source;
unsigned short dest;
} *ipp;
char addinfo[100];
data_ofs = ((p[0] & 15) * 4);
switch (proto) {
case ETH_P_IP:
switch (p[9]) {
case IPPROTO_ICMP:
strcpy(addinfo, "ICMP");
break;
case IPPROTO_TCP:
case IPPROTO_UDP:
ipp = (struct ip_ports *) (&p[data_ofs]);
sprintf(addinfo, "%s, port: %d -> %d",
p[9] == IPPROTO_TCP ? "TCP" : "UDP",
ntohs(ipp->source), ntohs(ipp->dest));
break;
default:
sprintf(addinfo, "type %d", p[9]);
}
printk(KERN_INFO
"OPEN: %u.%u.%u.%u -> %u.%u.%u.%u %s\n",
NIPQUAD(*(u32 *)(p + 12)), NIPQUAD(*(u32 *)(p + 16)),
addinfo);
break;
case ETH_P_ARP:
printk(KERN_INFO
"OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
NIPQUAD(*(u32 *)(p + 14)), NIPQUAD(*(u32 *)(p + 24)));
break;
default:
printk(KERN_INFO "OPEN: unknown proto %#x\n", proto);
}
}
int
isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev)
{
isdn_net_local *mlp = ndev->priv;
isdn_net_dev *idev = list_entry(mlp->slaves.next, isdn_net_dev, slaves);
int retval;
if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
goto discard;
retval = isdn_net_dial(idev);
if (retval == -ESRCH)
goto stop_queue;
if (retval < 0)
goto discard;
/* Log packet, which triggered dialing */
if (dev->net_verbose)
isdn_net_log_skb(skb, idev);
stop_queue:
netif_stop_queue(ndev);
return 1;
discard:
isdn_net_unreachable(ndev, skb, "dial rejected");
dev_kfree_skb(skb);
return 0;
}
static void
isdn_net_dial_slave(isdn_net_local *mlp)
{
isdn_net_dev *idev;
if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
return;
list_for_each_entry(idev, &mlp->slaves, slaves) {
if (fsm_event(&idev->fi, EV_DO_DIAL, NULL) != -ESRCH) {
break;
}
}
}
static int static int
accept_icall(struct fsm_inst *fi, int pr, void *arg) accept_icall(struct fsm_inst *fi, int pr, void *arg)
{ {
...@@ -1629,7 +1731,6 @@ bconn(struct fsm_inst *fi, int pr, void *arg) ...@@ -1629,7 +1731,6 @@ bconn(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata; isdn_net_dev *idev = fi->userdata;
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
unsigned long flags;
fsm_change_state(&idev->fi, ST_ACTIVE); fsm_change_state(&idev->fi, ST_ACTIVE);
...@@ -1641,10 +1742,6 @@ bconn(struct fsm_inst *fi, int pr, void *arg) ...@@ -1641,10 +1742,6 @@ bconn(struct fsm_inst *fi, int pr, void *arg)
del_timer(&idev->dial_timer); del_timer(&idev->dial_timer);
} }
spin_lock_irqsave(&mlp->xmit_lock, flags);
list_add(&idev->online, &mlp->online);
spin_unlock_irqrestore(&mlp->xmit_lock, flags);
printk(KERN_INFO "%s connected\n", idev->name); printk(KERN_INFO "%s connected\n", idev->name);
/* If first Chargeinfo comes before B-Channel connect, /* If first Chargeinfo comes before B-Channel connect,
* we correct the timestamp here. * we correct the timestamp here.
...@@ -1658,7 +1755,7 @@ bconn(struct fsm_inst *fi, int pr, void *arg) ...@@ -1658,7 +1755,7 @@ bconn(struct fsm_inst *fi, int pr, void *arg)
if (mlp->ops->connected) if (mlp->ops->connected)
mlp->ops->connected(idev); mlp->ops->connected(idev);
else else
netif_wake_queue(&idev->mlp->dev); isdn_net_online(idev);
return 0; return 0;
} }
...@@ -1668,15 +1765,12 @@ bhup(struct fsm_inst *fi, int pr, void *arg) ...@@ -1668,15 +1765,12 @@ bhup(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata; isdn_net_dev *idev = fi->userdata;
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
unsigned long flags;
del_timer(&idev->dial_timer); del_timer(&idev->dial_timer);
if (mlp->ops->disconnected) if (mlp->ops->disconnected)
mlp->ops->disconnected(idev); mlp->ops->disconnected(idev);
else
spin_lock_irqsave(&mlp->xmit_lock, flags); isdn_net_offline(idev);
list_del(&idev->online);
spin_unlock_irqrestore(&mlp->xmit_lock, flags);
printk(KERN_INFO "%s: disconnected\n", idev->name); printk(KERN_INFO "%s: disconnected\n", idev->name);
fsm_change_state(fi, ST_WAIT_DHUP); fsm_change_state(fi, ST_WAIT_DHUP);
...@@ -1797,7 +1891,7 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c) ...@@ -1797,7 +1891,7 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c)
} }
} }
int static int
isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg) isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg)
{ {
fsm_event(&idev->fi, pr, arg); fsm_event(&idev->fi, pr, arg);
...@@ -1879,6 +1973,329 @@ static void isdn_net_dev_debug(struct fsm_inst *fi, char *fmt, ...) ...@@ -1879,6 +1973,329 @@ static void isdn_net_dev_debug(struct fsm_inst *fi, char *fmt, ...)
printk(KERN_DEBUG "%s\n", buf); printk(KERN_DEBUG "%s\n", buf);
} }
/* ====================================================================== */
/* xmit path */
/* ====================================================================== */
#define ISDN_NET_MAX_QUEUE_LENGTH 2
/*
* is this particular channel busy?
*/
static inline int
isdn_net_dev_busy(isdn_net_dev *idev)
{
return idev->frame_cnt >= ISDN_NET_MAX_QUEUE_LENGTH;
}
/*
* find out if the net_device which this mlp is belongs to is busy.
* It's busy iff all channels are busy.
* must hold mlp->xmit_lock
* FIXME: Use a mlp->frame_cnt instead of loop?
*/
static inline int
isdn_net_local_busy(isdn_net_local *mlp)
{
isdn_net_dev *idev;
list_for_each_entry(idev, &mlp->online, online) {
if (!isdn_net_dev_busy(idev))
return 0;
}
return 1;
}
/*
* For the given net device, this will get a non-busy channel out of the
* corresponding bundle.
*/
static inline isdn_net_dev *
isdn_net_get_xmit_dev(isdn_net_local *mlp)
{
isdn_net_dev *idev;
list_for_each_entry(idev, &mlp->online, online) {
if (!isdn_net_dev_busy(idev)) {
/* point the head to next online channel */
list_del(&mlp->online);
list_add(&mlp->online, &idev->online);
return idev;
}
}
return NULL;
}
/* mlp->xmit_lock must be held */
static inline void
isdn_net_inc_frame_cnt(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
if (isdn_net_local_busy(mlp))
isdn_BUG();
idev->frame_cnt++;
if (isdn_net_local_busy(mlp))
netif_stop_queue(&mlp->dev);
}
/* mlp->xmit_lock must be held */
static inline void
isdn_net_dec_frame_cnt(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
int was_busy;
was_busy = isdn_net_local_busy(mlp);
idev->frame_cnt--;
if (isdn_net_local_busy(mlp))
isdn_BUG();
if (!was_busy)
return;
if (!skb_queue_empty(&idev->super_tx_queue))
tasklet_schedule(&idev->tlet);
else
netif_wake_queue(&mlp->dev);
}
static void
isdn_net_tasklet(unsigned long data)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
isdn_net_local *mlp = idev->mlp;
struct sk_buff *skb;
unsigned long flags;
spin_lock_irqsave(&mlp->xmit_lock, flags);
while (!isdn_net_dev_busy(idev) &&
(skb = skb_dequeue(&idev->super_tx_queue))) {
isdn_net_writebuf_skb(idev, skb);
}
spin_unlock_irqrestore(&mlp->xmit_lock, flags);
}
/* We're good to accept (IP/whatever) traffic now */
void
isdn_net_online(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
unsigned long flags;
spin_lock_irqsave(&mlp->xmit_lock, flags);
list_add(&idev->online, &mlp->online);
spin_unlock_irqrestore(&mlp->xmit_lock, flags);
netif_wake_queue(&mlp->dev);
}
/* No more (IP/whatever) traffic over the net interface */
void
isdn_net_offline(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
unsigned long flags;
spin_lock_irqsave(&mlp->xmit_lock, flags);
list_del(&idev->online);
spin_unlock_irqrestore(&mlp->xmit_lock, flags);
skb_queue_purge(&idev->super_tx_queue);
}
/*
* all frames sent from the (net) LL to a HL driver should go via this function
* must hold mlp->xmit_lock
*/
void
isdn_net_writebuf_skb(isdn_net_dev *idev, struct sk_buff *skb)
{
isdn_net_local *mlp = idev->mlp;
int ret;
int len = skb->len; /* save len */
/* before obtaining the lock the caller should have checked that
the lp isn't busy */
if (isdn_net_dev_busy(idev)) {
isdn_BUG();
goto error;
}
if (!isdn_net_is_connected(idev)) {
isdn_BUG();
goto error;
}
ret = isdn_slot_write(idev->isdn_slot, skb);
if (ret != len) {
/* we should never get here */
printk(KERN_WARNING "%s: HL driver queue full\n", idev->name);
goto error;
}
idev->transcount += len;
isdn_net_inc_frame_cnt(idev);
return;
error:
dev_kfree_skb(skb);
mlp->stats.tx_errors++;
}
/* A packet has successfully been sent out. */
static int
isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c)
{
isdn_net_local *mlp = idev->mlp;
unsigned long flags;
spin_lock_irqsave(&mlp->xmit_lock, flags);
isdn_net_dec_frame_cnt(idev);
spin_unlock_irqrestore(&mlp->xmit_lock, flags);
mlp->stats.tx_packets++;
mlp->stats.tx_bytes += c->parm.length;
return 1;
}
/*
* Based on cps-calculation, check if device is overloaded.
* If so, and if a slave exists, trigger dialing for it.
* If any slave is online, deliver packets using a simple round robin
* scheme.
*
* Return: 0 on success, !0 on failure.
*/
int
isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
isdn_net_dev *idev;
isdn_net_local *mlp = ndev->priv;
unsigned long flags;
int retval;
ndev->trans_start = jiffies;
spin_lock_irqsave(&mlp->xmit_lock, flags);
if (list_empty(&mlp->online)) {
retval = isdn_net_autodial(skb, ndev);
goto out;
}
idev = isdn_net_get_xmit_dev(mlp);
if (!idev) {
printk(KERN_INFO "%s: all channels busy - requeuing!\n", ndev->name);
netif_stop_queue(ndev);
retval = 1;
goto out;
}
isdn_net_writebuf_skb(idev, skb);
/* the following stuff is here for backwards compatibility.
* in future, start-up and hangup of slaves (based on current load)
* should move to userspace and get based on an overall cps
* calculation
*/
if (jiffies != idev->last_jiffies) {
idev->cps = idev->transcount * HZ / (jiffies - idev->last_jiffies);
idev->last_jiffies = jiffies;
idev->transcount = 0;
}
if (dev->net_verbose > 3)
printk(KERN_DEBUG "%s: %d bogocps\n", idev->name, idev->cps);
if (idev->cps > mlp->triggercps) {
if (!idev->sqfull) {
/* First time overload: set timestamp only */
idev->sqfull = 1;
idev->sqfull_stamp = jiffies;
} else {
/* subsequent overload: if slavedelay exceeded, start dialing */
if (time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay)) {
isdn_net_dial_slave(mlp);
}
}
} else {
if (idev->sqfull && time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay + 10 * HZ)) {
idev->sqfull = 0;
}
/* this is a hack to allow auto-hangup for slaves on moderate loads */
list_del(&mlp->online);
list_add_tail(&mlp->online, &idev->online);
}
retval = 0;
out:
spin_unlock_irqrestore(&mlp->xmit_lock, flags);
return retval;
}
/*
* this function is used to send supervisory data, i.e. data which was
* not received from the network layer, but e.g. frames from ipppd, CCP
* reset frames etc.
* must hold mlp->xmit_lock
*/
void
isdn_net_write_super(isdn_net_dev *idev, struct sk_buff *skb)
{
if (!isdn_net_dev_busy(idev))
isdn_net_writebuf_skb(idev, skb);
else
skb_queue_tail(&idev->super_tx_queue, skb);
}
/* ====================================================================== */
/* receive path */
/* ====================================================================== */
/*
* A packet arrived via ISDN. Search interface-chain for a corresponding
* interface. If found, deliver packet to receiver-function and return 1,
* else return 0.
*/
int
isdn_net_rcv_skb(int idx, struct sk_buff *skb)
{
isdn_net_dev *idev = isdn_slot_idev(idx);
isdn_net_local *mlp;
if (!idev) {
isdn_BUG();
return 0;
}
if (!isdn_net_is_connected(idev)) {
isdn_BUG();
return 0;
}
mlp = idev->mlp;
idev->transcount += skb->len;
mlp->stats.rx_packets++;
mlp->stats.rx_bytes += skb->len;
skb->dev = &mlp->dev;
skb->pkt_type = PACKET_HOST;
isdn_dumppkt("R:", skb->data, skb->len, 40);
mlp->ops->receive(mlp, idev, skb);
return 1;
}
/* ====================================================================== */
/* init / exit */
/* ====================================================================== */
void void
isdn_net_lib_init(void) isdn_net_lib_init(void)
{ {
...@@ -1888,5 +2305,7 @@ isdn_net_lib_init(void) ...@@ -1888,5 +2305,7 @@ isdn_net_lib_init(void)
void void
isdn_net_lib_exit(void) isdn_net_lib_exit(void)
{ {
isdn_net_cleanup();
fsm_free(&isdn_net_fsm); fsm_free(&isdn_net_fsm);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment