Commit 6572b206 authored by Linus Torvalds's avatar Linus Torvalds

Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6

* master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6:
  [NET_SCHED]: HFSC: fix thinko in hfsc_adjust_levels()
  [IPV6]: skb leakage in inet6_csk_xmit
  [BRIDGE]: Do sysfs registration inside rtnl.
  [NET]: Do sysfs registration as part of register_netdevice.
  [TG3]: Fix possible NULL deref in tg3_run_loopback().
  [NET] linkwatch: Handle jiffies wrap-around
  [IRDA]: Switching to a workqueue for the SIR work
  [IRDA]: smsc-ircc: Minimal hotplug support.
  [IRDA]: Removing unused EXPORT_SYMBOLs
  [IRDA]: New maintainer.
  [NET]: Make netdev_chain a raw notifier.
  [IPV4]: ip_options_fragment() has no effect on fragmentation
  [NET]: Add missing operstates documentation.
parents f7a014af 210525d6
1. Introduction
Linux distinguishes between administrative and operational state of an
interface. Admininstrative state is the result of "ip link set dev
<dev> up or down" and reflects whether the administrator wants to use
the device for traffic.
However, an interface is not usable just because the admin enabled it
- ethernet requires to be plugged into the switch and, depending on
a site's networking policy and configuration, an 802.1X authentication
to be performed before user data can be transferred. Operational state
shows the ability of an interface to transmit this user data.
Thanks to 802.1X, userspace must be granted the possibility to
influence operational state. To accommodate this, operational state is
split into two parts: Two flags that can be set by the driver only, and
a RFC2863 compatible state that is derived from these flags, a policy,
and changeable from userspace under certain rules.
2. Querying from userspace
Both admin and operational state can be queried via the netlink
operation RTM_GETLINK. It is also possible to subscribe to RTMGRP_LINK
to be notified of updates. This is important for setting from userspace.
These values contain interface state:
ifinfomsg::if_flags & IFF_UP:
Interface is admin up
ifinfomsg::if_flags & IFF_RUNNING:
Interface is in RFC2863 operational state UP or UNKNOWN. This is for
backward compatibility, routing daemons, dhcp clients can use this
flag to determine whether they should use the interface.
ifinfomsg::if_flags & IFF_LOWER_UP:
Driver has signaled netif_carrier_on()
ifinfomsg::if_flags & IFF_DORMANT:
Driver has signaled netif_dormant_on()
These interface flags can also be queried without netlink using the
SIOCGIFFLAGS ioctl.
TLV IFLA_OPERSTATE
contains RFC2863 state of the interface in numeric representation:
IF_OPER_UNKNOWN (0):
Interface is in unknown state, neither driver nor userspace has set
operational state. Interface must be considered for user data as
setting operational state has not been implemented in every driver.
IF_OPER_NOTPRESENT (1):
Unused in current kernel (notpresent interfaces normally disappear),
just a numerical placeholder.
IF_OPER_DOWN (2):
Interface is unable to transfer data on L1, f.e. ethernet is not
plugged or interface is ADMIN down.
IF_OPER_LOWERLAYERDOWN (3):
Interfaces stacked on an interface that is IF_OPER_DOWN show this
state (f.e. VLAN).
IF_OPER_TESTING (4):
Unused in current kernel.
IF_OPER_DORMANT (5):
Interface is L1 up, but waiting for an external event, f.e. for a
protocol to establish. (802.1X)
IF_OPER_UP (6):
Interface is operational up and can be used.
This TLV can also be queried via sysfs.
TLV IFLA_LINKMODE
contains link policy. This is needed for userspace interaction
described below.
This TLV can also be queried via sysfs.
3. Kernel driver API
Kernel drivers have access to two flags that map to IFF_LOWER_UP and
IFF_DORMANT. These flags can be set from everywhere, even from
interrupts. It is guaranteed that only the driver has write access,
however, if different layers of the driver manipulate the same flag,
the driver has to provide the synchronisation needed.
__LINK_STATE_NOCARRIER, maps to !IFF_LOWER_UP:
The driver uses netif_carrier_on() to clear and netif_carrier_off() to
set this flag. On netif_carrier_off(), the scheduler stops sending
packets. The name 'carrier' and the inversion are historical, think of
it as lower layer.
netif_carrier_ok() can be used to query that bit.
__LINK_STATE_DORMANT, maps to IFF_DORMANT:
Set by the driver to express that the device cannot yet be used
because some driver controlled protocol establishment has to
complete. Corresponding functions are netif_dormant_on() to set the
flag, netif_dormant_off() to clear it and netif_dormant() to query.
On device allocation, networking core sets the flags equivalent to
netif_carrier_ok() and !netif_dormant().
Whenever the driver CHANGES one of these flags, a workqueue event is
scheduled to translate the flag combination to IFLA_OPERSTATE as
follows:
!netif_carrier_ok():
IF_OPER_LOWERLAYERDOWN if the interface is stacked, IF_OPER_DOWN
otherwise. Kernel can recognise stacked interfaces because their
ifindex != iflink.
netif_carrier_ok() && netif_dormant():
IF_OPER_DORMANT
netif_carrier_ok() && !netif_dormant():
IF_OPER_UP if userspace interaction is disabled. Otherwise
IF_OPER_DORMANT with the possibility for userspace to initiate the
IF_OPER_UP transition afterwards.
4. Setting from userspace
Applications have to use the netlink interface to influence the
RFC2863 operational state of an interface. Setting IFLA_LINKMODE to 1
via RTM_SETLINK instructs the kernel that an interface should go to
IF_OPER_DORMANT instead of IF_OPER_UP when the combination
netif_carrier_ok() && !netif_dormant() is set by the
driver. Afterwards, the userspace application can set IFLA_OPERSTATE
to IF_OPER_DORMANT or IF_OPER_UP as long as the driver does not set
netif_carrier_off() or netif_dormant_on(). Changes made by userspace
are multicasted on the netlink group RTMGRP_LINK.
So basically a 802.1X supplicant interacts with the kernel like this:
-subscribe to RTMGRP_LINK
-set IFLA_LINKMODE to 1 via RTM_SETLINK
-query RTM_GETLINK once to get initial state
-if initial flags are not (IFF_LOWER_UP && !IFF_DORMANT), wait until
netlink multicast signals this state
-do 802.1X, eventually abort if flags go down again
-send RTM_SETLINK to set operstate to IF_OPER_UP if authentication
succeeds, IF_OPER_DORMANT otherwise
-see how operstate and IFF_RUNNING is echoed via netlink multicast
-set interface back to IF_OPER_DORMANT if 802.1X reauthentication
fails
-restart if kernel changes IFF_LOWER_UP or IFF_DORMANT flag
if supplicant goes down, bring back IFLA_LINKMODE to 0 and
IFLA_OPERSTATE to a sane value.
A routing daemon or dhcp client just needs to care for IFF_RUNNING or
waiting for operstate to go IF_OPER_UP/IF_OPER_UNKNOWN before
considering the interface / querying a DHCP address.
For technical questions and/or comments please e-mail to Stefan Rompf
(stefan at loplof.de).
......@@ -1480,10 +1480,11 @@ L: netdev@vger.kernel.org
S: Maintained
IRDA SUBSYSTEM
P: Jean Tourrilhes
P: Samuel Ortiz
M: samuel@sortiz.org
L: irda-users@lists.sourceforge.net (subscribers-only)
W: http://irda.sourceforge.net/
S: Odd Fixes
S: Maintained
ISAPNP
P: Jaroslav Kysela
......
......@@ -46,4 +46,4 @@ obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o
obj-$(CONFIG_TOIM3232_DONGLE) += toim3232-sir.o
# The SIR helper module
sir-dev-objs := sir_dev.o sir_dongle.o sir_kthread.o
sir-dev-objs := sir_dev.o sir_dongle.o
......@@ -15,23 +15,14 @@
#define IRDA_SIR_H
#include <linux/netdevice.h>
#include <linux/workqueue.h>
#include <net/irda/irda.h>
#include <net/irda/irda_device.h> // iobuff_t
/* FIXME: unify irda_request with sir_fsm! */
struct irda_request {
struct list_head lh_request;
unsigned long pending;
void (*func)(void *);
void *data;
struct timer_list timer;
};
struct sir_fsm {
struct semaphore sem;
struct irda_request rq;
struct work_struct work;
unsigned state, substate;
int param;
int result;
......
......@@ -23,6 +23,298 @@
#include "sir-dev.h"
static struct workqueue_struct *irda_sir_wq;
/* STATE MACHINE */
/* substate handler of the config-fsm to handle the cases where we want
* to wait for transmit completion before changing the port configuration
*/
static int sirdev_tx_complete_fsm(struct sir_dev *dev)
{
struct sir_fsm *fsm = &dev->fsm;
unsigned next_state, delay;
unsigned bytes_left;
do {
next_state = fsm->substate; /* default: stay in current substate */
delay = 0;
switch(fsm->substate) {
case SIRDEV_STATE_WAIT_XMIT:
if (dev->drv->chars_in_buffer)
bytes_left = dev->drv->chars_in_buffer(dev);
else
bytes_left = 0;
if (!bytes_left) {
next_state = SIRDEV_STATE_WAIT_UNTIL_SENT;
break;
}
if (dev->speed > 115200)
delay = (bytes_left*8*10000) / (dev->speed/100);
else if (dev->speed > 0)
delay = (bytes_left*10*10000) / (dev->speed/100);
else
delay = 0;
/* expected delay (usec) until remaining bytes are sent */
if (delay < 100) {
udelay(delay);
delay = 0;
break;
}
/* sleep some longer delay (msec) */
delay = (delay+999) / 1000;
break;
case SIRDEV_STATE_WAIT_UNTIL_SENT:
/* block until underlaying hardware buffer are empty */
if (dev->drv->wait_until_sent)
dev->drv->wait_until_sent(dev);
next_state = SIRDEV_STATE_TX_DONE;
break;
case SIRDEV_STATE_TX_DONE:
return 0;
default:
IRDA_ERROR("%s - undefined state\n", __FUNCTION__);
return -EINVAL;
}
fsm->substate = next_state;
} while (delay == 0);
return delay;
}
/*
* Function sirdev_config_fsm
*
* State machine to handle the configuration of the device (and attached dongle, if any).
* This handler is scheduled for execution in kIrDAd context, so we can sleep.
* however, kIrDAd is shared by all sir_dev devices so we better don't sleep there too
* long. Instead, for longer delays we start a timer to reschedule us later.
* On entry, fsm->sem is always locked and the netdev xmit queue stopped.
* Both must be unlocked/restarted on completion - but only on final exit.
*/
static void sirdev_config_fsm(void *data)
{
struct sir_dev *dev = data;
struct sir_fsm *fsm = &dev->fsm;
int next_state;
int ret = -1;
unsigned delay;
IRDA_DEBUG(2, "%s(), <%ld>\n", __FUNCTION__, jiffies);
do {
IRDA_DEBUG(3, "%s - state=0x%04x / substate=0x%04x\n",
__FUNCTION__, fsm->state, fsm->substate);
next_state = fsm->state;
delay = 0;
switch(fsm->state) {
case SIRDEV_STATE_DONGLE_OPEN:
if (dev->dongle_drv != NULL) {
ret = sirdev_put_dongle(dev);
if (ret) {
fsm->result = -EINVAL;
next_state = SIRDEV_STATE_ERROR;
break;
}
}
/* Initialize dongle */
ret = sirdev_get_dongle(dev, fsm->param);
if (ret) {
fsm->result = ret;
next_state = SIRDEV_STATE_ERROR;
break;
}
/* Dongles are powered through the modem control lines which
* were just set during open. Before resetting, let's wait for
* the power to stabilize. This is what some dongle drivers did
* in open before, while others didn't - should be safe anyway.
*/
delay = 50;
fsm->substate = SIRDEV_STATE_DONGLE_RESET;
next_state = SIRDEV_STATE_DONGLE_RESET;
fsm->param = 9600;
break;
case SIRDEV_STATE_DONGLE_CLOSE:
/* shouldn't we just treat this as success=? */
if (dev->dongle_drv == NULL) {
fsm->result = -EINVAL;
next_state = SIRDEV_STATE_ERROR;
break;
}
ret = sirdev_put_dongle(dev);
if (ret) {
fsm->result = ret;
next_state = SIRDEV_STATE_ERROR;
break;
}
next_state = SIRDEV_STATE_DONE;
break;
case SIRDEV_STATE_SET_DTR_RTS:
ret = sirdev_set_dtr_rts(dev,
(fsm->param&0x02) ? TRUE : FALSE,
(fsm->param&0x01) ? TRUE : FALSE);
next_state = SIRDEV_STATE_DONE;
break;
case SIRDEV_STATE_SET_SPEED:
fsm->substate = SIRDEV_STATE_WAIT_XMIT;
next_state = SIRDEV_STATE_DONGLE_CHECK;
break;
case SIRDEV_STATE_DONGLE_CHECK:
ret = sirdev_tx_complete_fsm(dev);
if (ret < 0) {
fsm->result = ret;
next_state = SIRDEV_STATE_ERROR;
break;
}
if ((delay=ret) != 0)
break;
if (dev->dongle_drv) {
fsm->substate = SIRDEV_STATE_DONGLE_RESET;
next_state = SIRDEV_STATE_DONGLE_RESET;
}
else {
dev->speed = fsm->param;
next_state = SIRDEV_STATE_PORT_SPEED;
}
break;
case SIRDEV_STATE_DONGLE_RESET:
if (dev->dongle_drv->reset) {
ret = dev->dongle_drv->reset(dev);
if (ret < 0) {
fsm->result = ret;
next_state = SIRDEV_STATE_ERROR;
break;
}
}
else
ret = 0;
if ((delay=ret) == 0) {
/* set serial port according to dongle default speed */
if (dev->drv->set_speed)
dev->drv->set_speed(dev, dev->speed);
fsm->substate = SIRDEV_STATE_DONGLE_SPEED;
next_state = SIRDEV_STATE_DONGLE_SPEED;
}
break;
case SIRDEV_STATE_DONGLE_SPEED:
if (dev->dongle_drv->reset) {
ret = dev->dongle_drv->set_speed(dev, fsm->param);
if (ret < 0) {
fsm->result = ret;
next_state = SIRDEV_STATE_ERROR;
break;
}
}
else
ret = 0;
if ((delay=ret) == 0)
next_state = SIRDEV_STATE_PORT_SPEED;
break;
case SIRDEV_STATE_PORT_SPEED:
/* Finally we are ready to change the serial port speed */
if (dev->drv->set_speed)
dev->drv->set_speed(dev, dev->speed);
dev->new_speed = 0;
next_state = SIRDEV_STATE_DONE;
break;
case SIRDEV_STATE_DONE:
/* Signal network layer so it can send more frames */
netif_wake_queue(dev->netdev);
next_state = SIRDEV_STATE_COMPLETE;
break;
default:
IRDA_ERROR("%s - undefined state\n", __FUNCTION__);
fsm->result = -EINVAL;
/* fall thru */
case SIRDEV_STATE_ERROR:
IRDA_ERROR("%s - error: %d\n", __FUNCTION__, fsm->result);
#if 0 /* don't enable this before we have netdev->tx_timeout to recover */
netif_stop_queue(dev->netdev);
#else
netif_wake_queue(dev->netdev);
#endif
/* fall thru */
case SIRDEV_STATE_COMPLETE:
/* config change finished, so we are not busy any longer */
sirdev_enable_rx(dev);
up(&fsm->sem);
return;
}
fsm->state = next_state;
} while(!delay);
queue_delayed_work(irda_sir_wq, &fsm->work, msecs_to_jiffies(delay));
}
/* schedule some device configuration task for execution by kIrDAd
* on behalf of the above state machine.
* can be called from process or interrupt/tasklet context.
*/
int sirdev_schedule_request(struct sir_dev *dev, int initial_state, unsigned param)
{
struct sir_fsm *fsm = &dev->fsm;
IRDA_DEBUG(2, "%s - state=0x%04x / param=%u\n", __FUNCTION__, initial_state, param);
if (down_trylock(&fsm->sem)) {
if (in_interrupt() || in_atomic() || irqs_disabled()) {
IRDA_DEBUG(1, "%s(), state machine busy!\n", __FUNCTION__);
return -EWOULDBLOCK;
} else
down(&fsm->sem);
}
if (fsm->state == SIRDEV_STATE_DEAD) {
/* race with sirdev_close should never happen */
IRDA_ERROR("%s(), instance staled!\n", __FUNCTION__);
up(&fsm->sem);
return -ESTALE; /* or better EPIPE? */
}
netif_stop_queue(dev->netdev);
atomic_set(&dev->enable_rx, 0);
fsm->state = initial_state;
fsm->param = param;
fsm->result = 0;
INIT_WORK(&fsm->work, sirdev_config_fsm, dev);
queue_work(irda_sir_wq, &fsm->work);
return 0;
}
/***************************************************************************/
void sirdev_enable_rx(struct sir_dev *dev)
......@@ -619,10 +911,6 @@ struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *n
spin_lock_init(&dev->tx_lock);
init_MUTEX(&dev->fsm.sem);
INIT_LIST_HEAD(&dev->fsm.rq.lh_request);
dev->fsm.rq.pending = 0;
init_timer(&dev->fsm.rq.timer);
dev->drv = drv;
dev->netdev = ndev;
......@@ -682,3 +970,22 @@ int sirdev_put_instance(struct sir_dev *dev)
}
EXPORT_SYMBOL(sirdev_put_instance);
static int __init sir_wq_init(void)
{
irda_sir_wq = create_singlethread_workqueue("irda_sir_wq");
if (!irda_sir_wq)
return -ENOMEM;
return 0;
}
static void __exit sir_wq_exit(void)
{
destroy_workqueue(irda_sir_wq);
}
module_init(sir_wq_init);
module_exit(sir_wq_exit);
MODULE_AUTHOR("Martin Diehl <info@mdiehl.de>");
MODULE_DESCRIPTION("IrDA SIR core");
MODULE_LICENSE("GPL");
/*********************************************************************
*
* sir_kthread.c: dedicated thread to process scheduled
* sir device setup requests
*
* Copyright (c) 2002 Martin Diehl
*
* 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 (at your option) any later version.
*
********************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <net/irda/irda.h>
#include "sir-dev.h"
/**************************************************************************
*
* kIrDAd kernel thread and config state machine
*
*/
struct irda_request_queue {
struct list_head request_list;
spinlock_t lock;
task_t *thread;
struct completion exit;
wait_queue_head_t kick, done;
atomic_t num_pending;
};
static struct irda_request_queue irda_rq_queue;
static int irda_queue_request(struct irda_request *rq)
{
int ret = 0;
unsigned long flags;
if (!test_and_set_bit(0, &rq->pending)) {
spin_lock_irqsave(&irda_rq_queue.lock, flags);
list_add_tail(&rq->lh_request, &irda_rq_queue.request_list);
wake_up(&irda_rq_queue.kick);
atomic_inc(&irda_rq_queue.num_pending);
spin_unlock_irqrestore(&irda_rq_queue.lock, flags);
ret = 1;
}
return ret;
}
static void irda_request_timer(unsigned long data)
{
struct irda_request *rq = (struct irda_request *)data;
unsigned long flags;
spin_lock_irqsave(&irda_rq_queue.lock, flags);
list_add_tail(&rq->lh_request, &irda_rq_queue.request_list);
wake_up(&irda_rq_queue.kick);
spin_unlock_irqrestore(&irda_rq_queue.lock, flags);
}
static int irda_queue_delayed_request(struct irda_request *rq, unsigned long delay)
{
int ret = 0;
struct timer_list *timer = &rq->timer;
if (!test_and_set_bit(0, &rq->pending)) {
timer->expires = jiffies + delay;
timer->function = irda_request_timer;
timer->data = (unsigned long)rq;
atomic_inc(&irda_rq_queue.num_pending);
add_timer(timer);
ret = 1;
}
return ret;
}
static void run_irda_queue(void)
{
unsigned long flags;
struct list_head *entry, *tmp;
struct irda_request *rq;
spin_lock_irqsave(&irda_rq_queue.lock, flags);
list_for_each_safe(entry, tmp, &irda_rq_queue.request_list) {
rq = list_entry(entry, struct irda_request, lh_request);
list_del_init(entry);
spin_unlock_irqrestore(&irda_rq_queue.lock, flags);
clear_bit(0, &rq->pending);
rq->func(rq->data);
if (atomic_dec_and_test(&irda_rq_queue.num_pending))
wake_up(&irda_rq_queue.done);
spin_lock_irqsave(&irda_rq_queue.lock, flags);
}
spin_unlock_irqrestore(&irda_rq_queue.lock, flags);
}
static int irda_thread(void *startup)
{
DECLARE_WAITQUEUE(wait, current);
daemonize("kIrDAd");
irda_rq_queue.thread = current;
complete((struct completion *)startup);
while (irda_rq_queue.thread != NULL) {
/* We use TASK_INTERRUPTIBLE, rather than
* TASK_UNINTERRUPTIBLE. Andrew Morton made this
* change ; he told me that it is safe, because "signal
* blocking is now handled in daemonize()", he added
* that the problem is that "uninterruptible sleep
* contributes to load average", making user worry.
* Jean II */
set_task_state(current, TASK_INTERRUPTIBLE);
add_wait_queue(&irda_rq_queue.kick, &wait);
if (list_empty(&irda_rq_queue.request_list))
schedule();
else
__set_task_state(current, TASK_RUNNING);
remove_wait_queue(&irda_rq_queue.kick, &wait);
/* make swsusp happy with our thread */
try_to_freeze();
run_irda_queue();
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,35)
reparent_to_init();
#endif
complete_and_exit(&irda_rq_queue.exit, 0);
/* never reached */
return 0;
}
static void flush_irda_queue(void)
{
if (atomic_read(&irda_rq_queue.num_pending)) {
DECLARE_WAITQUEUE(wait, current);
if (!list_empty(&irda_rq_queue.request_list))
run_irda_queue();
set_task_state(current, TASK_UNINTERRUPTIBLE);
add_wait_queue(&irda_rq_queue.done, &wait);
if (atomic_read(&irda_rq_queue.num_pending))
schedule();
else
__set_task_state(current, TASK_RUNNING);
remove_wait_queue(&irda_rq_queue.done, &wait);
}
}
/* substate handler of the config-fsm to handle the cases where we want
* to wait for transmit completion before changing the port configuration
*/
static int irda_tx_complete_fsm(struct sir_dev *dev)
{
struct sir_fsm *fsm = &dev->fsm;
unsigned next_state, delay;
unsigned bytes_left;
do {
next_state = fsm->substate; /* default: stay in current substate */
delay = 0;
switch(fsm->substate) {
case SIRDEV_STATE_WAIT_XMIT:
if (dev->drv->chars_in_buffer)
bytes_left = dev->drv->chars_in_buffer(dev);
else
bytes_left = 0;
if (!bytes_left) {
next_state = SIRDEV_STATE_WAIT_UNTIL_SENT;
break;
}
if (dev->speed > 115200)
delay = (bytes_left*8*10000) / (dev->speed/100);
else if (dev->speed > 0)
delay = (bytes_left*10*10000) / (dev->speed/100);
else
delay = 0;
/* expected delay (usec) until remaining bytes are sent */
if (delay < 100) {
udelay(delay);
delay = 0;
break;
}
/* sleep some longer delay (msec) */
delay = (delay+999) / 1000;
break;
case SIRDEV_STATE_WAIT_UNTIL_SENT:
/* block until underlaying hardware buffer are empty */
if (dev->drv->wait_until_sent)
dev->drv->wait_until_sent(dev);
next_state = SIRDEV_STATE_TX_DONE;
break;
case SIRDEV_STATE_TX_DONE:
return 0;
default:
IRDA_ERROR("%s - undefined state\n", __FUNCTION__);
return -EINVAL;
}
fsm->substate = next_state;
} while (delay == 0);
return delay;
}
/*
* Function irda_config_fsm
*
* State machine to handle the configuration of the device (and attached dongle, if any).
* This handler is scheduled for execution in kIrDAd context, so we can sleep.
* however, kIrDAd is shared by all sir_dev devices so we better don't sleep there too
* long. Instead, for longer delays we start a timer to reschedule us later.
* On entry, fsm->sem is always locked and the netdev xmit queue stopped.
* Both must be unlocked/restarted on completion - but only on final exit.
*/
static void irda_config_fsm(void *data)
{
struct sir_dev *dev = data;
struct sir_fsm *fsm = &dev->fsm;
int next_state;
int ret = -1;
unsigned delay;
IRDA_DEBUG(2, "%s(), <%ld>\n", __FUNCTION__, jiffies);
do {
IRDA_DEBUG(3, "%s - state=0x%04x / substate=0x%04x\n",
__FUNCTION__, fsm->state, fsm->substate);
next_state = fsm->state;
delay = 0;
switch(fsm->state) {
case SIRDEV_STATE_DONGLE_OPEN:
if (dev->dongle_drv != NULL) {
ret = sirdev_put_dongle(dev);
if (ret) {
fsm->result = -EINVAL;
next_state = SIRDEV_STATE_ERROR;
break;
}
}
/* Initialize dongle */
ret = sirdev_get_dongle(dev, fsm->param);
if (ret) {
fsm->result = ret;
next_state = SIRDEV_STATE_ERROR;
break;
}
/* Dongles are powered through the modem control lines which
* were just set during open. Before resetting, let's wait for
* the power to stabilize. This is what some dongle drivers did
* in open before, while others didn't - should be safe anyway.
*/
delay = 50;
fsm->substate = SIRDEV_STATE_DONGLE_RESET;
next_state = SIRDEV_STATE_DONGLE_RESET;
fsm->param = 9600;
break;
case SIRDEV_STATE_DONGLE_CLOSE:
/* shouldn't we just treat this as success=? */
if (dev->dongle_drv == NULL) {
fsm->result = -EINVAL;
next_state = SIRDEV_STATE_ERROR;
break;
}
ret = sirdev_put_dongle(dev);
if (ret) {
fsm->result = ret;
next_state = SIRDEV_STATE_ERROR;
break;
}
next_state = SIRDEV_STATE_DONE;
break;
case SIRDEV_STATE_SET_DTR_RTS:
ret = sirdev_set_dtr_rts(dev,
(fsm->param&0x02) ? TRUE : FALSE,
(fsm->param&0x01) ? TRUE : FALSE);
next_state = SIRDEV_STATE_DONE;
break;
case SIRDEV_STATE_SET_SPEED:
fsm->substate = SIRDEV_STATE_WAIT_XMIT;
next_state = SIRDEV_STATE_DONGLE_CHECK;
break;
case SIRDEV_STATE_DONGLE_CHECK:
ret = irda_tx_complete_fsm(dev);
if (ret < 0) {
fsm->result = ret;
next_state = SIRDEV_STATE_ERROR;
break;
}
if ((delay=ret) != 0)
break;
if (dev->dongle_drv) {
fsm->substate = SIRDEV_STATE_DONGLE_RESET;
next_state = SIRDEV_STATE_DONGLE_RESET;
}
else {
dev->speed = fsm->param;
next_state = SIRDEV_STATE_PORT_SPEED;
}
break;
case SIRDEV_STATE_DONGLE_RESET:
if (dev->dongle_drv->reset) {
ret = dev->dongle_drv->reset(dev);
if (ret < 0) {
fsm->result = ret;
next_state = SIRDEV_STATE_ERROR;
break;
}
}
else
ret = 0;
if ((delay=ret) == 0) {
/* set serial port according to dongle default speed */
if (dev->drv->set_speed)
dev->drv->set_speed(dev, dev->speed);
fsm->substate = SIRDEV_STATE_DONGLE_SPEED;
next_state = SIRDEV_STATE_DONGLE_SPEED;
}
break;
case SIRDEV_STATE_DONGLE_SPEED:
if (dev->dongle_drv->reset) {
ret = dev->dongle_drv->set_speed(dev, fsm->param);
if (ret < 0) {
fsm->result = ret;
next_state = SIRDEV_STATE_ERROR;
break;
}
}
else
ret = 0;
if ((delay=ret) == 0)
next_state = SIRDEV_STATE_PORT_SPEED;
break;
case SIRDEV_STATE_PORT_SPEED:
/* Finally we are ready to change the serial port speed */
if (dev->drv->set_speed)
dev->drv->set_speed(dev, dev->speed);
dev->new_speed = 0;
next_state = SIRDEV_STATE_DONE;
break;
case SIRDEV_STATE_DONE:
/* Signal network layer so it can send more frames */
netif_wake_queue(dev->netdev);
next_state = SIRDEV_STATE_COMPLETE;
break;
default:
IRDA_ERROR("%s - undefined state\n", __FUNCTION__);
fsm->result = -EINVAL;
/* fall thru */
case SIRDEV_STATE_ERROR:
IRDA_ERROR("%s - error: %d\n", __FUNCTION__, fsm->result);
#if 0 /* don't enable this before we have netdev->tx_timeout to recover */
netif_stop_queue(dev->netdev);
#else
netif_wake_queue(dev->netdev);
#endif
/* fall thru */
case SIRDEV_STATE_COMPLETE:
/* config change finished, so we are not busy any longer */
sirdev_enable_rx(dev);
up(&fsm->sem);
return;
}
fsm->state = next_state;
} while(!delay);
irda_queue_delayed_request(&fsm->rq, msecs_to_jiffies(delay));
}
/* schedule some device configuration task for execution by kIrDAd
* on behalf of the above state machine.
* can be called from process or interrupt/tasklet context.
*/
int sirdev_schedule_request(struct sir_dev *dev, int initial_state, unsigned param)
{
struct sir_fsm *fsm = &dev->fsm;
int xmit_was_down;
IRDA_DEBUG(2, "%s - state=0x%04x / param=%u\n", __FUNCTION__, initial_state, param);
if (down_trylock(&fsm->sem)) {
if (in_interrupt() || in_atomic() || irqs_disabled()) {
IRDA_DEBUG(1, "%s(), state machine busy!\n", __FUNCTION__);
return -EWOULDBLOCK;
} else
down(&fsm->sem);
}
if (fsm->state == SIRDEV_STATE_DEAD) {
/* race with sirdev_close should never happen */
IRDA_ERROR("%s(), instance staled!\n", __FUNCTION__);
up(&fsm->sem);
return -ESTALE; /* or better EPIPE? */
}
xmit_was_down = netif_queue_stopped(dev->netdev);
netif_stop_queue(dev->netdev);
atomic_set(&dev->enable_rx, 0);
fsm->state = initial_state;
fsm->param = param;
fsm->result = 0;
INIT_LIST_HEAD(&fsm->rq.lh_request);
fsm->rq.pending = 0;
fsm->rq.func = irda_config_fsm;
fsm->rq.data = dev;
if (!irda_queue_request(&fsm->rq)) { /* returns 0 on error! */
atomic_set(&dev->enable_rx, 1);
if (!xmit_was_down)
netif_wake_queue(dev->netdev);
up(&fsm->sem);
return -EAGAIN;
}
return 0;
}
static int __init irda_thread_create(void)
{
struct completion startup;
int pid;
spin_lock_init(&irda_rq_queue.lock);
irda_rq_queue.thread = NULL;
INIT_LIST_HEAD(&irda_rq_queue.request_list);
init_waitqueue_head(&irda_rq_queue.kick);
init_waitqueue_head(&irda_rq_queue.done);
atomic_set(&irda_rq_queue.num_pending, 0);
init_completion(&startup);
pid = kernel_thread(irda_thread, &startup, CLONE_FS|CLONE_FILES);
if (pid <= 0)
return -EAGAIN;
else
wait_for_completion(&startup);
return 0;
}
static void __exit irda_thread_join(void)
{
if (irda_rq_queue.thread) {
flush_irda_queue();
init_completion(&irda_rq_queue.exit);
irda_rq_queue.thread = NULL;
wake_up(&irda_rq_queue.kick);
wait_for_completion(&irda_rq_queue.exit);
}
}
module_init(irda_thread_create);
module_exit(irda_thread_join);
MODULE_AUTHOR("Martin Diehl <info@mdiehl.de>");
MODULE_DESCRIPTION("IrDA SIR core");
MODULE_LICENSE("GPL");
......@@ -54,6 +54,7 @@
#include <linux/rtnetlink.h>
#include <linux/serial_reg.h>
#include <linux/dma-mapping.h>
#include <linux/pnp.h>
#include <linux/platform_device.h>
#include <asm/io.h>
......@@ -358,6 +359,16 @@ static inline void register_bank(int iobase, int bank)
iobase + IRCC_MASTER);
}
#ifdef CONFIG_PNP
/* PNP hotplug support */
static const struct pnp_device_id smsc_ircc_pnp_table[] = {
{ .id = "SMCf010", .driver_data = 0 },
/* and presumably others */
{ }
};
MODULE_DEVICE_TABLE(pnp, smsc_ircc_pnp_table);
#endif
/*******************************************************************************
*
......@@ -2072,7 +2083,8 @@ static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self)
/* PROBING
*
*
* REVISIT we can be told about the device by PNP, and should use that info
* instead of probing hardware and creating a platform_device ...
*/
static int __init smsc_ircc_look_for_chips(void)
......
......@@ -8454,6 +8454,9 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
tx_len = 1514;
skb = dev_alloc_skb(tx_len);
if (!skb)
return -ENOMEM;
tx_data = skb_put(skb, tx_len);
memcpy(tx_data, tp->dev->dev_addr, 6);
memset(tx_data + 6, 0x0, 8);
......
......@@ -433,8 +433,7 @@ struct net_device
/* register/unregister state machine */
enum { NETREG_UNINITIALIZED=0,
NETREG_REGISTERING, /* called register_netdevice */
NETREG_REGISTERED, /* completed register todo */
NETREG_REGISTERED, /* completed register_netdevice */
NETREG_UNREGISTERING, /* called unregister_netdevice */
NETREG_UNREGISTERED, /* completed unregister todo */
NETREG_RELEASED, /* called free_netdev */
......
......@@ -308,26 +308,19 @@ int br_add_bridge(const char *name)
if (ret)
goto err2;
/* network device kobject is not setup until
* after rtnl_unlock does it's hotplug magic.
* so hold reference to avoid race.
*/
dev_hold(dev);
rtnl_unlock();
ret = br_sysfs_addbr(dev);
dev_put(dev);
if (ret)
unregister_netdev(dev);
out:
return ret;
goto err3;
rtnl_unlock();
return 0;
err3:
unregister_netdev(dev);
err2:
free_netdev(dev);
err1:
rtnl_unlock();
goto out;
return ret;
}
int br_del_bridge(const char *name)
......
......@@ -193,7 +193,7 @@ static inline struct hlist_head *dev_index_hash(int ifindex)
* Our notifier list
*/
static BLOCKING_NOTIFIER_HEAD(netdev_chain);
static RAW_NOTIFIER_HEAD(netdev_chain);
/*
* Device drivers call our routines to queue packets here. We empty the
......@@ -736,7 +736,7 @@ int dev_change_name(struct net_device *dev, char *newname)
if (!err) {
hlist_del(&dev->name_hlist);
hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name));
blocking_notifier_call_chain(&netdev_chain,
raw_notifier_call_chain(&netdev_chain,
NETDEV_CHANGENAME, dev);
}
......@@ -751,7 +751,7 @@ int dev_change_name(struct net_device *dev, char *newname)
*/
void netdev_features_change(struct net_device *dev)
{
blocking_notifier_call_chain(&netdev_chain, NETDEV_FEAT_CHANGE, dev);
raw_notifier_call_chain(&netdev_chain, NETDEV_FEAT_CHANGE, dev);
}
EXPORT_SYMBOL(netdev_features_change);
......@@ -766,7 +766,7 @@ EXPORT_SYMBOL(netdev_features_change);
void netdev_state_change(struct net_device *dev)
{
if (dev->flags & IFF_UP) {
blocking_notifier_call_chain(&netdev_chain,
raw_notifier_call_chain(&netdev_chain,
NETDEV_CHANGE, dev);
rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
}
......@@ -864,7 +864,7 @@ int dev_open(struct net_device *dev)
/*
* ... and announce new interface.
*/
blocking_notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
raw_notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
}
return ret;
}
......@@ -887,7 +887,7 @@ int dev_close(struct net_device *dev)
* Tell people we are going down, so that they can
* prepare to death, when device is still operating.
*/
blocking_notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev);
raw_notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev);
dev_deactivate(dev);
......@@ -924,7 +924,7 @@ int dev_close(struct net_device *dev)
/*
* Tell people we are down
*/
blocking_notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
raw_notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
return 0;
}
......@@ -955,7 +955,7 @@ int register_netdevice_notifier(struct notifier_block *nb)
int err;
rtnl_lock();
err = blocking_notifier_chain_register(&netdev_chain, nb);
err = raw_notifier_chain_register(&netdev_chain, nb);
if (!err) {
for (dev = dev_base; dev; dev = dev->next) {
nb->notifier_call(nb, NETDEV_REGISTER, dev);
......@@ -983,7 +983,7 @@ int unregister_netdevice_notifier(struct notifier_block *nb)
int err;
rtnl_lock();
err = blocking_notifier_chain_unregister(&netdev_chain, nb);
err = raw_notifier_chain_unregister(&netdev_chain, nb);
rtnl_unlock();
return err;
}
......@@ -994,12 +994,12 @@ int unregister_netdevice_notifier(struct notifier_block *nb)
* @v: pointer passed unmodified to notifier function
*
* Call all network notifier blocks. Parameters and return value
* are as for blocking_notifier_call_chain().
* are as for raw_notifier_call_chain().
*/
int call_netdevice_notifiers(unsigned long val, void *v)
{
return blocking_notifier_call_chain(&netdev_chain, val, v);
return raw_notifier_call_chain(&netdev_chain, val, v);
}
/* When > 0 there are consumers of rx skb time stamps */
......@@ -2308,7 +2308,7 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
if (dev->flags & IFF_UP &&
((old_flags ^ dev->flags) &~ (IFF_UP | IFF_PROMISC | IFF_ALLMULTI |
IFF_VOLATILE)))
blocking_notifier_call_chain(&netdev_chain,
raw_notifier_call_chain(&netdev_chain,
NETDEV_CHANGE, dev);
if ((flags ^ dev->gflags) & IFF_PROMISC) {
......@@ -2353,7 +2353,7 @@ int dev_set_mtu(struct net_device *dev, int new_mtu)
else
dev->mtu = new_mtu;
if (!err && dev->flags & IFF_UP)
blocking_notifier_call_chain(&netdev_chain,
raw_notifier_call_chain(&netdev_chain,
NETDEV_CHANGEMTU, dev);
return err;
}
......@@ -2370,7 +2370,7 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
return -ENODEV;
err = dev->set_mac_address(dev, sa);
if (!err)
blocking_notifier_call_chain(&netdev_chain,
raw_notifier_call_chain(&netdev_chain,
NETDEV_CHANGEADDR, dev);
return err;
}
......@@ -2427,7 +2427,7 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
return -EINVAL;
memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data,
min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len));
blocking_notifier_call_chain(&netdev_chain,
raw_notifier_call_chain(&netdev_chain,
NETDEV_CHANGEADDR, dev);
return 0;
......@@ -2777,6 +2777,8 @@ int register_netdevice(struct net_device *dev)
BUG_ON(dev_boot_phase);
ASSERT_RTNL();
might_sleep();
/* When net_device's are persistent, this will be fatal. */
BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
......@@ -2863,6 +2865,11 @@ int register_netdevice(struct net_device *dev)
if (!dev->rebuild_header)
dev->rebuild_header = default_rebuild_header;
ret = netdev_register_sysfs(dev);
if (ret)
goto out_err;
dev->reg_state = NETREG_REGISTERED;
/*
* Default initial state at registry is that the
* device is present.
......@@ -2878,14 +2885,11 @@ int register_netdevice(struct net_device *dev)
hlist_add_head(&dev->name_hlist, head);
hlist_add_head(&dev->index_hlist, dev_index_hash(dev->ifindex));
dev_hold(dev);
dev->reg_state = NETREG_REGISTERING;
write_unlock_bh(&dev_base_lock);
/* Notify protocols, that a new device appeared. */
blocking_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
/* Finish registration after unlock */
net_set_todo(dev);
ret = 0;
out:
......@@ -2961,7 +2965,7 @@ static void netdev_wait_allrefs(struct net_device *dev)
rtnl_lock();
/* Rebroadcast unregister notification */
blocking_notifier_call_chain(&netdev_chain,
raw_notifier_call_chain(&netdev_chain,
NETDEV_UNREGISTER, dev);
if (test_bit(__LINK_STATE_LINKWATCH_PENDING,
......@@ -3008,7 +3012,7 @@ static void netdev_wait_allrefs(struct net_device *dev)
*
* We are invoked by rtnl_unlock() after it drops the semaphore.
* This allows us to deal with problems:
* 1) We can create/delete sysfs objects which invoke hotplug
* 1) We can delete sysfs objects which invoke hotplug
* without deadlocking with linkwatch via keventd.
* 2) Since we run with the RTNL semaphore not held, we can sleep
* safely in order to wait for the netdev refcnt to drop to zero.
......@@ -3017,8 +3021,6 @@ static DEFINE_MUTEX(net_todo_run_mutex);
void netdev_run_todo(void)
{
struct list_head list = LIST_HEAD_INIT(list);
int err;
/* Need to guard against multiple cpu's getting out of order. */
mutex_lock(&net_todo_run_mutex);
......@@ -3041,16 +3043,13 @@ void netdev_run_todo(void)
= list_entry(list.next, struct net_device, todo_list);
list_del(&dev->todo_list);
switch(dev->reg_state) {
case NETREG_REGISTERING:
err = netdev_register_sysfs(dev);
if (err)
printk(KERN_ERR "%s: failed sysfs registration (%d)\n",
dev->name, err);
dev->reg_state = NETREG_REGISTERED;
break;
if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) {
printk(KERN_ERR "network todo '%s' but state %d\n",
dev->name, dev->reg_state);
dump_stack();
continue;
}
case NETREG_UNREGISTERING:
netdev_unregister_sysfs(dev);
dev->reg_state = NETREG_UNREGISTERED;
......@@ -3062,19 +3061,11 @@ void netdev_run_todo(void)
BUG_TRAP(!dev->ip6_ptr);
BUG_TRAP(!dev->dn_ptr);
/* It must be the very last action,
* after this 'dev' may point to freed up memory.
*/
if (dev->destructor)
dev->destructor(dev);
break;
default:
printk(KERN_ERR "network todo '%s' but state %d\n",
dev->name, dev->reg_state);
break;
}
}
out:
......@@ -3216,7 +3207,7 @@ int unregister_netdevice(struct net_device *dev)
/* Notify protocols, that we are about to destroy
this device. They should clean all the things.
*/
blocking_notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);
raw_notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);
/*
* Flush the multicast chain
......
......@@ -170,13 +170,13 @@ void linkwatch_fire_event(struct net_device *dev)
spin_unlock_irqrestore(&lweventlist_lock, flags);
if (!test_and_set_bit(LW_RUNNING, &linkwatch_flags)) {
unsigned long thisevent = jiffies;
unsigned long delay = linkwatch_nextevent - jiffies;
if (thisevent >= linkwatch_nextevent) {
/* If we wrap around we'll delay it by at most HZ. */
if (!delay || delay > HZ)
schedule_work(&linkwatch_work);
} else {
schedule_delayed_work(&linkwatch_work, linkwatch_nextevent - thisevent);
}
else
schedule_delayed_work(&linkwatch_work, delay);
}
}
}
......
......@@ -209,7 +209,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
void ip_options_fragment(struct sk_buff * skb)
{
unsigned char * optptr = skb->nh.raw;
unsigned char * optptr = skb->nh.raw + sizeof(struct iphdr);
struct ip_options * opt = &(IPCB(skb)->opt);
int l = opt->optlen;
int optlen;
......
......@@ -173,6 +173,7 @@ int inet6_csk_xmit(struct sk_buff *skb, int ipfragok)
if (err) {
sk->sk_err_soft = -err;
kfree_skb(skb);
return err;
}
......@@ -181,6 +182,7 @@ int inet6_csk_xmit(struct sk_buff *skb, int ipfragok)
if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
sk->sk_route_caps = 0;
kfree_skb(skb);
return err;
}
......
......@@ -257,7 +257,6 @@ struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name)
/* Unsafe (locking), attrib might change */
return attrib;
}
EXPORT_SYMBOL(irias_find_attrib);
/*
* Function irias_add_attribute (obj, attrib)
......@@ -484,7 +483,6 @@ struct ias_value *irias_new_string_value(char *string)
return value;
}
EXPORT_SYMBOL(irias_new_string_value);
/*
* Function irias_new_octseq_value (octets, len)
......@@ -519,7 +517,6 @@ struct ias_value *irias_new_octseq_value(__u8 *octseq , int len)
memcpy(value->t.oct_seq, octseq , len);
return value;
}
EXPORT_SYMBOL(irias_new_octseq_value);
struct ias_value *irias_new_missing_value(void)
{
......
......@@ -974,10 +974,10 @@ hfsc_adjust_levels(struct hfsc_class *cl)
do {
level = 0;
list_for_each_entry(p, &cl->children, siblings) {
if (p->level > level)
level = p->level;
if (p->level >= level)
level = p->level + 1;
}
cl->level = level + 1;
cl->level = level;
} while ((cl = cl->cl_parent) != NULL);
}
......
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