> >> Worse, even if we encode the channel into the skb somehow,
> >> flow control is a problem. Flow control works the obvious way,
> >> i.e. netif_stop_queue() if all channels are busy and
> >> netif_wake_queue() if at least one channel becomes non-busy. So
> >> now hard_start_xmit() might give us the control frame for a
> >> specific channel, which could still be busy though, because we
> >> just know that (any) one channel is non-busy.
> Alert!!! Try to avoid this. This system has _no_ support for multiple
> hardware queues. Essentially, you have two choices: to send control
> frames
> internally without dev_queue_xmit() (it is easy), or to move channel
> demultiplexing to special qdisc (f.e. as sch_atm does). It is still not
> clear how support for multiple hardware queues can be organized. I am
> inclined to believe that it is simply not well-defined task. Probably,
> it
> is better to try to move channel multiplexing out of hardware level.
Okay, for the time being I went for the former solution, i.e. I do send
control frames internally.
BTW: Sorry for the late reply, this list's lag is incredible.
Just one more question, about backporting: spinlock_bh doesn't exist in
2.2 AFAICS, I guess I need to use spinlock_irqsave?
I'd be very happy if someone could comment on the locking/procedure
I used - thanks!
isdn_net_writebuf_skb(channel, skb)
{
/* we are guaranteed that this channel is not (yet) busy */
/* channel is always protected by channel->xmit_lock spinlock */
write skb to channel;
atomic_inc (&channel->frame_cnt);
if (all channels in this bundle busy) {
/* a channel is busy if channel->frame_cnt >= 2) */
netif_stop_queue();
}
}
isdn_net_stat_callback(channel)
/* this is called from hardware channel when frame has actually been sent
down the line */
/* this may be called in hard-irq context, so we use the task queue
for sending (don't want to call isdn_net_writebuf_skb() in irq
context) */
{
atomic_dec(&channel->frame_cnt);
if (!(channel_busy(channel))) {
if (!skb_queue_empty(&channel->super_tx_queue)) {
/* if there is supervisory data waiting,
send it first */
queue_task(&lchannel->tqueue, &tq_immediate);
} else {
netif_wake_queue();
}
}
}
where the task on tq_immediate would do the following:
{
spin_lock_bh(&channel->xmit_lock);
while (!isdn_net_lp_busy(channel)) {
skb = skb_dequeue(&channel->super_tx_queue);
if (!skb)
break;
isdn_net_writebuf_skb(channel, skb);
}
spin_unlock_bh(&channel->xmit_lock);
}
isdn_net_hard_start_xmit(net_device, skb)
{
for all channels in bundle(net_device) {
spin_lock_bh(&channel->xmit_lock)
if (!(channel_busy(channel))
break;
spin_unlock_bh(&channel->xmit_lock)
}
if (!channel) /* no non-busy channel found */
return 1;
isdn_net_writebuf_skb(channel, skb)
spin_unlock_bh(&channel->xmit_lock);
return 0;
}
isdn_net_write_super(channel, skb)
/* this can be called from task queue / process / timer */
/* we send the skb directly if possible, if not we queue it
on channel->super_tx_queue */
{
spin_lock_bh(&channel->xmit_lock); }
if (!channel_busy(channel)) {
isdn_net_writebuf_skb(channel, skb);
} else {
skb_queue_tail(&channel->super_tx_queue, skb);
}
spin_unlock_bh(&channel->xmit_lock);
}
|