tifm: replace per-adapter kthread with freezeable workqueue
Freezeable workqueue makes sure that adapter work items (device insertions
and removals) would be handled after the system is fully resumed. Previously
this was achieved by explicit freezing of the kthread.
Signed-off-by: Alex Dubov <oakad@yahoo.com>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c
index fd7b8da..e5655fe 100644
--- a/drivers/misc/tifm_7xx1.c
+++ b/drivers/misc/tifm_7xx1.c
@@ -28,7 +28,7 @@
spin_lock_irqsave(&fm->lock, flags);
fm->socket_change_set |= 1 << sock->socket_id;
- wake_up_all(&fm->change_set_notify);
+ tifm_queue_work(&fm->media_switcher);
spin_unlock_irqrestore(&fm->lock, flags);
}
@@ -64,10 +64,12 @@
}
writel(irq_status, fm->addr + FM_INTERRUPT_STATUS);
- if (!fm->socket_change_set)
+ if (fm->finish_me)
+ complete_all(fm->finish_me);
+ else if (!fm->socket_change_set)
writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE);
else
- wake_up_all(&fm->change_set_notify);
+ tifm_queue_work(&fm->media_switcher);
spin_unlock(&fm->lock);
return IRQ_HANDLED;
@@ -125,37 +127,29 @@
return base_addr + ((sock_num + 1) << 10);
}
-static int tifm_7xx1_switch_media(void *data)
+static void tifm_7xx1_switch_media(struct work_struct *work)
{
- struct tifm_adapter *fm = data;
+ struct tifm_adapter *fm = container_of(work, struct tifm_adapter,
+ media_switcher);
unsigned long flags;
unsigned char media_id;
char *card_name = "xx";
- int cnt, rc;
+ int cnt;
struct tifm_dev *sock;
unsigned int socket_change_set;
- while (1) {
- rc = wait_event_interruptible(fm->change_set_notify,
- fm->socket_change_set);
- if (rc == -ERESTARTSYS)
- try_to_freeze();
+ spin_lock_irqsave(&fm->lock, flags);
+ socket_change_set = fm->socket_change_set;
+ fm->socket_change_set = 0;
- spin_lock_irqsave(&fm->lock, flags);
- socket_change_set = fm->socket_change_set;
- fm->socket_change_set = 0;
+ dev_dbg(fm->dev, "checking media set %x\n",
+ socket_change_set);
- dev_dbg(fm->dev, "checking media set %x\n",
- socket_change_set);
-
- if (kthread_should_stop())
- socket_change_set = (1 << fm->num_sockets) - 1;
+ if (!socket_change_set) {
spin_unlock_irqrestore(&fm->lock, flags);
+ return;
+ }
- if (!socket_change_set)
- continue;
-
- spin_lock_irqsave(&fm->lock, flags);
for (cnt = 0; cnt < fm->num_sockets; cnt++) {
if (!(socket_change_set & (1 << cnt)))
continue;
@@ -172,8 +166,6 @@
tifm_7xx1_sock_addr(fm->addr, cnt)
+ SOCK_CONTROL);
}
- if (kthread_should_stop())
- continue;
spin_unlock_irqrestore(&fm->lock, flags);
media_id = tifm_7xx1_toggle_sock_power(
@@ -222,30 +214,16 @@
}
}
- if (!kthread_should_stop()) {
- writel(TIFM_IRQ_FIFOMASK(socket_change_set)
- | TIFM_IRQ_CARDMASK(socket_change_set),
- fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
- writel(TIFM_IRQ_FIFOMASK(socket_change_set)
- | TIFM_IRQ_CARDMASK(socket_change_set),
- fm->addr + FM_SET_INTERRUPT_ENABLE);
- writel(TIFM_IRQ_ENABLE,
- fm->addr + FM_SET_INTERRUPT_ENABLE);
- spin_unlock_irqrestore(&fm->lock, flags);
- } else {
- for (cnt = 0; cnt < fm->num_sockets; cnt++) {
- if (fm->sockets[cnt])
- fm->socket_change_set |= 1 << cnt;
- }
- if (!fm->socket_change_set) {
- spin_unlock_irqrestore(&fm->lock, flags);
- return 0;
- } else {
- spin_unlock_irqrestore(&fm->lock, flags);
- }
- }
- }
- return 0;
+ writel(TIFM_IRQ_FIFOMASK(socket_change_set)
+ | TIFM_IRQ_CARDMASK(socket_change_set),
+ fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
+
+ writel(TIFM_IRQ_FIFOMASK(socket_change_set)
+ | TIFM_IRQ_CARDMASK(socket_change_set),
+ fm->addr + FM_SET_INTERRUPT_ENABLE);
+
+ writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE);
+ spin_unlock_irqrestore(&fm->lock, flags);
}
#ifdef CONFIG_PM
@@ -267,6 +245,7 @@
int cnt, rc;
unsigned long flags;
unsigned char new_ids[fm->num_sockets];
+ DECLARE_COMPLETION_ONSTACK(finish_resume);
pci_set_power_state(dev, PCI_D0);
pci_restore_state(dev);
@@ -299,12 +278,14 @@
return 0;
} else {
fm->socket_change_set = 0;
+ fm->finish_me = &finish_resume;
spin_unlock_irqrestore(&fm->lock, flags);
}
- wait_event_timeout(fm->change_set_notify, fm->socket_change_set, HZ);
+ wait_for_completion_timeout(&finish_resume, HZ);
spin_lock_irqsave(&fm->lock, flags);
+ fm->finish_me = NULL;
writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set)
| TIFM_IRQ_CARDMASK(fm->socket_change_set),
fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
@@ -365,6 +346,7 @@
if (!fm->sockets)
goto err_out_free;
+ INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media);
fm->eject = tifm_7xx1_eject;
pci_set_drvdata(dev, fm);
@@ -377,15 +359,14 @@
if (rc)
goto err_out_unmap;
- init_waitqueue_head(&fm->change_set_notify);
- rc = tifm_add_adapter(fm, tifm_7xx1_switch_media);
+ rc = tifm_add_adapter(fm);
if (rc)
goto err_out_irq;
writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
fm->addr + FM_SET_INTERRUPT_ENABLE);
- wake_up_process(fm->media_switcher);
+
return 0;
err_out_irq:
@@ -417,8 +398,6 @@
fm->socket_change_set = (1 << fm->num_sockets) - 1;
spin_unlock_irqrestore(&fm->lock, flags);
- kthread_stop(fm->media_switcher);
-
tifm_remove_adapter(fm);
pci_set_drvdata(dev, NULL);
diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c
index 6b2c447..ef8a97b8 100644
--- a/drivers/misc/tifm_core.c
+++ b/drivers/misc/tifm_core.c
@@ -16,6 +16,7 @@
#define DRIVER_NAME "tifm_core"
#define DRIVER_VERSION "0.8"
+static struct workqueue_struct *workqueue;
static DEFINE_IDR(tifm_adapter_idr);
static DEFINE_SPINLOCK(tifm_adapter_lock);
@@ -184,8 +185,7 @@
}
EXPORT_SYMBOL(tifm_free_adapter);
-int tifm_add_adapter(struct tifm_adapter *fm,
- int (*mediathreadfn)(void *data))
+int tifm_add_adapter(struct tifm_adapter *fm)
{
int rc;
@@ -197,16 +197,13 @@
spin_unlock(&tifm_adapter_lock);
if (!rc) {
snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id);
- fm->media_switcher = kthread_create(mediathreadfn,
- fm, "tifm/%u", fm->id);
+ rc = class_device_add(&fm->cdev);
- if (!IS_ERR(fm->media_switcher))
- return class_device_add(&fm->cdev);
-
- spin_lock(&tifm_adapter_lock);
- idr_remove(&tifm_adapter_idr, fm->id);
- spin_unlock(&tifm_adapter_lock);
- rc = -ENOMEM;
+ if (rc) {
+ spin_lock(&tifm_adapter_lock);
+ idr_remove(&tifm_adapter_idr, fm->id);
+ spin_unlock(&tifm_adapter_lock);
+ }
}
return rc;
}
@@ -214,6 +211,7 @@
void tifm_remove_adapter(struct tifm_adapter *fm)
{
+ flush_workqueue(workqueue);
class_device_del(&fm->cdev);
spin_lock(&tifm_adapter_lock);
@@ -267,6 +265,12 @@
}
EXPORT_SYMBOL(tifm_unmap_sg);
+void tifm_queue_work(struct work_struct *work)
+{
+ queue_work(workqueue, work);
+}
+EXPORT_SYMBOL(tifm_queue_work);
+
int tifm_register_driver(struct tifm_driver *drv)
{
drv->driver.bus = &tifm_bus_type;
@@ -283,13 +287,25 @@
static int __init tifm_init(void)
{
- int rc = bus_register(&tifm_bus_type);
+ int rc;
- if (!rc) {
- rc = class_register(&tifm_adapter_class);
- if (rc)
- bus_unregister(&tifm_bus_type);
- }
+ workqueue = create_freezeable_workqueue("tifm");
+ if (!workqueue)
+ return -ENOMEM;
+
+ rc = bus_register(&tifm_bus_type);
+
+ if (rc)
+ goto err_out_wq;
+
+ rc = class_register(&tifm_adapter_class);
+ if (!rc)
+ return 0;
+
+ bus_unregister(&tifm_bus_type);
+
+err_out_wq:
+ destroy_workqueue(workqueue);
return rc;
}
@@ -298,6 +314,7 @@
{
class_unregister(&tifm_adapter_class);
bus_unregister(&tifm_bus_type);
+ destroy_workqueue(workqueue);
}
subsys_initcall(tifm_init);
diff --git a/include/linux/tifm.h b/include/linux/tifm.h
index 57b2653..d9de792 100644
--- a/include/linux/tifm.h
+++ b/include/linux/tifm.h
@@ -111,11 +111,11 @@
spinlock_t lock;
unsigned int irq_status;
unsigned int socket_change_set;
- wait_queue_head_t change_set_notify;
unsigned int id;
unsigned int num_sockets;
+ struct completion *finish_me;
struct tifm_dev **sockets;
- struct task_struct *media_switcher;
+ struct work_struct media_switcher;
struct class_device cdev;
struct device *dev;
@@ -125,7 +125,7 @@
struct tifm_adapter *tifm_alloc_adapter(void);
void tifm_free_device(struct device *dev);
void tifm_free_adapter(struct tifm_adapter *fm);
-int tifm_add_adapter(struct tifm_adapter *fm, int (*mediathreadfn)(void *data));
+int tifm_add_adapter(struct tifm_adapter *fm);
void tifm_remove_adapter(struct tifm_adapter *fm);
struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm);
int tifm_register_driver(struct tifm_driver *drv);
@@ -135,7 +135,7 @@
int direction);
void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
int direction);
-
+void tifm_queue_work(struct work_struct *work);
static inline void *tifm_get_drvdata(struct tifm_dev *dev)
{