* [GIT PATCH] rfkill updates
@ 2008-10-09 21:15 Henrique de Moraes Holschuh
2008-10-09 21:15 ` [PATCH 1/5] rfkill: use killable locks instead of interruptible Henrique de Moraes Holschuh
` (5 more replies)
0 siblings, 6 replies; 18+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-10-09 21:15 UTC (permalink / raw)
To: John Linville; +Cc: linux-wireless, Ivo van Doorn
This patchset has the current stack of changes I am comfortable enough to
propose for merging.
There is some other stuff in the burner, but since they are userspace-ABI
related and I am not sure I am happy with it yet, I want to think a bit
more before sending them to the list as an RFC patchset.
I don't really care if this patch set make it to 2.6.28 merge window or
not, they're mostly enhancements... still, I would appreciate comments
(and if they are good enough already, ACKs and maybe even a merge into
wireless-testing).
Shortlog:
Henrique de Moraes Holschuh (5):
rfkill: use killable locks instead of interruptible
rfkill: export global states to rfkill-input
rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input
rfkill: honour EPO state when resuming a rfkill controller
rfkill: rate-limit rfkill-input workqueue usage (v3)
Thanks!
--
"One disk to rule them all, One disk to find them. One disk to bring
them all and in the darkness grind them. In the Land of Redmond
where the shadows lie." -- The Silicon Valley Tarot
Henrique Holschuh
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 1/5] rfkill: use killable locks instead of interruptible
2008-10-09 21:15 [GIT PATCH] rfkill updates Henrique de Moraes Holschuh
@ 2008-10-09 21:15 ` Henrique de Moraes Holschuh
2008-10-11 13:53 ` Ivo van Doorn
2008-10-09 21:15 ` [PATCH 2/5] rfkill: export global states to rfkill-input Henrique de Moraes Holschuh
` (4 subsequent siblings)
5 siblings, 1 reply; 18+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-10-09 21:15 UTC (permalink / raw)
To: John Linville
Cc: linux-wireless, Ivo van Doorn, Henrique de Moraes Holschuh,
Ivo van Doorn
Apparently, many applications don't expect to get EAGAIN from fd read/write
operations, since POSIX doesn't mandate it.
Use mutex_lock_killable instead of mutex_lock_interruptible, which won't
cause issues.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Ivo van Doorn <IvDoorn@gmail.com>
---
net/rfkill/rfkill.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
index f949a48..08be968 100644
--- a/net/rfkill/rfkill.c
+++ b/net/rfkill/rfkill.c
@@ -431,8 +431,9 @@ static ssize_t rfkill_state_store(struct device *dev,
state != RFKILL_STATE_SOFT_BLOCKED)
return -EINVAL;
- if (mutex_lock_interruptible(&rfkill->mutex))
- return -ERESTARTSYS;
+ error = mutex_lock_killable(&rfkill->mutex);
+ if (error)
+ return error;
error = rfkill_toggle_radio(rfkill, state, 0);
mutex_unlock(&rfkill->mutex);
@@ -472,7 +473,7 @@ static ssize_t rfkill_claim_store(struct device *dev,
* Take the global lock to make sure the kernel is not in
* the middle of rfkill_switch_all
*/
- error = mutex_lock_interruptible(&rfkill_global_mutex);
+ error = mutex_lock_killable(&rfkill_global_mutex);
if (error)
return error;
--
1.5.6.5
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 2/5] rfkill: export global states to rfkill-input
2008-10-09 21:15 [GIT PATCH] rfkill updates Henrique de Moraes Holschuh
2008-10-09 21:15 ` [PATCH 1/5] rfkill: use killable locks instead of interruptible Henrique de Moraes Holschuh
@ 2008-10-09 21:15 ` Henrique de Moraes Holschuh
2008-10-11 13:53 ` Ivo van Doorn
2008-10-09 21:15 ` [PATCH 3/5] rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input Henrique de Moraes Holschuh
` (3 subsequent siblings)
5 siblings, 1 reply; 18+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-10-09 21:15 UTC (permalink / raw)
To: John Linville
Cc: linux-wireless, Ivo van Doorn, Henrique de Moraes Holschuh,
Ivo van Doorn
Export the the global switch states to rfkill-input. This is needed to
properly implement KEY_* handling without disregarding the initial state.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Ivo van Doorn <IvDoorn@gmail.com>
---
net/rfkill/rfkill-input.h | 1 +
net/rfkill/rfkill.c | 14 ++++++++++++++
2 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill-input.h
index bbfa646..d1e03e8 100644
--- a/net/rfkill/rfkill-input.h
+++ b/net/rfkill/rfkill-input.h
@@ -14,5 +14,6 @@
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
void rfkill_epo(void);
void rfkill_restore_states(void);
+enum rfkill_state rfkill_get_global_state(const enum rfkill_type type);
#endif /* __RFKILL_INPUT_H */
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
index 08be968..fdf87d2 100644
--- a/net/rfkill/rfkill.c
+++ b/net/rfkill/rfkill.c
@@ -324,6 +324,19 @@ void rfkill_restore_states(void)
EXPORT_SYMBOL_GPL(rfkill_restore_states);
/**
+ * rfkill_get_global_state - returns global state for a type
+ * @type: the type to get the global state of
+ *
+ * Returns the current global state for a given wireless
+ * device type.
+ */
+enum rfkill_state rfkill_get_global_state(const enum rfkill_type type)
+{
+ return rfkill_global_states[type].current_state;
+}
+EXPORT_SYMBOL_GPL(rfkill_get_global_state);
+
+/**
* rfkill_force_state - Force the internal rfkill radio state
* @rfkill: pointer to the rfkill class to modify.
* @state: the current radio state the class should be forced to.
@@ -834,6 +847,7 @@ int rfkill_set_default(enum rfkill_type type, enum rfkill_state state)
if (!test_and_set_bit(type, rfkill_states_lockdflt)) {
rfkill_global_states[type].default_state = state;
+ rfkill_global_states[type].current_state = state;
error = 0;
} else
error = -EPERM;
--
1.5.6.5
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 3/5] rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input
2008-10-09 21:15 [GIT PATCH] rfkill updates Henrique de Moraes Holschuh
2008-10-09 21:15 ` [PATCH 1/5] rfkill: use killable locks instead of interruptible Henrique de Moraes Holschuh
2008-10-09 21:15 ` [PATCH 2/5] rfkill: export global states to rfkill-input Henrique de Moraes Holschuh
@ 2008-10-09 21:15 ` Henrique de Moraes Holschuh
2008-10-09 21:20 ` Henrique de Moraes Holschuh
2008-10-09 21:15 ` [PATCH 4/5] rfkill: honour EPO state when resuming a rfkill controller Henrique de Moraes Holschuh
` (2 subsequent siblings)
5 siblings, 1 reply; 18+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-10-09 21:15 UTC (permalink / raw)
To: John Linville
Cc: linux-wireless, Ivo van Doorn, Henrique de Moraes Holschuh,
Ivo van Doorn, Dmitry Torokhov
Add of software-based sanity to rfkill and rfkill-input so that it can
reproduce what hardware-based EPO switches do, blocking all transmitters
and locking down any further attempts to unblock them until the switch is
deactivated.
rfkill-input is responsible for issuing the EPO control requests, like
before.
While an rfkill EPO is active, all transmitters are locked to one of the
BLOCKED states and all attempts to change that thorugh the rfkill API
(userspace and kernel) will be either ignored or return -EPERM errors.
The lock will be released upon recepit of EV_SW SW_RFKILL_ALL ON by
rfkill-input, or should modular rfkill-input be unloaded.
This makes rfkill and rfkill-input extend the operation of an existing
wireless master kill switch to all wireless devices in the system, even
those that are not under hardware or firmware control.
Since the above is the expected operational behaviour for the master rfkill
switch, the EPO lock functionality is not optional.
Also, extend rfkill-input to allow for three diferent behaviours when it
receives an EV_SW SW_RFKILL_ALL ON input event. The user can set which
behaviour he wants through the master_switch_mode parameter:
master_switch_mode = 0: EV_SW SW_RFKILL_ALL ON just unlocks rfkill
controller state changes (so that the rfkill userspace and kernel APIs can
now be used to change rfkill controller states again), but doesn't change
any of their states (so they will all remain blocked). This is the safest
mode of operation, as it requires explicit operator action to re-enable a
transmitter.
master_switch_mode = 1: EV_SW SW_RFKILL_ALL ON causes rfkill-input to
attempt to restore the system to the state before the last EV_SW
SW_RFKILL_ALL OFF event, or to the default global states if no EV_SW
SW_RFKILL_ALL OFF ever happened. This is the recommented mode of
operation for laptops.
master_switch_mode = 2: tries to unblock all rfkill controllers (i.e.
enable all transmitters) when an EV_SW SW_RFKILL_ALL ON event is received.
This is the default mode of operation, as it mimics the previous behaviour
of rfkill-input.
In order to implement these features in a clean way, the entire event
handling of rfkill-input was refactored into a single worker function.
Protection against input event DoS (repeatedly firing rfkill events for
rfkill-input to process) was removed during the code refactoring. It will
be added back in a future patch.
Note that with these changes, rfkill-input doesn't need to explicitly
handle any radio types for which KEY_<radio type> or SW_<radio type> events
do not exist yet.
Code to handle EV_SW SW_{WLAN,WWAN,BLUETOOTH,WIMAX,...} was added as it
might be needed in the future (and its implementation is not that obvious),
but is currently #ifdef'd out to avoid wasting resources.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Ivo van Doorn <IvDoorn@gmail.com>
Cc: Dmitry Torokhov <dtor@mail.ru>
---
Documentation/rfkill.txt | 20 ++-
net/rfkill/rfkill-input.c | 301 ++++++++++++++++++++++++++++++++++-----------
net/rfkill/rfkill-input.h | 2 +
net/rfkill/rfkill.c | 46 +++++++-
4 files changed, 287 insertions(+), 82 deletions(-)
diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt
index b65f079..4d3ee31 100644
--- a/Documentation/rfkill.txt
+++ b/Documentation/rfkill.txt
@@ -191,12 +191,20 @@ Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
to tell the devices registered with the rfkill class to change
their state (i.e. translates the input layer event into real
action).
+
* rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
(power off all transmitters) in a special way: it ignores any
overrides and local state cache and forces all transmitters to the
RFKILL_STATE_SOFT_BLOCKED state (including those which are already
- supposed to be BLOCKED). Note that the opposite event (power on all
- transmitters) is handled normally.
+ supposed to be BLOCKED).
+ * rfkill EPO will remain active until rfkill-input receives an
+ EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters
+ are locked in the blocked state (rfkill will refuse to unblock them).
+ * rfkill-input implements different policies that the user can
+ select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill,
+ and either do nothing (leave transmitters blocked, but now unlocked),
+ restore the transmitters to their state before the EPO, or unblock
+ them all.
Userspace uevent handler or kernel platform-specific drivers hooked to the
rfkill notifier chain:
@@ -331,11 +339,9 @@ class to get a sysfs interface :-)
correct event for your switch/button. These events are emergency power-off
events when they are trying to turn the transmitters off. An example of an
input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill
-switch in a laptop which is NOT a hotkey, but a real switch that kills radios
-in hardware, even if the O.S. has gone to lunch. An example of an input device
-which SHOULD NOT generate *_RFKILL_ALL events by default, is any sort of hot
-key that does nothing by itself, as well as any hot key that is type-specific
-(e.g. the one for WLAN).
+switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch.
+An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
+default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
3.1 Guidelines for wireless device drivers
diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c
index e5b6955..4ea4e68 100644
--- a/net/rfkill/rfkill-input.c
+++ b/net/rfkill/rfkill-input.c
@@ -23,138 +23,291 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("Input layer to RF switch connector");
MODULE_LICENSE("GPL");
+enum rfkill_input_master_mode {
+ RFKILL_INPUT_MASTER_DONOTHING = 0,
+ RFKILL_INPUT_MASTER_RESTORE = 1,
+ RFKILL_INPUT_MASTER_UNBLOCKALL = 2,
+ RFKILL_INPUT_MASTER_MAX, /* marker */
+};
+
+static enum rfkill_input_master_mode rfkill_master_switch_mode =
+ RFKILL_INPUT_MASTER_UNBLOCKALL;
+module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
+MODULE_PARM_DESC(master_switch_mode,
+ "SW_RFKILL_ALL ON should: 0=do nothing; 1=restore; 2=unblock all");
+
+enum rfkill_global_sched_op {
+ RFKILL_GLOBAL_OP_EPO = 0,
+ RFKILL_GLOBAL_OP_RESTORE,
+ RFKILL_GLOBAL_OP_UNLOCK,
+ RFKILL_GLOBAL_OP_UNBLOCK,
+};
+
+/*
+ * Currently, the code marked with RFKILL_NEED_SWSET is inactive.
+ * If handling of EV_SW SW_WLAN/WWAN/BLUETOOTH/etc is needed in the
+ * future, when such events are added, that code will be necessary.
+ */
+
struct rfkill_task {
struct work_struct work;
- enum rfkill_type type;
- struct mutex mutex; /* ensures that task is serialized */
- spinlock_t lock; /* for accessing last and desired state */
- unsigned long last; /* last schedule */
- enum rfkill_state desired_state; /* on/off */
+
+ /* ensures that task is serialized */
+ struct mutex mutex;
+
+ /* protects everything below */
+ spinlock_t lock;
+
+ /* pending regular switch operations (1=pending) */
+ unsigned long sw_pending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+
+#ifdef RFKILL_NEED_SWSET
+ /* set operation pending (1=pending) */
+ unsigned long sw_setpending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+
+ /* desired state for pending set operation (1=unblock) */
+ unsigned long sw_newstate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+#endif
+
+ /* should the state be complemented (1=yes) */
+ unsigned long sw_togglestate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+
+ bool global_op_pending;
+ enum rfkill_global_sched_op op;
};
-static void rfkill_task_handler(struct work_struct *work)
+static void __rfkill_handle_global_op(enum rfkill_global_sched_op op)
{
- struct rfkill_task *task = container_of(work, struct rfkill_task, work);
+ unsigned int i;
+
+ switch (op) {
+ case RFKILL_GLOBAL_OP_EPO:
+ rfkill_epo();
+ break;
+ case RFKILL_GLOBAL_OP_RESTORE:
+ rfkill_restore_states();
+ break;
+ case RFKILL_GLOBAL_OP_UNLOCK:
+ rfkill_remove_epo_lock();
+ break;
+ case RFKILL_GLOBAL_OP_UNBLOCK:
+ rfkill_remove_epo_lock();
+ for (i = 0; i < RFKILL_TYPE_MAX; i++)
+ rfkill_switch_all(i, RFKILL_STATE_UNBLOCKED);
+ break;
+ default:
+ /* memory corruption or bug, fail safely */
+ rfkill_epo();
+ WARN(1, "Unknown requested operation %d! "
+ "rfkill Emergency Power Off activated\n",
+ op);
+ }
+}
- mutex_lock(&task->mutex);
+#ifdef RFKILL_NEED_SWSET
+static void __rfkill_handle_normal_op(const enum rfkill_type type,
+ const bool sp, const bool s, const bool c)
+{
+ enum rfkill_state state;
- rfkill_switch_all(task->type, task->desired_state);
+ if (sp)
+ state = (s) ? RFKILL_STATE_UNBLOCKED :
+ RFKILL_STATE_SOFT_BLOCKED;
+ else
+ state = rfkill_get_global_state(type);
- mutex_unlock(&task->mutex);
+ if (c)
+ state = rfkill_state_complement(state);
+
+ rfkill_switch_all(type, state);
}
+#else
+static void __rfkill_handle_normal_op(const enum rfkill_type type,
+ const bool c)
+{
+ enum rfkill_state state;
+
+ state = rfkill_get_global_state(type);
+ if (c)
+ state = rfkill_state_complement(state);
+
+ rfkill_switch_all(type, state);
+}
+#endif
-static void rfkill_task_epo_handler(struct work_struct *work)
+static void rfkill_task_handler(struct work_struct *work)
{
- rfkill_epo();
+ struct rfkill_task *task =
+ container_of(work, struct rfkill_task, work);
+ bool doit = true;
+
+ mutex_lock(&task->mutex);
+
+ spin_lock_irq(&task->lock);
+ while (doit) {
+ if (task->global_op_pending) {
+ enum rfkill_global_sched_op op = task->op;
+ task->global_op_pending = false;
+ memset(task->sw_pending, 0, sizeof(task->sw_pending));
+ spin_unlock_irq(&task->lock);
+
+ __rfkill_handle_global_op(op);
+
+ /* make sure we do at least one pass with
+ * !task->global_op_pending */
+ spin_lock_irq(&task->lock);
+ continue;
+ } else if (!rfkill_is_epo_lock_active()) {
+ unsigned int i = 0;
+
+ while (!task->global_op_pending &&
+ i < RFKILL_TYPE_MAX) {
+ if (test_and_clear_bit(i, task->sw_pending)) {
+ bool c;
+#ifdef RFKILL_NEED_SWSET
+ bool sp, s;
+ sp = test_and_clear_bit(i,
+ task->sw_setpending);
+ s = test_bit(i, task->sw_newstate);
+#endif
+ c = test_and_clear_bit(i,
+ task->sw_togglestate);
+ spin_unlock_irq(&task->lock);
+
+#ifdef RFKILL_NEED_SWSET
+ __rfkill_handle_normal_op(i, sp, s, c);
+#else
+ __rfkill_handle_normal_op(i, c);
+#endif
+
+ spin_lock_irq(&task->lock);
+ }
+ i++;
+ }
+ }
+ doit = task->global_op_pending;
+ }
+ spin_unlock_irq(&task->lock);
+
+ mutex_unlock(&task->mutex);
}
-static DECLARE_WORK(epo_work, rfkill_task_epo_handler);
+static struct rfkill_task rfkill_task = {
+ .work = __WORK_INITIALIZER(rfkill_task.work,
+ rfkill_task_handler),
+ .mutex = __MUTEX_INITIALIZER(rfkill_task.mutex),
+ .lock = __SPIN_LOCK_UNLOCKED(rfkill_task.lock),
+};
-static void rfkill_schedule_epo(void)
+static void rfkill_schedule_global_op(enum rfkill_global_sched_op op)
{
- schedule_work(&epo_work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rfkill_task.lock, flags);
+ rfkill_task.op = op;
+ rfkill_task.global_op_pending = true;
+ schedule_work(&rfkill_task.work);
+ spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
-static void rfkill_schedule_set(struct rfkill_task *task,
+#ifdef RFKILL_NEED_SWSET
+/* Use this if you need to add EV_SW SW_WLAN/WWAN/BLUETOOTH/etc handling */
+
+static void rfkill_schedule_set(enum rfkill_type type,
enum rfkill_state desired_state)
{
unsigned long flags;
- if (unlikely(work_pending(&epo_work)))
+ if (rfkill_is_epo_lock_active())
return;
- spin_lock_irqsave(&task->lock, flags);
-
- if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
- task->desired_state = desired_state;
- task->last = jiffies;
- schedule_work(&task->work);
+ spin_lock_irqsave(&rfkill_task.lock, flags);
+ if (!rfkill_task.global_op_pending) {
+ set_bit(type, rfkill_task.sw_pending);
+ set_bit(type, rfkill_task.sw_setpending);
+ clear_bit(type, rfkill_task.sw_togglestate);
+ if (desired_state)
+ set_bit(type, rfkill_task.sw_newstate);
+ else
+ clear_bit(type, rfkill_task.sw_newstate);
+ schedule_work(&rfkill_task.work);
}
-
- spin_unlock_irqrestore(&task->lock, flags);
+ spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
+#endif
-static void rfkill_schedule_toggle(struct rfkill_task *task)
+static void rfkill_schedule_toggle(enum rfkill_type type)
{
unsigned long flags;
- if (unlikely(work_pending(&epo_work)))
+ if (rfkill_is_epo_lock_active())
return;
- spin_lock_irqsave(&task->lock, flags);
-
- if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
- task->desired_state =
- rfkill_state_complement(task->desired_state);
- task->last = jiffies;
- schedule_work(&task->work);
+ spin_lock_irqsave(&rfkill_task.lock, flags);
+ if (!rfkill_task.global_op_pending) {
+ set_bit(type, rfkill_task.sw_pending);
+ change_bit(type, rfkill_task.sw_togglestate);
+ schedule_work(&rfkill_task.work);
}
-
- spin_unlock_irqrestore(&task->lock, flags);
+ spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
-#define DEFINE_RFKILL_TASK(n, t) \
- struct rfkill_task n = { \
- .work = __WORK_INITIALIZER(n.work, \
- rfkill_task_handler), \
- .type = t, \
- .mutex = __MUTEX_INITIALIZER(n.mutex), \
- .lock = __SPIN_LOCK_UNLOCKED(n.lock), \
- .desired_state = RFKILL_STATE_UNBLOCKED, \
- }
-
-static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN);
-static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH);
-static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB);
-static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX);
-static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN);
-
static void rfkill_schedule_evsw_rfkillall(int state)
{
- /* EVERY radio type. state != 0 means radios ON */
- /* handle EPO (emergency power off) through shortcut */
if (state) {
- rfkill_schedule_set(&rfkill_wwan,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_wimax,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_uwb,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_bt,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_wlan,
- RFKILL_STATE_UNBLOCKED);
+ switch (rfkill_master_switch_mode) {
+ case RFKILL_INPUT_MASTER_UNBLOCKALL:
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNBLOCK);
+ break;
+ case RFKILL_INPUT_MASTER_RESTORE:
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_RESTORE);
+ break;
+ case RFKILL_INPUT_MASTER_DONOTHING:
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNLOCK);
+ break;
+ default:
+ /* memory corruption or driver bug! fail safely */
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
+ WARN(1, "Unknown rfkill_master_switch_mode (%d), "
+ "driver bug or memory corruption detected!\n",
+ rfkill_master_switch_mode);
+ break;
+ }
} else
- rfkill_schedule_epo();
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
}
static void rfkill_event(struct input_handle *handle, unsigned int type,
unsigned int code, int data)
{
if (type == EV_KEY && data == 1) {
+ enum rfkill_type t;
+
switch (code) {
case KEY_WLAN:
- rfkill_schedule_toggle(&rfkill_wlan);
+ t = RFKILL_TYPE_WLAN;
break;
case KEY_BLUETOOTH:
- rfkill_schedule_toggle(&rfkill_bt);
+ t = RFKILL_TYPE_BLUETOOTH;
break;
case KEY_UWB:
- rfkill_schedule_toggle(&rfkill_uwb);
+ t = RFKILL_TYPE_UWB;
break;
case KEY_WIMAX:
- rfkill_schedule_toggle(&rfkill_wimax);
+ t = RFKILL_TYPE_WIMAX;
break;
default:
- break;
+ return;
}
+ rfkill_schedule_toggle(t);
+ return;
} else if (type == EV_SW) {
switch (code) {
case SW_RFKILL_ALL:
rfkill_schedule_evsw_rfkillall(data);
- break;
+ return;
default:
- break;
+ return;
}
}
}
@@ -255,6 +408,9 @@ static struct input_handler rfkill_handler = {
static int __init rfkill_handler_init(void)
{
+ if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX)
+ return -EINVAL;
+
return input_register_handler(&rfkill_handler);
}
@@ -262,6 +418,7 @@ static void __exit rfkill_handler_exit(void)
{
input_unregister_handler(&rfkill_handler);
flush_scheduled_work();
+ rfkill_remove_epo_lock();
}
module_init(rfkill_handler_init);
diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill-input.h
index d1e03e8..fe8df6b 100644
--- a/net/rfkill/rfkill-input.h
+++ b/net/rfkill/rfkill-input.h
@@ -14,6 +14,8 @@
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
void rfkill_epo(void);
void rfkill_restore_states(void);
+void rfkill_remove_epo_lock(void);
+bool rfkill_is_epo_lock_active(void);
enum rfkill_state rfkill_get_global_state(const enum rfkill_type type);
#endif /* __RFKILL_INPUT_H */
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
index fdf87d2..e348eab 100644
--- a/net/rfkill/rfkill.c
+++ b/net/rfkill/rfkill.c
@@ -51,6 +51,7 @@ struct rfkill_gsw_state {
static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX];
static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+static bool rfkill_epo_lock_active;
static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list);
@@ -264,11 +265,14 @@ static void __rfkill_switch_all(const enum rfkill_type type,
*
* Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state).
* Please refer to __rfkill_switch_all() for details.
+ *
+ * Does nothing if the EPO lock is active.
*/
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
{
mutex_lock(&rfkill_global_mutex);
- __rfkill_switch_all(type, state);
+ if (!rfkill_epo_lock_active)
+ __rfkill_switch_all(type, state);
mutex_unlock(&rfkill_global_mutex);
}
EXPORT_SYMBOL(rfkill_switch_all);
@@ -289,6 +293,7 @@ void rfkill_epo(void)
mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = true;
list_for_each_entry(rfkill, &rfkill_list, node) {
mutex_lock(&rfkill->mutex);
rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
@@ -317,6 +322,7 @@ void rfkill_restore_states(void)
mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = false;
for (i = 0; i < RFKILL_TYPE_MAX; i++)
__rfkill_switch_all(i, rfkill_global_states[i].default_state);
mutex_unlock(&rfkill_global_mutex);
@@ -324,6 +330,35 @@ void rfkill_restore_states(void)
EXPORT_SYMBOL_GPL(rfkill_restore_states);
/**
+ * rfkill_remove_epo_lock - unlock state changes
+ *
+ * Used by rfkill-input manually unlock state changes, when
+ * the EPO switch is deactivated.
+ */
+void rfkill_remove_epo_lock(void)
+{
+ mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = false;
+ mutex_unlock(&rfkill_global_mutex);
+}
+EXPORT_SYMBOL_GPL(rfkill_remove_epo_lock);
+
+/**
+ * rfkill_is_epo_lock_active - returns true EPO is active
+ *
+ * Returns 0 (false) if there is NOT an active EPO contidion,
+ * and 1 (true) if there is an active EPO contition, which
+ * locks all radios in one of the BLOCKED states.
+ *
+ * Can be called in atomic context.
+ */
+bool rfkill_is_epo_lock_active(void)
+{
+ return rfkill_epo_lock_active;
+}
+EXPORT_SYMBOL_GPL(rfkill_is_epo_lock_active);
+
+/**
* rfkill_get_global_state - returns global state for a type
* @type: the type to get the global state of
*
@@ -447,7 +482,12 @@ static ssize_t rfkill_state_store(struct device *dev,
error = mutex_lock_killable(&rfkill->mutex);
if (error)
return error;
- error = rfkill_toggle_radio(rfkill, state, 0);
+
+ if (!rfkill_epo_lock_active)
+ error = rfkill_toggle_radio(rfkill, state, 0);
+ else
+ error = -EPERM;
+
mutex_unlock(&rfkill->mutex);
return error ? error : count;
@@ -491,7 +531,7 @@ static ssize_t rfkill_claim_store(struct device *dev,
return error;
if (rfkill->user_claim != claim) {
- if (!claim) {
+ if (!claim && !rfkill_epo_lock_active) {
mutex_lock(&rfkill->mutex);
rfkill_toggle_radio(rfkill,
rfkill_global_states[rfkill->type].current_state,
--
1.5.6.5
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 4/5] rfkill: honour EPO state when resuming a rfkill controller
2008-10-09 21:15 [GIT PATCH] rfkill updates Henrique de Moraes Holschuh
` (2 preceding siblings ...)
2008-10-09 21:15 ` [PATCH 3/5] rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input Henrique de Moraes Holschuh
@ 2008-10-09 21:15 ` Henrique de Moraes Holschuh
2008-10-11 13:53 ` Ivo van Doorn
2008-10-09 21:15 ` [PATCH 5/5] rfkill: rate-limit rfkill-input workqueue usage (v3) Henrique de Moraes Holschuh
2008-10-11 13:53 ` [GIT PATCH] rfkill updates Ivo van Doorn
5 siblings, 1 reply; 18+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-10-09 21:15 UTC (permalink / raw)
To: John Linville
Cc: linux-wireless, Ivo van Doorn, Henrique de Moraes Holschuh,
Ivo van Doorn
rfkill_resume() would always restore the rfkill controller state to its
pre-suspend state.
Now that we know when we are under EPO, kick the rfkill controller to
SOFT_BLOCKED state instead of to its pre-suspend state when it is resumed
while EPO mode is active.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Ivo van Doorn <IvDoorn@gmail.com>
---
net/rfkill/rfkill.c | 13 +++++++++++--
1 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
index e348eab..c9180c8 100644
--- a/net/rfkill/rfkill.c
+++ b/net/rfkill/rfkill.c
@@ -581,8 +581,17 @@ static int rfkill_resume(struct device *dev)
dev->power.power_state.event = PM_EVENT_ON;
- /* restore radio state AND notify everybody */
- rfkill_toggle_radio(rfkill, rfkill->state, 1);
+ /*
+ * If we are under EPO, kick transmitter offline,
+ * otherwise restore to pre-suspend state.
+ *
+ * Issue a notification in any case
+ */
+ rfkill_toggle_radio(rfkill,
+ rfkill_epo_lock_active ?
+ RFKILL_STATE_SOFT_BLOCKED :
+ rfkill->state,
+ 1);
mutex_unlock(&rfkill->mutex);
}
--
1.5.6.5
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 5/5] rfkill: rate-limit rfkill-input workqueue usage (v3)
2008-10-09 21:15 [GIT PATCH] rfkill updates Henrique de Moraes Holschuh
` (3 preceding siblings ...)
2008-10-09 21:15 ` [PATCH 4/5] rfkill: honour EPO state when resuming a rfkill controller Henrique de Moraes Holschuh
@ 2008-10-09 21:15 ` Henrique de Moraes Holschuh
2008-10-11 13:53 ` Ivo van Doorn
2008-10-11 13:53 ` [GIT PATCH] rfkill updates Ivo van Doorn
5 siblings, 1 reply; 18+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-10-09 21:15 UTC (permalink / raw)
To: John Linville
Cc: linux-wireless, Ivo van Doorn, Henrique de Moraes Holschuh,
Ivo van Doorn, Dmitry Torokhov
Limit the number of "expensive" rfkill workqueue operations per second, in
order to not hog system resources too much when faced with a rogue source
of rfkill input events.
The old rfkill-input code (before it was refactored) had such a limit in
place. It used to drop new events that were past the rate limit. This
behaviour was not implemented as an anti-DoS measure, but rather as an
attempt to work around deficiencies in input device drivers which would
issue multiple KEY_FOO events too soon for a given key FOO (i.e. ones that
do not implement mechanical debouncing properly).
However, we can't really expect such issues to be worked around by every
input handler out there, and also by every userspace client of input
devices. It is the input device driver's responsability to do debouncing
instead of spamming the input layer with bogus events.
The new limiter code is focused only on anti-DoS behaviour, and tries to
not lose events (instead, it coalesces them when possible).
The transmitters are updated once every 200ms, maximum. Care is taken not
to delay a request to _enter_ rfkill transmitter Emergency Power Off (EPO)
mode.
If mistriggered (e.g. by a jiffies counter wrap), the code delays processing
*once* by 200ms.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Ivo van Doorn <IvDoorn@gmail.com>
Cc: Dmitry Torokhov <dtor@mail.ru>
---
net/rfkill/rfkill-input.c | 49 +++++++++++++++++++++++++++++++++++++-------
1 files changed, 41 insertions(+), 8 deletions(-)
diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c
index 4ea4e68..6ddf3ce 100644
--- a/net/rfkill/rfkill-input.c
+++ b/net/rfkill/rfkill-input.c
@@ -30,6 +30,9 @@ enum rfkill_input_master_mode {
RFKILL_INPUT_MASTER_MAX, /* marker */
};
+/* Delay (in ms) between consecutive switch ops */
+#define RFKILL_OPS_DELAY 200
+
static enum rfkill_input_master_mode rfkill_master_switch_mode =
RFKILL_INPUT_MASTER_UNBLOCKALL;
module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
@@ -50,7 +53,7 @@ enum rfkill_global_sched_op {
*/
struct rfkill_task {
- struct work_struct work;
+ struct delayed_work dwork;
/* ensures that task is serialized */
struct mutex mutex;
@@ -74,6 +77,9 @@ struct rfkill_task {
bool global_op_pending;
enum rfkill_global_sched_op op;
+
+ /* last time it was scheduled */
+ unsigned long last_scheduled;
};
static void __rfkill_handle_global_op(enum rfkill_global_sched_op op)
@@ -137,8 +143,8 @@ static void __rfkill_handle_normal_op(const enum rfkill_type type,
static void rfkill_task_handler(struct work_struct *work)
{
- struct rfkill_task *task =
- container_of(work, struct rfkill_task, work);
+ struct rfkill_task *task = container_of(work,
+ struct rfkill_task, dwork.work);
bool doit = true;
mutex_lock(&task->mutex);
@@ -193,12 +199,27 @@ static void rfkill_task_handler(struct work_struct *work)
}
static struct rfkill_task rfkill_task = {
- .work = __WORK_INITIALIZER(rfkill_task.work,
+ .dwork = __DELAYED_WORK_INITIALIZER(rfkill_task.dwork,
rfkill_task_handler),
.mutex = __MUTEX_INITIALIZER(rfkill_task.mutex),
.lock = __SPIN_LOCK_UNLOCKED(rfkill_task.lock),
};
+static unsigned long rfkill_ratelimit(const unsigned long last)
+{
+ const unsigned long delay = msecs_to_jiffies(RFKILL_OPS_DELAY);
+ return (time_after(jiffies, last + delay)) ? 0 : delay;
+}
+
+static void rfkill_schedule_ratelimited(void)
+{
+ if (!delayed_work_pending(&rfkill_task.dwork)) {
+ schedule_delayed_work(&rfkill_task.dwork,
+ rfkill_ratelimit(rfkill_task.last_scheduled));
+ rfkill_task.last_scheduled = jiffies;
+ }
+}
+
static void rfkill_schedule_global_op(enum rfkill_global_sched_op op)
{
unsigned long flags;
@@ -206,7 +227,13 @@ static void rfkill_schedule_global_op(enum rfkill_global_sched_op op)
spin_lock_irqsave(&rfkill_task.lock, flags);
rfkill_task.op = op;
rfkill_task.global_op_pending = true;
- schedule_work(&rfkill_task.work);
+ if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) {
+ /* bypass the limiter for EPO */
+ cancel_delayed_work(&rfkill_task.dwork);
+ schedule_delayed_work(&rfkill_task.dwork, 0);
+ rfkill_task.last_scheduled = jiffies;
+ } else
+ rfkill_schedule_ratelimited();
spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
@@ -230,7 +257,7 @@ static void rfkill_schedule_set(enum rfkill_type type,
set_bit(type, rfkill_task.sw_newstate);
else
clear_bit(type, rfkill_task.sw_newstate);
- schedule_work(&rfkill_task.work);
+ rfkill_schedule_ratelimited();
}
spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
@@ -247,7 +274,7 @@ static void rfkill_schedule_toggle(enum rfkill_type type)
if (!rfkill_task.global_op_pending) {
set_bit(type, rfkill_task.sw_pending);
change_bit(type, rfkill_task.sw_togglestate);
- schedule_work(&rfkill_task.work);
+ rfkill_schedule_ratelimited();
}
spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
@@ -411,13 +438,19 @@ static int __init rfkill_handler_init(void)
if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX)
return -EINVAL;
+ /*
+ * The penalty to not doing this is a possible RFKILL_OPS_DELAY delay
+ * at the first use. Acceptable, but if we can avoid it, why not?
+ */
+ rfkill_task.last_scheduled =
+ jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1;
return input_register_handler(&rfkill_handler);
}
static void __exit rfkill_handler_exit(void)
{
input_unregister_handler(&rfkill_handler);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&rfkill_task.dwork);
rfkill_remove_epo_lock();
}
--
1.5.6.5
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH 3/5] rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input
2008-10-09 21:15 ` [PATCH 3/5] rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input Henrique de Moraes Holschuh
@ 2008-10-09 21:20 ` Henrique de Moraes Holschuh
2008-10-10 0:49 ` Henrique de Moraes Holschuh
0 siblings, 1 reply; 18+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-10-09 21:20 UTC (permalink / raw)
To: John Linville; +Cc: linux-wireless, Ivo van Doorn, Dmitry Torokhov
Hmm... lots of typos in the commit log of this one, I wonder why I didn't
notice it before sending.
Oh well, I will spell-check and resend later so that we don't get so many
wordos in the commit log.
--
"One disk to rule them all, One disk to find them. One disk to bring
them all and in the darkness grind them. In the Land of Redmond
where the shadows lie." -- The Silicon Valley Tarot
Henrique Holschuh
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 3/5] rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input
2008-10-09 21:20 ` Henrique de Moraes Holschuh
@ 2008-10-10 0:49 ` Henrique de Moraes Holschuh
2008-10-11 13:53 ` Ivo van Doorn
0 siblings, 1 reply; 18+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-10-10 0:49 UTC (permalink / raw)
To: John Linville; +Cc: linux-wireless, Ivo van Doorn, Dmitry Torokhov
Add of software-based sanity to rfkill and rfkill-input so that it can
reproduce what hardware-based EPO switches do, blocking all transmitters
and locking down any further attempts to unblock them until the switch is
deactivated.
rfkill-input is responsible for issuing the EPO control requests, like
before.
While an rfkill EPO is active, all transmitters are locked to one of the
BLOCKED states and all attempts to change that through the rfkill API
(userspace and kernel) will be either ignored or return -EPERM errors.
The lock will be released upon receipt of EV_SW SW_RFKILL_ALL ON by
rfkill-input, or should modular rfkill-input be unloaded.
This makes rfkill and rfkill-input extend the operation of an existing
wireless master kill switch to all wireless devices in the system, even
those that are not under hardware or firmware control.
Since the above is the expected operational behavior for the master rfkill
switch, the EPO lock functionality is not optional.
Also, extend rfkill-input to allow for three different behaviors when it
receives an EV_SW SW_RFKILL_ALL ON input event. The user can set which
behavior he wants through the master_switch_mode parameter:
master_switch_mode = 0: EV_SW SW_RFKILL_ALL ON just unlocks rfkill
controller state changes (so that the rfkill userspace and kernel APIs can
now be used to change rfkill controller states again), but doesn't change
any of their states (so they will all remain blocked). This is the safest
mode of operation, as it requires explicit operator action to re-enable a
transmitter.
master_switch_mode = 1: EV_SW SW_RFKILL_ALL ON causes rfkill-input to
attempt to restore the system to the state before the last EV_SW
SW_RFKILL_ALL OFF event, or to the default global states if no EV_SW
SW_RFKILL_ALL OFF ever happened. This is the recommended mode of
operation for laptops.
master_switch_mode = 2: tries to unblock all rfkill controllers (i.e.
enable all transmitters) when an EV_SW SW_RFKILL_ALL ON event is received.
This is the default mode of operation, as it mimics the previous behavior
of rfkill-input.
In order to implement these features in a clean way, the entire event
handling of rfkill-input was refactored into a single worker function.
Protection against input event DoS (repeatedly firing rfkill events for
rfkill-input to process) was removed during the code refactoring. It will
be added back in a future patch.
Note that with these changes, rfkill-input doesn't need to explicitly
handle any radio types for which KEY_<radio type> or SW_<radio type> events
do not exist yet.
Code to handle EV_SW SW_{WLAN,WWAN,BLUETOOTH,WIMAX,...} was added as it
might be needed in the future (and its implementation is not that obvious),
but is currently #ifdef'd out to avoid wasting resources.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Ivo van Doorn <IvDoorn@gmail.com>
Cc: Dmitry Torokhov <dtor@mail.ru>
---
Here, typos fixed...
Documentation/rfkill.txt | 20 ++-
net/rfkill/rfkill-input.c | 301 ++++++++++++++++++++++++++++++++++-----------
net/rfkill/rfkill-input.h | 2 +
net/rfkill/rfkill.c | 46 +++++++-
4 files changed, 287 insertions(+), 82 deletions(-)
diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt
index b65f079..4d3ee31 100644
--- a/Documentation/rfkill.txt
+++ b/Documentation/rfkill.txt
@@ -191,12 +191,20 @@ Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
to tell the devices registered with the rfkill class to change
their state (i.e. translates the input layer event into real
action).
+
* rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
(power off all transmitters) in a special way: it ignores any
overrides and local state cache and forces all transmitters to the
RFKILL_STATE_SOFT_BLOCKED state (including those which are already
- supposed to be BLOCKED). Note that the opposite event (power on all
- transmitters) is handled normally.
+ supposed to be BLOCKED).
+ * rfkill EPO will remain active until rfkill-input receives an
+ EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters
+ are locked in the blocked state (rfkill will refuse to unblock them).
+ * rfkill-input implements different policies that the user can
+ select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill,
+ and either do nothing (leave transmitters blocked, but now unlocked),
+ restore the transmitters to their state before the EPO, or unblock
+ them all.
Userspace uevent handler or kernel platform-specific drivers hooked to the
rfkill notifier chain:
@@ -331,11 +339,9 @@ class to get a sysfs interface :-)
correct event for your switch/button. These events are emergency power-off
events when they are trying to turn the transmitters off. An example of an
input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill
-switch in a laptop which is NOT a hotkey, but a real switch that kills radios
-in hardware, even if the O.S. has gone to lunch. An example of an input device
-which SHOULD NOT generate *_RFKILL_ALL events by default, is any sort of hot
-key that does nothing by itself, as well as any hot key that is type-specific
-(e.g. the one for WLAN).
+switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch.
+An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
+default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
3.1 Guidelines for wireless device drivers
diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c
index e5b6955..4ea4e68 100644
--- a/net/rfkill/rfkill-input.c
+++ b/net/rfkill/rfkill-input.c
@@ -23,138 +23,291 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("Input layer to RF switch connector");
MODULE_LICENSE("GPL");
+enum rfkill_input_master_mode {
+ RFKILL_INPUT_MASTER_DONOTHING = 0,
+ RFKILL_INPUT_MASTER_RESTORE = 1,
+ RFKILL_INPUT_MASTER_UNBLOCKALL = 2,
+ RFKILL_INPUT_MASTER_MAX, /* marker */
+};
+
+static enum rfkill_input_master_mode rfkill_master_switch_mode =
+ RFKILL_INPUT_MASTER_UNBLOCKALL;
+module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
+MODULE_PARM_DESC(master_switch_mode,
+ "SW_RFKILL_ALL ON should: 0=do nothing; 1=restore; 2=unblock all");
+
+enum rfkill_global_sched_op {
+ RFKILL_GLOBAL_OP_EPO = 0,
+ RFKILL_GLOBAL_OP_RESTORE,
+ RFKILL_GLOBAL_OP_UNLOCK,
+ RFKILL_GLOBAL_OP_UNBLOCK,
+};
+
+/*
+ * Currently, the code marked with RFKILL_NEED_SWSET is inactive.
+ * If handling of EV_SW SW_WLAN/WWAN/BLUETOOTH/etc is needed in the
+ * future, when such events are added, that code will be necessary.
+ */
+
struct rfkill_task {
struct work_struct work;
- enum rfkill_type type;
- struct mutex mutex; /* ensures that task is serialized */
- spinlock_t lock; /* for accessing last and desired state */
- unsigned long last; /* last schedule */
- enum rfkill_state desired_state; /* on/off */
+
+ /* ensures that task is serialized */
+ struct mutex mutex;
+
+ /* protects everything below */
+ spinlock_t lock;
+
+ /* pending regular switch operations (1=pending) */
+ unsigned long sw_pending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+
+#ifdef RFKILL_NEED_SWSET
+ /* set operation pending (1=pending) */
+ unsigned long sw_setpending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+
+ /* desired state for pending set operation (1=unblock) */
+ unsigned long sw_newstate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+#endif
+
+ /* should the state be complemented (1=yes) */
+ unsigned long sw_togglestate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+
+ bool global_op_pending;
+ enum rfkill_global_sched_op op;
};
-static void rfkill_task_handler(struct work_struct *work)
+static void __rfkill_handle_global_op(enum rfkill_global_sched_op op)
{
- struct rfkill_task *task = container_of(work, struct rfkill_task, work);
+ unsigned int i;
+
+ switch (op) {
+ case RFKILL_GLOBAL_OP_EPO:
+ rfkill_epo();
+ break;
+ case RFKILL_GLOBAL_OP_RESTORE:
+ rfkill_restore_states();
+ break;
+ case RFKILL_GLOBAL_OP_UNLOCK:
+ rfkill_remove_epo_lock();
+ break;
+ case RFKILL_GLOBAL_OP_UNBLOCK:
+ rfkill_remove_epo_lock();
+ for (i = 0; i < RFKILL_TYPE_MAX; i++)
+ rfkill_switch_all(i, RFKILL_STATE_UNBLOCKED);
+ break;
+ default:
+ /* memory corruption or bug, fail safely */
+ rfkill_epo();
+ WARN(1, "Unknown requested operation %d! "
+ "rfkill Emergency Power Off activated\n",
+ op);
+ }
+}
- mutex_lock(&task->mutex);
+#ifdef RFKILL_NEED_SWSET
+static void __rfkill_handle_normal_op(const enum rfkill_type type,
+ const bool sp, const bool s, const bool c)
+{
+ enum rfkill_state state;
- rfkill_switch_all(task->type, task->desired_state);
+ if (sp)
+ state = (s) ? RFKILL_STATE_UNBLOCKED :
+ RFKILL_STATE_SOFT_BLOCKED;
+ else
+ state = rfkill_get_global_state(type);
- mutex_unlock(&task->mutex);
+ if (c)
+ state = rfkill_state_complement(state);
+
+ rfkill_switch_all(type, state);
}
+#else
+static void __rfkill_handle_normal_op(const enum rfkill_type type,
+ const bool c)
+{
+ enum rfkill_state state;
+
+ state = rfkill_get_global_state(type);
+ if (c)
+ state = rfkill_state_complement(state);
+
+ rfkill_switch_all(type, state);
+}
+#endif
-static void rfkill_task_epo_handler(struct work_struct *work)
+static void rfkill_task_handler(struct work_struct *work)
{
- rfkill_epo();
+ struct rfkill_task *task =
+ container_of(work, struct rfkill_task, work);
+ bool doit = true;
+
+ mutex_lock(&task->mutex);
+
+ spin_lock_irq(&task->lock);
+ while (doit) {
+ if (task->global_op_pending) {
+ enum rfkill_global_sched_op op = task->op;
+ task->global_op_pending = false;
+ memset(task->sw_pending, 0, sizeof(task->sw_pending));
+ spin_unlock_irq(&task->lock);
+
+ __rfkill_handle_global_op(op);
+
+ /* make sure we do at least one pass with
+ * !task->global_op_pending */
+ spin_lock_irq(&task->lock);
+ continue;
+ } else if (!rfkill_is_epo_lock_active()) {
+ unsigned int i = 0;
+
+ while (!task->global_op_pending &&
+ i < RFKILL_TYPE_MAX) {
+ if (test_and_clear_bit(i, task->sw_pending)) {
+ bool c;
+#ifdef RFKILL_NEED_SWSET
+ bool sp, s;
+ sp = test_and_clear_bit(i,
+ task->sw_setpending);
+ s = test_bit(i, task->sw_newstate);
+#endif
+ c = test_and_clear_bit(i,
+ task->sw_togglestate);
+ spin_unlock_irq(&task->lock);
+
+#ifdef RFKILL_NEED_SWSET
+ __rfkill_handle_normal_op(i, sp, s, c);
+#else
+ __rfkill_handle_normal_op(i, c);
+#endif
+
+ spin_lock_irq(&task->lock);
+ }
+ i++;
+ }
+ }
+ doit = task->global_op_pending;
+ }
+ spin_unlock_irq(&task->lock);
+
+ mutex_unlock(&task->mutex);
}
-static DECLARE_WORK(epo_work, rfkill_task_epo_handler);
+static struct rfkill_task rfkill_task = {
+ .work = __WORK_INITIALIZER(rfkill_task.work,
+ rfkill_task_handler),
+ .mutex = __MUTEX_INITIALIZER(rfkill_task.mutex),
+ .lock = __SPIN_LOCK_UNLOCKED(rfkill_task.lock),
+};
-static void rfkill_schedule_epo(void)
+static void rfkill_schedule_global_op(enum rfkill_global_sched_op op)
{
- schedule_work(&epo_work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rfkill_task.lock, flags);
+ rfkill_task.op = op;
+ rfkill_task.global_op_pending = true;
+ schedule_work(&rfkill_task.work);
+ spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
-static void rfkill_schedule_set(struct rfkill_task *task,
+#ifdef RFKILL_NEED_SWSET
+/* Use this if you need to add EV_SW SW_WLAN/WWAN/BLUETOOTH/etc handling */
+
+static void rfkill_schedule_set(enum rfkill_type type,
enum rfkill_state desired_state)
{
unsigned long flags;
- if (unlikely(work_pending(&epo_work)))
+ if (rfkill_is_epo_lock_active())
return;
- spin_lock_irqsave(&task->lock, flags);
-
- if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
- task->desired_state = desired_state;
- task->last = jiffies;
- schedule_work(&task->work);
+ spin_lock_irqsave(&rfkill_task.lock, flags);
+ if (!rfkill_task.global_op_pending) {
+ set_bit(type, rfkill_task.sw_pending);
+ set_bit(type, rfkill_task.sw_setpending);
+ clear_bit(type, rfkill_task.sw_togglestate);
+ if (desired_state)
+ set_bit(type, rfkill_task.sw_newstate);
+ else
+ clear_bit(type, rfkill_task.sw_newstate);
+ schedule_work(&rfkill_task.work);
}
-
- spin_unlock_irqrestore(&task->lock, flags);
+ spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
+#endif
-static void rfkill_schedule_toggle(struct rfkill_task *task)
+static void rfkill_schedule_toggle(enum rfkill_type type)
{
unsigned long flags;
- if (unlikely(work_pending(&epo_work)))
+ if (rfkill_is_epo_lock_active())
return;
- spin_lock_irqsave(&task->lock, flags);
-
- if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
- task->desired_state =
- rfkill_state_complement(task->desired_state);
- task->last = jiffies;
- schedule_work(&task->work);
+ spin_lock_irqsave(&rfkill_task.lock, flags);
+ if (!rfkill_task.global_op_pending) {
+ set_bit(type, rfkill_task.sw_pending);
+ change_bit(type, rfkill_task.sw_togglestate);
+ schedule_work(&rfkill_task.work);
}
-
- spin_unlock_irqrestore(&task->lock, flags);
+ spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
-#define DEFINE_RFKILL_TASK(n, t) \
- struct rfkill_task n = { \
- .work = __WORK_INITIALIZER(n.work, \
- rfkill_task_handler), \
- .type = t, \
- .mutex = __MUTEX_INITIALIZER(n.mutex), \
- .lock = __SPIN_LOCK_UNLOCKED(n.lock), \
- .desired_state = RFKILL_STATE_UNBLOCKED, \
- }
-
-static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN);
-static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH);
-static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB);
-static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX);
-static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN);
-
static void rfkill_schedule_evsw_rfkillall(int state)
{
- /* EVERY radio type. state != 0 means radios ON */
- /* handle EPO (emergency power off) through shortcut */
if (state) {
- rfkill_schedule_set(&rfkill_wwan,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_wimax,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_uwb,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_bt,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_wlan,
- RFKILL_STATE_UNBLOCKED);
+ switch (rfkill_master_switch_mode) {
+ case RFKILL_INPUT_MASTER_UNBLOCKALL:
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNBLOCK);
+ break;
+ case RFKILL_INPUT_MASTER_RESTORE:
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_RESTORE);
+ break;
+ case RFKILL_INPUT_MASTER_DONOTHING:
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNLOCK);
+ break;
+ default:
+ /* memory corruption or driver bug! fail safely */
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
+ WARN(1, "Unknown rfkill_master_switch_mode (%d), "
+ "driver bug or memory corruption detected!\n",
+ rfkill_master_switch_mode);
+ break;
+ }
} else
- rfkill_schedule_epo();
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
}
static void rfkill_event(struct input_handle *handle, unsigned int type,
unsigned int code, int data)
{
if (type == EV_KEY && data == 1) {
+ enum rfkill_type t;
+
switch (code) {
case KEY_WLAN:
- rfkill_schedule_toggle(&rfkill_wlan);
+ t = RFKILL_TYPE_WLAN;
break;
case KEY_BLUETOOTH:
- rfkill_schedule_toggle(&rfkill_bt);
+ t = RFKILL_TYPE_BLUETOOTH;
break;
case KEY_UWB:
- rfkill_schedule_toggle(&rfkill_uwb);
+ t = RFKILL_TYPE_UWB;
break;
case KEY_WIMAX:
- rfkill_schedule_toggle(&rfkill_wimax);
+ t = RFKILL_TYPE_WIMAX;
break;
default:
- break;
+ return;
}
+ rfkill_schedule_toggle(t);
+ return;
} else if (type == EV_SW) {
switch (code) {
case SW_RFKILL_ALL:
rfkill_schedule_evsw_rfkillall(data);
- break;
+ return;
default:
- break;
+ return;
}
}
}
@@ -255,6 +408,9 @@ static struct input_handler rfkill_handler = {
static int __init rfkill_handler_init(void)
{
+ if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX)
+ return -EINVAL;
+
return input_register_handler(&rfkill_handler);
}
@@ -262,6 +418,7 @@ static void __exit rfkill_handler_exit(void)
{
input_unregister_handler(&rfkill_handler);
flush_scheduled_work();
+ rfkill_remove_epo_lock();
}
module_init(rfkill_handler_init);
diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill-input.h
index d1e03e8..fe8df6b 100644
--- a/net/rfkill/rfkill-input.h
+++ b/net/rfkill/rfkill-input.h
@@ -14,6 +14,8 @@
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
void rfkill_epo(void);
void rfkill_restore_states(void);
+void rfkill_remove_epo_lock(void);
+bool rfkill_is_epo_lock_active(void);
enum rfkill_state rfkill_get_global_state(const enum rfkill_type type);
#endif /* __RFKILL_INPUT_H */
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
index fdf87d2..e348eab 100644
--- a/net/rfkill/rfkill.c
+++ b/net/rfkill/rfkill.c
@@ -51,6 +51,7 @@ struct rfkill_gsw_state {
static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX];
static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+static bool rfkill_epo_lock_active;
static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list);
@@ -264,11 +265,14 @@ static void __rfkill_switch_all(const enum rfkill_type type,
*
* Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state).
* Please refer to __rfkill_switch_all() for details.
+ *
+ * Does nothing if the EPO lock is active.
*/
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
{
mutex_lock(&rfkill_global_mutex);
- __rfkill_switch_all(type, state);
+ if (!rfkill_epo_lock_active)
+ __rfkill_switch_all(type, state);
mutex_unlock(&rfkill_global_mutex);
}
EXPORT_SYMBOL(rfkill_switch_all);
@@ -289,6 +293,7 @@ void rfkill_epo(void)
mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = true;
list_for_each_entry(rfkill, &rfkill_list, node) {
mutex_lock(&rfkill->mutex);
rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
@@ -317,6 +322,7 @@ void rfkill_restore_states(void)
mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = false;
for (i = 0; i < RFKILL_TYPE_MAX; i++)
__rfkill_switch_all(i, rfkill_global_states[i].default_state);
mutex_unlock(&rfkill_global_mutex);
@@ -324,6 +330,35 @@ void rfkill_restore_states(void)
EXPORT_SYMBOL_GPL(rfkill_restore_states);
/**
+ * rfkill_remove_epo_lock - unlock state changes
+ *
+ * Used by rfkill-input manually unlock state changes, when
+ * the EPO switch is deactivated.
+ */
+void rfkill_remove_epo_lock(void)
+{
+ mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = false;
+ mutex_unlock(&rfkill_global_mutex);
+}
+EXPORT_SYMBOL_GPL(rfkill_remove_epo_lock);
+
+/**
+ * rfkill_is_epo_lock_active - returns true EPO is active
+ *
+ * Returns 0 (false) if there is NOT an active EPO contidion,
+ * and 1 (true) if there is an active EPO contition, which
+ * locks all radios in one of the BLOCKED states.
+ *
+ * Can be called in atomic context.
+ */
+bool rfkill_is_epo_lock_active(void)
+{
+ return rfkill_epo_lock_active;
+}
+EXPORT_SYMBOL_GPL(rfkill_is_epo_lock_active);
+
+/**
* rfkill_get_global_state - returns global state for a type
* @type: the type to get the global state of
*
@@ -447,7 +482,12 @@ static ssize_t rfkill_state_store(struct device *dev,
error = mutex_lock_killable(&rfkill->mutex);
if (error)
return error;
- error = rfkill_toggle_radio(rfkill, state, 0);
+
+ if (!rfkill_epo_lock_active)
+ error = rfkill_toggle_radio(rfkill, state, 0);
+ else
+ error = -EPERM;
+
mutex_unlock(&rfkill->mutex);
return error ? error : count;
@@ -491,7 +531,7 @@ static ssize_t rfkill_claim_store(struct device *dev,
return error;
if (rfkill->user_claim != claim) {
- if (!claim) {
+ if (!claim && !rfkill_epo_lock_active) {
mutex_lock(&rfkill->mutex);
rfkill_toggle_radio(rfkill,
rfkill_global_states[rfkill->type].current_state,
--
1.5.6.5
--
"One disk to rule them all, One disk to find them. One disk to bring
them all and in the darkness grind them. In the Land of Redmond
where the shadows lie." -- The Silicon Valley Tarot
Henrique Holschuh
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [GIT PATCH] rfkill updates
2008-10-09 21:15 [GIT PATCH] rfkill updates Henrique de Moraes Holschuh
` (4 preceding siblings ...)
2008-10-09 21:15 ` [PATCH 5/5] rfkill: rate-limit rfkill-input workqueue usage (v3) Henrique de Moraes Holschuh
@ 2008-10-11 13:53 ` Ivo van Doorn
5 siblings, 0 replies; 18+ messages in thread
From: Ivo van Doorn @ 2008-10-11 13:53 UTC (permalink / raw)
To: Henrique de Moraes Holschuh; +Cc: John Linville, linux-wireless
Hi,
> This patchset has the current stack of changes I am comfortable enough to
> propose for merging.
>
> There is some other stuff in the burner, but since they are userspace-ABI
> related and I am not sure I am happy with it yet, I want to think a bit
> more before sending them to the list as an RFC patchset.
>
> I don't really care if this patch set make it to 2.6.28 merge window or
> not, they're mostly enhancements... still, I would appreciate comments
> (and if they are good enough already, ACKs and maybe even a merge into
> wireless-testing).
2.6.27 has been released so these will be for 2.6.29,
it seems they were already merged into linux-wireless,
but for completeness here are my acks.
Ivo
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 1/5] rfkill: use killable locks instead of interruptible
2008-10-09 21:15 ` [PATCH 1/5] rfkill: use killable locks instead of interruptible Henrique de Moraes Holschuh
@ 2008-10-11 13:53 ` Ivo van Doorn
0 siblings, 0 replies; 18+ messages in thread
From: Ivo van Doorn @ 2008-10-11 13:53 UTC (permalink / raw)
To: Henrique de Moraes Holschuh; +Cc: John Linville, linux-wireless
On Thursday 09 October 2008, Henrique de Moraes Holschuh wrote:
> Apparently, many applications don't expect to get EAGAIN from fd read/write
> operations, since POSIX doesn't mandate it.
>
> Use mutex_lock_killable instead of mutex_lock_interruptible, which won't
> cause issues.
>
> Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
> Cc: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Ivo van Doorn <IvDoorn@gmail.com>
> ---
> net/rfkill/rfkill.c | 7 ++++---
> 1 files changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
> index f949a48..08be968 100644
> --- a/net/rfkill/rfkill.c
> +++ b/net/rfkill/rfkill.c
> @@ -431,8 +431,9 @@ static ssize_t rfkill_state_store(struct device *dev,
> state != RFKILL_STATE_SOFT_BLOCKED)
> return -EINVAL;
>
> - if (mutex_lock_interruptible(&rfkill->mutex))
> - return -ERESTARTSYS;
> + error = mutex_lock_killable(&rfkill->mutex);
> + if (error)
> + return error;
> error = rfkill_toggle_radio(rfkill, state, 0);
> mutex_unlock(&rfkill->mutex);
>
> @@ -472,7 +473,7 @@ static ssize_t rfkill_claim_store(struct device *dev,
> * Take the global lock to make sure the kernel is not in
> * the middle of rfkill_switch_all
> */
> - error = mutex_lock_interruptible(&rfkill_global_mutex);
> + error = mutex_lock_killable(&rfkill_global_mutex);
> if (error)
> return error;
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/5] rfkill: export global states to rfkill-input
2008-10-09 21:15 ` [PATCH 2/5] rfkill: export global states to rfkill-input Henrique de Moraes Holschuh
@ 2008-10-11 13:53 ` Ivo van Doorn
0 siblings, 0 replies; 18+ messages in thread
From: Ivo van Doorn @ 2008-10-11 13:53 UTC (permalink / raw)
To: Henrique de Moraes Holschuh; +Cc: John Linville, linux-wireless
On Thursday 09 October 2008, Henrique de Moraes Holschuh wrote:
> Export the the global switch states to rfkill-input. This is needed to
> properly implement KEY_* handling without disregarding the initial state.
>
> Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
> Cc: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Ivo van Doorn <IvDoorn@gmail.com>
> ---
> net/rfkill/rfkill-input.h | 1 +
> net/rfkill/rfkill.c | 14 ++++++++++++++
> 2 files changed, 15 insertions(+), 0 deletions(-)
>
> diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill-input.h
> index bbfa646..d1e03e8 100644
> --- a/net/rfkill/rfkill-input.h
> +++ b/net/rfkill/rfkill-input.h
> @@ -14,5 +14,6 @@
> void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
> void rfkill_epo(void);
> void rfkill_restore_states(void);
> +enum rfkill_state rfkill_get_global_state(const enum rfkill_type type);
>
> #endif /* __RFKILL_INPUT_H */
> diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
> index 08be968..fdf87d2 100644
> --- a/net/rfkill/rfkill.c
> +++ b/net/rfkill/rfkill.c
> @@ -324,6 +324,19 @@ void rfkill_restore_states(void)
> EXPORT_SYMBOL_GPL(rfkill_restore_states);
>
> /**
> + * rfkill_get_global_state - returns global state for a type
> + * @type: the type to get the global state of
> + *
> + * Returns the current global state for a given wireless
> + * device type.
> + */
> +enum rfkill_state rfkill_get_global_state(const enum rfkill_type type)
> +{
> + return rfkill_global_states[type].current_state;
> +}
> +EXPORT_SYMBOL_GPL(rfkill_get_global_state);
> +
> +/**
> * rfkill_force_state - Force the internal rfkill radio state
> * @rfkill: pointer to the rfkill class to modify.
> * @state: the current radio state the class should be forced to.
> @@ -834,6 +847,7 @@ int rfkill_set_default(enum rfkill_type type, enum rfkill_state state)
>
> if (!test_and_set_bit(type, rfkill_states_lockdflt)) {
> rfkill_global_states[type].default_state = state;
> + rfkill_global_states[type].current_state = state;
> error = 0;
> } else
> error = -EPERM;
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 3/5] rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input
2008-10-10 0:49 ` Henrique de Moraes Holschuh
@ 2008-10-11 13:53 ` Ivo van Doorn
2008-10-12 1:44 ` Henrique de Moraes Holschuh
0 siblings, 1 reply; 18+ messages in thread
From: Ivo van Doorn @ 2008-10-11 13:53 UTC (permalink / raw)
To: Henrique de Moraes Holschuh
Cc: John Linville, linux-wireless, Dmitry Torokhov
On Friday 10 October 2008, Henrique de Moraes Holschuh wrote:
> Add of software-based sanity to rfkill and rfkill-input so that it can
> reproduce what hardware-based EPO switches do, blocking all transmitters
> and locking down any further attempts to unblock them until the switch is
> deactivated.
>
> rfkill-input is responsible for issuing the EPO control requests, like
> before.
>
> While an rfkill EPO is active, all transmitters are locked to one of the
> BLOCKED states and all attempts to change that through the rfkill API
> (userspace and kernel) will be either ignored or return -EPERM errors.
>
> The lock will be released upon receipt of EV_SW SW_RFKILL_ALL ON by
> rfkill-input, or should modular rfkill-input be unloaded.
>
> This makes rfkill and rfkill-input extend the operation of an existing
> wireless master kill switch to all wireless devices in the system, even
> those that are not under hardware or firmware control.
>
> Since the above is the expected operational behavior for the master rfkill
> switch, the EPO lock functionality is not optional.
>
> Also, extend rfkill-input to allow for three different behaviors when it
> receives an EV_SW SW_RFKILL_ALL ON input event. The user can set which
> behavior he wants through the master_switch_mode parameter:
>
> master_switch_mode = 0: EV_SW SW_RFKILL_ALL ON just unlocks rfkill
> controller state changes (so that the rfkill userspace and kernel APIs can
> now be used to change rfkill controller states again), but doesn't change
> any of their states (so they will all remain blocked). This is the safest
> mode of operation, as it requires explicit operator action to re-enable a
> transmitter.
>
> master_switch_mode = 1: EV_SW SW_RFKILL_ALL ON causes rfkill-input to
> attempt to restore the system to the state before the last EV_SW
> SW_RFKILL_ALL OFF event, or to the default global states if no EV_SW
> SW_RFKILL_ALL OFF ever happened. This is the recommended mode of
> operation for laptops.
>
> master_switch_mode = 2: tries to unblock all rfkill controllers (i.e.
> enable all transmitters) when an EV_SW SW_RFKILL_ALL ON event is received.
> This is the default mode of operation, as it mimics the previous behavior
> of rfkill-input.
>
> In order to implement these features in a clean way, the entire event
> handling of rfkill-input was refactored into a single worker function.
>
> Protection against input event DoS (repeatedly firing rfkill events for
> rfkill-input to process) was removed during the code refactoring. It will
> be added back in a future patch.
>
> Note that with these changes, rfkill-input doesn't need to explicitly
> handle any radio types for which KEY_<radio type> or SW_<radio type> events
> do not exist yet.
>
> Code to handle EV_SW SW_{WLAN,WWAN,BLUETOOTH,WIMAX,...} was added as it
> might be needed in the future (and its implementation is not that obvious),
> but is currently #ifdef'd out to avoid wasting resources.
>
> Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
> Cc: Ivo van Doorn <IvDoorn@gmail.com>
> Cc: Dmitry Torokhov <dtor@mail.ru>
Although I think RFKILL_NEED_SWSET could be removed entirely if it isn't used anyway,
we might do that later.
Acked-by: Ivo van Doorn <IvDoorn@gmail.com>
> ---
> Here, typos fixed...
>
> Documentation/rfkill.txt | 20 ++-
> net/rfkill/rfkill-input.c | 301 ++++++++++++++++++++++++++++++++++-----------
> net/rfkill/rfkill-input.h | 2 +
> net/rfkill/rfkill.c | 46 +++++++-
> 4 files changed, 287 insertions(+), 82 deletions(-)
>
> diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt
> index b65f079..4d3ee31 100644
> --- a/Documentation/rfkill.txt
> +++ b/Documentation/rfkill.txt
> @@ -191,12 +191,20 @@ Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
> to tell the devices registered with the rfkill class to change
> their state (i.e. translates the input layer event into real
> action).
> +
> * rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
> (power off all transmitters) in a special way: it ignores any
> overrides and local state cache and forces all transmitters to the
> RFKILL_STATE_SOFT_BLOCKED state (including those which are already
> - supposed to be BLOCKED). Note that the opposite event (power on all
> - transmitters) is handled normally.
> + supposed to be BLOCKED).
> + * rfkill EPO will remain active until rfkill-input receives an
> + EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters
> + are locked in the blocked state (rfkill will refuse to unblock them).
> + * rfkill-input implements different policies that the user can
> + select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill,
> + and either do nothing (leave transmitters blocked, but now unlocked),
> + restore the transmitters to their state before the EPO, or unblock
> + them all.
>
> Userspace uevent handler or kernel platform-specific drivers hooked to the
> rfkill notifier chain:
> @@ -331,11 +339,9 @@ class to get a sysfs interface :-)
> correct event for your switch/button. These events are emergency power-off
> events when they are trying to turn the transmitters off. An example of an
> input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill
> -switch in a laptop which is NOT a hotkey, but a real switch that kills radios
> -in hardware, even if the O.S. has gone to lunch. An example of an input device
> -which SHOULD NOT generate *_RFKILL_ALL events by default, is any sort of hot
> -key that does nothing by itself, as well as any hot key that is type-specific
> -(e.g. the one for WLAN).
> +switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch.
> +An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
> +default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
>
>
> 3.1 Guidelines for wireless device drivers
> diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c
> index e5b6955..4ea4e68 100644
> --- a/net/rfkill/rfkill-input.c
> +++ b/net/rfkill/rfkill-input.c
> @@ -23,138 +23,291 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
> MODULE_DESCRIPTION("Input layer to RF switch connector");
> MODULE_LICENSE("GPL");
>
> +enum rfkill_input_master_mode {
> + RFKILL_INPUT_MASTER_DONOTHING = 0,
> + RFKILL_INPUT_MASTER_RESTORE = 1,
> + RFKILL_INPUT_MASTER_UNBLOCKALL = 2,
> + RFKILL_INPUT_MASTER_MAX, /* marker */
> +};
> +
> +static enum rfkill_input_master_mode rfkill_master_switch_mode =
> + RFKILL_INPUT_MASTER_UNBLOCKALL;
> +module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
> +MODULE_PARM_DESC(master_switch_mode,
> + "SW_RFKILL_ALL ON should: 0=do nothing; 1=restore; 2=unblock all");
> +
> +enum rfkill_global_sched_op {
> + RFKILL_GLOBAL_OP_EPO = 0,
> + RFKILL_GLOBAL_OP_RESTORE,
> + RFKILL_GLOBAL_OP_UNLOCK,
> + RFKILL_GLOBAL_OP_UNBLOCK,
> +};
> +
> +/*
> + * Currently, the code marked with RFKILL_NEED_SWSET is inactive.
> + * If handling of EV_SW SW_WLAN/WWAN/BLUETOOTH/etc is needed in the
> + * future, when such events are added, that code will be necessary.
> + */
> +
> struct rfkill_task {
> struct work_struct work;
> - enum rfkill_type type;
> - struct mutex mutex; /* ensures that task is serialized */
> - spinlock_t lock; /* for accessing last and desired state */
> - unsigned long last; /* last schedule */
> - enum rfkill_state desired_state; /* on/off */
> +
> + /* ensures that task is serialized */
> + struct mutex mutex;
> +
> + /* protects everything below */
> + spinlock_t lock;
> +
> + /* pending regular switch operations (1=pending) */
> + unsigned long sw_pending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
> +
> +#ifdef RFKILL_NEED_SWSET
> + /* set operation pending (1=pending) */
> + unsigned long sw_setpending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
> +
> + /* desired state for pending set operation (1=unblock) */
> + unsigned long sw_newstate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
> +#endif
> +
> + /* should the state be complemented (1=yes) */
> + unsigned long sw_togglestate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
> +
> + bool global_op_pending;
> + enum rfkill_global_sched_op op;
> };
>
> -static void rfkill_task_handler(struct work_struct *work)
> +static void __rfkill_handle_global_op(enum rfkill_global_sched_op op)
> {
> - struct rfkill_task *task = container_of(work, struct rfkill_task, work);
> + unsigned int i;
> +
> + switch (op) {
> + case RFKILL_GLOBAL_OP_EPO:
> + rfkill_epo();
> + break;
> + case RFKILL_GLOBAL_OP_RESTORE:
> + rfkill_restore_states();
> + break;
> + case RFKILL_GLOBAL_OP_UNLOCK:
> + rfkill_remove_epo_lock();
> + break;
> + case RFKILL_GLOBAL_OP_UNBLOCK:
> + rfkill_remove_epo_lock();
> + for (i = 0; i < RFKILL_TYPE_MAX; i++)
> + rfkill_switch_all(i, RFKILL_STATE_UNBLOCKED);
> + break;
> + default:
> + /* memory corruption or bug, fail safely */
> + rfkill_epo();
> + WARN(1, "Unknown requested operation %d! "
> + "rfkill Emergency Power Off activated\n",
> + op);
> + }
> +}
>
> - mutex_lock(&task->mutex);
> +#ifdef RFKILL_NEED_SWSET
> +static void __rfkill_handle_normal_op(const enum rfkill_type type,
> + const bool sp, const bool s, const bool c)
> +{
> + enum rfkill_state state;
>
> - rfkill_switch_all(task->type, task->desired_state);
> + if (sp)
> + state = (s) ? RFKILL_STATE_UNBLOCKED :
> + RFKILL_STATE_SOFT_BLOCKED;
> + else
> + state = rfkill_get_global_state(type);
>
> - mutex_unlock(&task->mutex);
> + if (c)
> + state = rfkill_state_complement(state);
> +
> + rfkill_switch_all(type, state);
> }
> +#else
> +static void __rfkill_handle_normal_op(const enum rfkill_type type,
> + const bool c)
> +{
> + enum rfkill_state state;
> +
> + state = rfkill_get_global_state(type);
> + if (c)
> + state = rfkill_state_complement(state);
> +
> + rfkill_switch_all(type, state);
> +}
> +#endif
>
> -static void rfkill_task_epo_handler(struct work_struct *work)
> +static void rfkill_task_handler(struct work_struct *work)
> {
> - rfkill_epo();
> + struct rfkill_task *task =
> + container_of(work, struct rfkill_task, work);
> + bool doit = true;
> +
> + mutex_lock(&task->mutex);
> +
> + spin_lock_irq(&task->lock);
> + while (doit) {
> + if (task->global_op_pending) {
> + enum rfkill_global_sched_op op = task->op;
> + task->global_op_pending = false;
> + memset(task->sw_pending, 0, sizeof(task->sw_pending));
> + spin_unlock_irq(&task->lock);
> +
> + __rfkill_handle_global_op(op);
> +
> + /* make sure we do at least one pass with
> + * !task->global_op_pending */
> + spin_lock_irq(&task->lock);
> + continue;
> + } else if (!rfkill_is_epo_lock_active()) {
> + unsigned int i = 0;
> +
> + while (!task->global_op_pending &&
> + i < RFKILL_TYPE_MAX) {
> + if (test_and_clear_bit(i, task->sw_pending)) {
> + bool c;
> +#ifdef RFKILL_NEED_SWSET
> + bool sp, s;
> + sp = test_and_clear_bit(i,
> + task->sw_setpending);
> + s = test_bit(i, task->sw_newstate);
> +#endif
> + c = test_and_clear_bit(i,
> + task->sw_togglestate);
> + spin_unlock_irq(&task->lock);
> +
> +#ifdef RFKILL_NEED_SWSET
> + __rfkill_handle_normal_op(i, sp, s, c);
> +#else
> + __rfkill_handle_normal_op(i, c);
> +#endif
> +
> + spin_lock_irq(&task->lock);
> + }
> + i++;
> + }
> + }
> + doit = task->global_op_pending;
> + }
> + spin_unlock_irq(&task->lock);
> +
> + mutex_unlock(&task->mutex);
> }
>
> -static DECLARE_WORK(epo_work, rfkill_task_epo_handler);
> +static struct rfkill_task rfkill_task = {
> + .work = __WORK_INITIALIZER(rfkill_task.work,
> + rfkill_task_handler),
> + .mutex = __MUTEX_INITIALIZER(rfkill_task.mutex),
> + .lock = __SPIN_LOCK_UNLOCKED(rfkill_task.lock),
> +};
>
> -static void rfkill_schedule_epo(void)
> +static void rfkill_schedule_global_op(enum rfkill_global_sched_op op)
> {
> - schedule_work(&epo_work);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&rfkill_task.lock, flags);
> + rfkill_task.op = op;
> + rfkill_task.global_op_pending = true;
> + schedule_work(&rfkill_task.work);
> + spin_unlock_irqrestore(&rfkill_task.lock, flags);
> }
>
> -static void rfkill_schedule_set(struct rfkill_task *task,
> +#ifdef RFKILL_NEED_SWSET
> +/* Use this if you need to add EV_SW SW_WLAN/WWAN/BLUETOOTH/etc handling */
> +
> +static void rfkill_schedule_set(enum rfkill_type type,
> enum rfkill_state desired_state)
> {
> unsigned long flags;
>
> - if (unlikely(work_pending(&epo_work)))
> + if (rfkill_is_epo_lock_active())
> return;
>
> - spin_lock_irqsave(&task->lock, flags);
> -
> - if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
> - task->desired_state = desired_state;
> - task->last = jiffies;
> - schedule_work(&task->work);
> + spin_lock_irqsave(&rfkill_task.lock, flags);
> + if (!rfkill_task.global_op_pending) {
> + set_bit(type, rfkill_task.sw_pending);
> + set_bit(type, rfkill_task.sw_setpending);
> + clear_bit(type, rfkill_task.sw_togglestate);
> + if (desired_state)
> + set_bit(type, rfkill_task.sw_newstate);
> + else
> + clear_bit(type, rfkill_task.sw_newstate);
> + schedule_work(&rfkill_task.work);
> }
> -
> - spin_unlock_irqrestore(&task->lock, flags);
> + spin_unlock_irqrestore(&rfkill_task.lock, flags);
> }
> +#endif
>
> -static void rfkill_schedule_toggle(struct rfkill_task *task)
> +static void rfkill_schedule_toggle(enum rfkill_type type)
> {
> unsigned long flags;
>
> - if (unlikely(work_pending(&epo_work)))
> + if (rfkill_is_epo_lock_active())
> return;
>
> - spin_lock_irqsave(&task->lock, flags);
> -
> - if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
> - task->desired_state =
> - rfkill_state_complement(task->desired_state);
> - task->last = jiffies;
> - schedule_work(&task->work);
> + spin_lock_irqsave(&rfkill_task.lock, flags);
> + if (!rfkill_task.global_op_pending) {
> + set_bit(type, rfkill_task.sw_pending);
> + change_bit(type, rfkill_task.sw_togglestate);
> + schedule_work(&rfkill_task.work);
> }
> -
> - spin_unlock_irqrestore(&task->lock, flags);
> + spin_unlock_irqrestore(&rfkill_task.lock, flags);
> }
>
> -#define DEFINE_RFKILL_TASK(n, t) \
> - struct rfkill_task n = { \
> - .work = __WORK_INITIALIZER(n.work, \
> - rfkill_task_handler), \
> - .type = t, \
> - .mutex = __MUTEX_INITIALIZER(n.mutex), \
> - .lock = __SPIN_LOCK_UNLOCKED(n.lock), \
> - .desired_state = RFKILL_STATE_UNBLOCKED, \
> - }
> -
> -static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN);
> -static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH);
> -static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB);
> -static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX);
> -static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN);
> -
> static void rfkill_schedule_evsw_rfkillall(int state)
> {
> - /* EVERY radio type. state != 0 means radios ON */
> - /* handle EPO (emergency power off) through shortcut */
> if (state) {
> - rfkill_schedule_set(&rfkill_wwan,
> - RFKILL_STATE_UNBLOCKED);
> - rfkill_schedule_set(&rfkill_wimax,
> - RFKILL_STATE_UNBLOCKED);
> - rfkill_schedule_set(&rfkill_uwb,
> - RFKILL_STATE_UNBLOCKED);
> - rfkill_schedule_set(&rfkill_bt,
> - RFKILL_STATE_UNBLOCKED);
> - rfkill_schedule_set(&rfkill_wlan,
> - RFKILL_STATE_UNBLOCKED);
> + switch (rfkill_master_switch_mode) {
> + case RFKILL_INPUT_MASTER_UNBLOCKALL:
> + rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNBLOCK);
> + break;
> + case RFKILL_INPUT_MASTER_RESTORE:
> + rfkill_schedule_global_op(RFKILL_GLOBAL_OP_RESTORE);
> + break;
> + case RFKILL_INPUT_MASTER_DONOTHING:
> + rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNLOCK);
> + break;
> + default:
> + /* memory corruption or driver bug! fail safely */
> + rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
> + WARN(1, "Unknown rfkill_master_switch_mode (%d), "
> + "driver bug or memory corruption detected!\n",
> + rfkill_master_switch_mode);
> + break;
> + }
> } else
> - rfkill_schedule_epo();
> + rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
> }
>
> static void rfkill_event(struct input_handle *handle, unsigned int type,
> unsigned int code, int data)
> {
> if (type == EV_KEY && data == 1) {
> + enum rfkill_type t;
> +
> switch (code) {
> case KEY_WLAN:
> - rfkill_schedule_toggle(&rfkill_wlan);
> + t = RFKILL_TYPE_WLAN;
> break;
> case KEY_BLUETOOTH:
> - rfkill_schedule_toggle(&rfkill_bt);
> + t = RFKILL_TYPE_BLUETOOTH;
> break;
> case KEY_UWB:
> - rfkill_schedule_toggle(&rfkill_uwb);
> + t = RFKILL_TYPE_UWB;
> break;
> case KEY_WIMAX:
> - rfkill_schedule_toggle(&rfkill_wimax);
> + t = RFKILL_TYPE_WIMAX;
> break;
> default:
> - break;
> + return;
> }
> + rfkill_schedule_toggle(t);
> + return;
> } else if (type == EV_SW) {
> switch (code) {
> case SW_RFKILL_ALL:
> rfkill_schedule_evsw_rfkillall(data);
> - break;
> + return;
> default:
> - break;
> + return;
> }
> }
> }
> @@ -255,6 +408,9 @@ static struct input_handler rfkill_handler = {
>
> static int __init rfkill_handler_init(void)
> {
> + if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX)
> + return -EINVAL;
> +
> return input_register_handler(&rfkill_handler);
> }
>
> @@ -262,6 +418,7 @@ static void __exit rfkill_handler_exit(void)
> {
> input_unregister_handler(&rfkill_handler);
> flush_scheduled_work();
> + rfkill_remove_epo_lock();
> }
>
> module_init(rfkill_handler_init);
> diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill-input.h
> index d1e03e8..fe8df6b 100644
> --- a/net/rfkill/rfkill-input.h
> +++ b/net/rfkill/rfkill-input.h
> @@ -14,6 +14,8 @@
> void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
> void rfkill_epo(void);
> void rfkill_restore_states(void);
> +void rfkill_remove_epo_lock(void);
> +bool rfkill_is_epo_lock_active(void);
> enum rfkill_state rfkill_get_global_state(const enum rfkill_type type);
>
> #endif /* __RFKILL_INPUT_H */
> diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
> index fdf87d2..e348eab 100644
> --- a/net/rfkill/rfkill.c
> +++ b/net/rfkill/rfkill.c
> @@ -51,6 +51,7 @@ struct rfkill_gsw_state {
>
> static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX];
> static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
> +static bool rfkill_epo_lock_active;
>
> static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list);
>
> @@ -264,11 +265,14 @@ static void __rfkill_switch_all(const enum rfkill_type type,
> *
> * Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state).
> * Please refer to __rfkill_switch_all() for details.
> + *
> + * Does nothing if the EPO lock is active.
> */
> void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
> {
> mutex_lock(&rfkill_global_mutex);
> - __rfkill_switch_all(type, state);
> + if (!rfkill_epo_lock_active)
> + __rfkill_switch_all(type, state);
> mutex_unlock(&rfkill_global_mutex);
> }
> EXPORT_SYMBOL(rfkill_switch_all);
> @@ -289,6 +293,7 @@ void rfkill_epo(void)
>
> mutex_lock(&rfkill_global_mutex);
>
> + rfkill_epo_lock_active = true;
> list_for_each_entry(rfkill, &rfkill_list, node) {
> mutex_lock(&rfkill->mutex);
> rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
> @@ -317,6 +322,7 @@ void rfkill_restore_states(void)
>
> mutex_lock(&rfkill_global_mutex);
>
> + rfkill_epo_lock_active = false;
> for (i = 0; i < RFKILL_TYPE_MAX; i++)
> __rfkill_switch_all(i, rfkill_global_states[i].default_state);
> mutex_unlock(&rfkill_global_mutex);
> @@ -324,6 +330,35 @@ void rfkill_restore_states(void)
> EXPORT_SYMBOL_GPL(rfkill_restore_states);
>
> /**
> + * rfkill_remove_epo_lock - unlock state changes
> + *
> + * Used by rfkill-input manually unlock state changes, when
> + * the EPO switch is deactivated.
> + */
> +void rfkill_remove_epo_lock(void)
> +{
> + mutex_lock(&rfkill_global_mutex);
> + rfkill_epo_lock_active = false;
> + mutex_unlock(&rfkill_global_mutex);
> +}
> +EXPORT_SYMBOL_GPL(rfkill_remove_epo_lock);
> +
> +/**
> + * rfkill_is_epo_lock_active - returns true EPO is active
> + *
> + * Returns 0 (false) if there is NOT an active EPO contidion,
> + * and 1 (true) if there is an active EPO contition, which
> + * locks all radios in one of the BLOCKED states.
> + *
> + * Can be called in atomic context.
> + */
> +bool rfkill_is_epo_lock_active(void)
> +{
> + return rfkill_epo_lock_active;
> +}
> +EXPORT_SYMBOL_GPL(rfkill_is_epo_lock_active);
> +
> +/**
> * rfkill_get_global_state - returns global state for a type
> * @type: the type to get the global state of
> *
> @@ -447,7 +482,12 @@ static ssize_t rfkill_state_store(struct device *dev,
> error = mutex_lock_killable(&rfkill->mutex);
> if (error)
> return error;
> - error = rfkill_toggle_radio(rfkill, state, 0);
> +
> + if (!rfkill_epo_lock_active)
> + error = rfkill_toggle_radio(rfkill, state, 0);
> + else
> + error = -EPERM;
> +
> mutex_unlock(&rfkill->mutex);
>
> return error ? error : count;
> @@ -491,7 +531,7 @@ static ssize_t rfkill_claim_store(struct device *dev,
> return error;
>
> if (rfkill->user_claim != claim) {
> - if (!claim) {
> + if (!claim && !rfkill_epo_lock_active) {
> mutex_lock(&rfkill->mutex);
> rfkill_toggle_radio(rfkill,
> rfkill_global_states[rfkill->type].current_state,
> --
> 1.5.6.5
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 4/5] rfkill: honour EPO state when resuming a rfkill controller
2008-10-09 21:15 ` [PATCH 4/5] rfkill: honour EPO state when resuming a rfkill controller Henrique de Moraes Holschuh
@ 2008-10-11 13:53 ` Ivo van Doorn
0 siblings, 0 replies; 18+ messages in thread
From: Ivo van Doorn @ 2008-10-11 13:53 UTC (permalink / raw)
To: Henrique de Moraes Holschuh; +Cc: John Linville, linux-wireless
On Thursday 09 October 2008, Henrique de Moraes Holschuh wrote:
> rfkill_resume() would always restore the rfkill controller state to its
> pre-suspend state.
>
> Now that we know when we are under EPO, kick the rfkill controller to
> SOFT_BLOCKED state instead of to its pre-suspend state when it is resumed
> while EPO mode is active.
>
> Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
> Cc: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Ivo van Doorn <IvDoorn@gmail.com>
> ---
> net/rfkill/rfkill.c | 13 +++++++++++--
> 1 files changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
> index e348eab..c9180c8 100644
> --- a/net/rfkill/rfkill.c
> +++ b/net/rfkill/rfkill.c
> @@ -581,8 +581,17 @@ static int rfkill_resume(struct device *dev)
>
> dev->power.power_state.event = PM_EVENT_ON;
>
> - /* restore radio state AND notify everybody */
> - rfkill_toggle_radio(rfkill, rfkill->state, 1);
> + /*
> + * If we are under EPO, kick transmitter offline,
> + * otherwise restore to pre-suspend state.
> + *
> + * Issue a notification in any case
> + */
> + rfkill_toggle_radio(rfkill,
> + rfkill_epo_lock_active ?
> + RFKILL_STATE_SOFT_BLOCKED :
> + rfkill->state,
> + 1);
>
> mutex_unlock(&rfkill->mutex);
> }
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 5/5] rfkill: rate-limit rfkill-input workqueue usage (v3)
2008-10-09 21:15 ` [PATCH 5/5] rfkill: rate-limit rfkill-input workqueue usage (v3) Henrique de Moraes Holschuh
@ 2008-10-11 13:53 ` Ivo van Doorn
0 siblings, 0 replies; 18+ messages in thread
From: Ivo van Doorn @ 2008-10-11 13:53 UTC (permalink / raw)
To: Henrique de Moraes Holschuh
Cc: John Linville, linux-wireless, Dmitry Torokhov
On Thursday 09 October 2008, Henrique de Moraes Holschuh wrote:
> Limit the number of "expensive" rfkill workqueue operations per second, in
> order to not hog system resources too much when faced with a rogue source
> of rfkill input events.
>
> The old rfkill-input code (before it was refactored) had such a limit in
> place. It used to drop new events that were past the rate limit. This
> behaviour was not implemented as an anti-DoS measure, but rather as an
> attempt to work around deficiencies in input device drivers which would
> issue multiple KEY_FOO events too soon for a given key FOO (i.e. ones that
> do not implement mechanical debouncing properly).
>
> However, we can't really expect such issues to be worked around by every
> input handler out there, and also by every userspace client of input
> devices. It is the input device driver's responsability to do debouncing
> instead of spamming the input layer with bogus events.
>
> The new limiter code is focused only on anti-DoS behaviour, and tries to
> not lose events (instead, it coalesces them when possible).
>
> The transmitters are updated once every 200ms, maximum. Care is taken not
> to delay a request to _enter_ rfkill transmitter Emergency Power Off (EPO)
> mode.
>
> If mistriggered (e.g. by a jiffies counter wrap), the code delays processing
> *once* by 200ms.
>
> Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
> Cc: Ivo van Doorn <IvDoorn@gmail.com>
> Cc: Dmitry Torokhov <dtor@mail.ru>
Acked-by: Ivo van Doorn <IvDoorn@gmail.com>
> ---
> net/rfkill/rfkill-input.c | 49 +++++++++++++++++++++++++++++++++++++-------
> 1 files changed, 41 insertions(+), 8 deletions(-)
>
> diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c
> index 4ea4e68..6ddf3ce 100644
> --- a/net/rfkill/rfkill-input.c
> +++ b/net/rfkill/rfkill-input.c
> @@ -30,6 +30,9 @@ enum rfkill_input_master_mode {
> RFKILL_INPUT_MASTER_MAX, /* marker */
> };
>
> +/* Delay (in ms) between consecutive switch ops */
> +#define RFKILL_OPS_DELAY 200
> +
> static enum rfkill_input_master_mode rfkill_master_switch_mode =
> RFKILL_INPUT_MASTER_UNBLOCKALL;
> module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
> @@ -50,7 +53,7 @@ enum rfkill_global_sched_op {
> */
>
> struct rfkill_task {
> - struct work_struct work;
> + struct delayed_work dwork;
>
> /* ensures that task is serialized */
> struct mutex mutex;
> @@ -74,6 +77,9 @@ struct rfkill_task {
>
> bool global_op_pending;
> enum rfkill_global_sched_op op;
> +
> + /* last time it was scheduled */
> + unsigned long last_scheduled;
> };
>
> static void __rfkill_handle_global_op(enum rfkill_global_sched_op op)
> @@ -137,8 +143,8 @@ static void __rfkill_handle_normal_op(const enum rfkill_type type,
>
> static void rfkill_task_handler(struct work_struct *work)
> {
> - struct rfkill_task *task =
> - container_of(work, struct rfkill_task, work);
> + struct rfkill_task *task = container_of(work,
> + struct rfkill_task, dwork.work);
> bool doit = true;
>
> mutex_lock(&task->mutex);
> @@ -193,12 +199,27 @@ static void rfkill_task_handler(struct work_struct *work)
> }
>
> static struct rfkill_task rfkill_task = {
> - .work = __WORK_INITIALIZER(rfkill_task.work,
> + .dwork = __DELAYED_WORK_INITIALIZER(rfkill_task.dwork,
> rfkill_task_handler),
> .mutex = __MUTEX_INITIALIZER(rfkill_task.mutex),
> .lock = __SPIN_LOCK_UNLOCKED(rfkill_task.lock),
> };
>
> +static unsigned long rfkill_ratelimit(const unsigned long last)
> +{
> + const unsigned long delay = msecs_to_jiffies(RFKILL_OPS_DELAY);
> + return (time_after(jiffies, last + delay)) ? 0 : delay;
> +}
> +
> +static void rfkill_schedule_ratelimited(void)
> +{
> + if (!delayed_work_pending(&rfkill_task.dwork)) {
> + schedule_delayed_work(&rfkill_task.dwork,
> + rfkill_ratelimit(rfkill_task.last_scheduled));
> + rfkill_task.last_scheduled = jiffies;
> + }
> +}
> +
> static void rfkill_schedule_global_op(enum rfkill_global_sched_op op)
> {
> unsigned long flags;
> @@ -206,7 +227,13 @@ static void rfkill_schedule_global_op(enum rfkill_global_sched_op op)
> spin_lock_irqsave(&rfkill_task.lock, flags);
> rfkill_task.op = op;
> rfkill_task.global_op_pending = true;
> - schedule_work(&rfkill_task.work);
> + if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) {
> + /* bypass the limiter for EPO */
> + cancel_delayed_work(&rfkill_task.dwork);
> + schedule_delayed_work(&rfkill_task.dwork, 0);
> + rfkill_task.last_scheduled = jiffies;
> + } else
> + rfkill_schedule_ratelimited();
> spin_unlock_irqrestore(&rfkill_task.lock, flags);
> }
>
> @@ -230,7 +257,7 @@ static void rfkill_schedule_set(enum rfkill_type type,
> set_bit(type, rfkill_task.sw_newstate);
> else
> clear_bit(type, rfkill_task.sw_newstate);
> - schedule_work(&rfkill_task.work);
> + rfkill_schedule_ratelimited();
> }
> spin_unlock_irqrestore(&rfkill_task.lock, flags);
> }
> @@ -247,7 +274,7 @@ static void rfkill_schedule_toggle(enum rfkill_type type)
> if (!rfkill_task.global_op_pending) {
> set_bit(type, rfkill_task.sw_pending);
> change_bit(type, rfkill_task.sw_togglestate);
> - schedule_work(&rfkill_task.work);
> + rfkill_schedule_ratelimited();
> }
> spin_unlock_irqrestore(&rfkill_task.lock, flags);
> }
> @@ -411,13 +438,19 @@ static int __init rfkill_handler_init(void)
> if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX)
> return -EINVAL;
>
> + /*
> + * The penalty to not doing this is a possible RFKILL_OPS_DELAY delay
> + * at the first use. Acceptable, but if we can avoid it, why not?
> + */
> + rfkill_task.last_scheduled =
> + jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1;
> return input_register_handler(&rfkill_handler);
> }
>
> static void __exit rfkill_handler_exit(void)
> {
> input_unregister_handler(&rfkill_handler);
> - flush_scheduled_work();
> + cancel_delayed_work_sync(&rfkill_task.dwork);
> rfkill_remove_epo_lock();
> }
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 3/5] rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input
2008-10-11 13:53 ` Ivo van Doorn
@ 2008-10-12 1:44 ` Henrique de Moraes Holschuh
0 siblings, 0 replies; 18+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-10-12 1:44 UTC (permalink / raw)
To: Ivo van Doorn; +Cc: John Linville, linux-wireless, Dmitry Torokhov
On Sat, 11 Oct 2008, Ivo van Doorn wrote:
> Although I think RFKILL_NEED_SWSET could be removed entirely if it isn't
> used anyway, we might do that later.
As I said, that code is NOT obvious. It is NOT too difficult to come up
with either, but I think it is safer if it is kept around for a while, or
just kept enabled but unused if you dislike the #ifdefs so much...
--
"One disk to rule them all, One disk to find them. One disk to bring
them all and in the darkness grind them. In the Land of Redmond
where the shadows lie." -- The Silicon Valley Tarot
Henrique Holschuh
^ permalink raw reply [flat|nested] 18+ messages in thread
* [GIT PATCH] rfkill updates
@ 2009-03-27 15:44 Henrique de Moraes Holschuh
2009-03-27 21:33 ` Johannes Berg
0 siblings, 1 reply; 18+ messages in thread
From: Henrique de Moraes Holschuh @ 2009-03-27 15:44 UTC (permalink / raw)
To: John Linville; +Cc: linux-wireless, Ivo van Doorn
While looking at the rfkill userspace API stuff that was bitrotting on
my git tree, I found this small set of minor patches for rfkill.
One of them will clash with a patch just sent to the list that removes
commented-out code. If both patches get Ack'd, I will take care of
the clash by collecting them all in a single set and reposting the
entire set with all Acks, etc.
Please consider for inclusion in wireless-testing. The patches are
based on wireless-testing master branch.
shortlog:
Henrique de Moraes Holschuh (4):
rfkill: minor locking fix
rfkill: status initialization paranoia
rfkill: defensive use of test_bit
rfkill: allow embedded config without an userspace API
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [GIT PATCH] rfkill updates
2009-03-27 15:44 Henrique de Moraes Holschuh
@ 2009-03-27 21:33 ` Johannes Berg
2009-03-28 12:36 ` Henrique de Moraes Holschuh
0 siblings, 1 reply; 18+ messages in thread
From: Johannes Berg @ 2009-03-27 21:33 UTC (permalink / raw)
To: Henrique de Moraes Holschuh; +Cc: John Linville, linux-wireless, Ivo van Doorn
[-- Attachment #1: Type: text/plain, Size: 456 bytes --]
Henrique,
Can we drop all this? I'm rewriting the entire rfkill core to better
support hard/soft kills, and all this code pretty much goes away anyway.
It's very frustrating to see a subsystem stagnate forever, and then once
somebody else starts working on it see a bunch of patches that break all
that work being posted.
Clearly those "bugs" are minor enough that nobody has even found them
yet, so it's not worth fixing them.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [GIT PATCH] rfkill updates
2009-03-27 21:33 ` Johannes Berg
@ 2009-03-28 12:36 ` Henrique de Moraes Holschuh
0 siblings, 0 replies; 18+ messages in thread
From: Henrique de Moraes Holschuh @ 2009-03-28 12:36 UTC (permalink / raw)
To: Johannes Berg; +Cc: John Linville, linux-wireless, Ivo van Doorn
On Fri, 27 Mar 2009, Johannes Berg wrote:
> Can we drop all this? I'm rewriting the entire rfkill core to better
> support hard/soft kills, and all this code pretty much goes away anyway.
Sure.
--
"One disk to rule them all, One disk to find them. One disk to bring
them all and in the darkness grind them. In the Land of Redmond
where the shadows lie." -- The Silicon Valley Tarot
Henrique Holschuh
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2009-03-28 12:36 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-09 21:15 [GIT PATCH] rfkill updates Henrique de Moraes Holschuh
2008-10-09 21:15 ` [PATCH 1/5] rfkill: use killable locks instead of interruptible Henrique de Moraes Holschuh
2008-10-11 13:53 ` Ivo van Doorn
2008-10-09 21:15 ` [PATCH 2/5] rfkill: export global states to rfkill-input Henrique de Moraes Holschuh
2008-10-11 13:53 ` Ivo van Doorn
2008-10-09 21:15 ` [PATCH 3/5] rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input Henrique de Moraes Holschuh
2008-10-09 21:20 ` Henrique de Moraes Holschuh
2008-10-10 0:49 ` Henrique de Moraes Holschuh
2008-10-11 13:53 ` Ivo van Doorn
2008-10-12 1:44 ` Henrique de Moraes Holschuh
2008-10-09 21:15 ` [PATCH 4/5] rfkill: honour EPO state when resuming a rfkill controller Henrique de Moraes Holschuh
2008-10-11 13:53 ` Ivo van Doorn
2008-10-09 21:15 ` [PATCH 5/5] rfkill: rate-limit rfkill-input workqueue usage (v3) Henrique de Moraes Holschuh
2008-10-11 13:53 ` Ivo van Doorn
2008-10-11 13:53 ` [GIT PATCH] rfkill updates Ivo van Doorn
-- strict thread matches above, loose matches on Subject: below --
2009-03-27 15:44 Henrique de Moraes Holschuh
2009-03-27 21:33 ` Johannes Berg
2009-03-28 12:36 ` Henrique de Moraes Holschuh
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).