Skip to content

Commit

Permalink
[PATCH] ipw2200: batch non-user-requested scan result notifications
Browse files Browse the repository at this point in the history
ipw2200 makes extensive use of background scanning when unassociated or
down.  Unfortunately, the firmware sends scan completed events many
times per second, which the driver pushes directly up to userspace.
This needlessly wakes up processes listening for wireless events many
times per second.  Batch together scan completed events for
non-user-requested scans and send them up to userspace every 4 seconds.
Scan completed events resulting from an SIOCSIWSCAN call are pushed up
without delay.

Signed-off-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Dan Williams authored and David S. Miller committed Oct 10, 2007
1 parent 04dd9d3 commit 0b53167
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 10 deletions.
56 changes: 46 additions & 10 deletions drivers/net/wireless/ipw2200.c
Original file line number Diff line number Diff line change
Expand Up @@ -1740,8 +1740,10 @@ static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
if (disable_radio) {
priv->status |= STATUS_RF_KILL_SW;

if (priv->workqueue)
if (priv->workqueue) {
cancel_delayed_work(&priv->request_scan);
cancel_delayed_work(&priv->scan_event);
}
queue_work(priv->workqueue, &priv->down);
} else {
priv->status &= ~STATUS_RF_KILL_SW;
Expand Down Expand Up @@ -1992,6 +1994,7 @@ static void ipw_irq_tasklet(struct ipw_priv *priv)
wake_up_interruptible(&priv->wait_command_queue);
priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
cancel_delayed_work(&priv->request_scan);
cancel_delayed_work(&priv->scan_event);
schedule_work(&priv->link_down);
queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
handled |= IPW_INTA_BIT_RF_KILL_DONE;
Expand Down Expand Up @@ -4343,6 +4346,37 @@ static void ipw_handle_missed_beacon(struct ipw_priv *priv,
IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count);
}

static void ipw_scan_event(struct work_struct *work)
{
union iwreq_data wrqu;

struct ipw_priv *priv =
container_of(work, struct ipw_priv, scan_event.work);

wrqu.data.length = 0;
wrqu.data.flags = 0;
wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
}

static void handle_scan_event(struct ipw_priv *priv)
{
/* Only userspace-requested scan completion events go out immediately */
if (!priv->user_requested_scan) {
if (!delayed_work_pending(&priv->scan_event))
queue_delayed_work(priv->workqueue, &priv->scan_event,
round_jiffies(msecs_to_jiffies(4000)));
} else {
union iwreq_data wrqu;

priv->user_requested_scan = 0;
cancel_delayed_work(&priv->scan_event);

wrqu.data.length = 0;
wrqu.data.flags = 0;
wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
}
}

/**
* Handle host notification packet.
* Called from interrupt routine
Expand Down Expand Up @@ -4705,14 +4739,8 @@ static void ipw_rx_notification(struct ipw_priv *priv,
* on how the scan was initiated. User space can just
* sync on periodic scan to get fresh data...
* Jean II */
if (x->status == SCAN_COMPLETED_STATUS_COMPLETE) {
union iwreq_data wrqu;

wrqu.data.length = 0;
wrqu.data.flags = 0;
wireless_send_event(priv->net_dev, SIOCGIWSCAN,
&wrqu, NULL);
}
if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
handle_scan_event(priv);
break;
}

Expand Down Expand Up @@ -9490,6 +9518,10 @@ static int ipw_wx_set_scan(struct net_device *dev,
struct ipw_priv *priv = ieee80211_priv(dev);
struct iw_scan_req *req = (struct iw_scan_req *)extra;

mutex_lock(&priv->mutex);
priv->user_requested_scan = 1;
mutex_unlock(&priv->mutex);

if (wrqu->data.length == sizeof(struct iw_scan_req)) {
if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
ipw_request_direct_scan(priv, req->essid,
Expand Down Expand Up @@ -10668,6 +10700,7 @@ static void ipw_link_up(struct ipw_priv *priv)
}

cancel_delayed_work(&priv->request_scan);
cancel_delayed_work(&priv->scan_event);
ipw_reset_stats(priv);
/* Ensure the rate is updated immediately */
priv->last_rate = ipw_get_current_rate(priv);
Expand Down Expand Up @@ -10705,7 +10738,8 @@ static void ipw_link_down(struct ipw_priv *priv)
if (!(priv->status & STATUS_EXIT_PENDING)) {
/* Queue up another scan... */
queue_delayed_work(priv->workqueue, &priv->request_scan, 0);
}
} else
cancel_delayed_work(&priv->scan_event);
}

static void ipw_bg_link_down(struct work_struct *work)
Expand Down Expand Up @@ -10735,6 +10769,7 @@ static int ipw_setup_deferred_work(struct ipw_priv *priv)
INIT_WORK(&priv->up, ipw_bg_up);
INIT_WORK(&priv->down, ipw_bg_down);
INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan);
INIT_DELAYED_WORK(&priv->scan_event, ipw_scan_event);
INIT_WORK(&priv->request_passive_scan, ipw_request_passive_scan);
INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats);
INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan);
Expand Down Expand Up @@ -11766,6 +11801,7 @@ static void ipw_pci_remove(struct pci_dev *pdev)
cancel_delayed_work(&priv->adhoc_check);
cancel_delayed_work(&priv->gather_stats);
cancel_delayed_work(&priv->request_scan);
cancel_delayed_work(&priv->scan_event);
cancel_delayed_work(&priv->rf_kill);
cancel_delayed_work(&priv->scan_check);
destroy_workqueue(priv->workqueue);
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/wireless/ipw2200.h
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,8 @@ struct ipw_priv {

struct iw_public_data wireless_data;

int user_requested_scan;

struct workqueue_struct *workqueue;

struct delayed_work adhoc_check;
Expand All @@ -1295,6 +1297,7 @@ struct ipw_priv {
struct work_struct system_config;
struct work_struct rx_replenish;
struct delayed_work request_scan;
struct delayed_work scan_event;
struct work_struct request_passive_scan;
struct work_struct adapter_restart;
struct delayed_work rf_kill;
Expand Down

0 comments on commit 0b53167

Please sign in to comment.