Browse Source

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>
Alex Dubov 18 years ago
parent
commit
3540af8ffd
3 changed files with 72 additions and 76 deletions
  1. 34 55
      drivers/misc/tifm_7xx1.c
  2. 34 17
      drivers/misc/tifm_core.c
  3. 4 4
      include/linux/tifm.h

+ 34 - 55
drivers/misc/tifm_7xx1.c

@@ -28,7 +28,7 @@ static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock)
 
 
 	spin_lock_irqsave(&fm->lock, flags);
 	spin_lock_irqsave(&fm->lock, flags);
 	fm->socket_change_set |= 1 << sock->socket_id;
 	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);
 	spin_unlock_irqrestore(&fm->lock, flags);
 }
 }
 
 
@@ -64,10 +64,12 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id)
 	}
 	}
 	writel(irq_status, fm->addr + FM_INTERRUPT_STATUS);
 	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);
 		writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE);
 	else
 	else
-		wake_up_all(&fm->change_set_notify);
+		tifm_queue_work(&fm->media_switcher);
 
 
 	spin_unlock(&fm->lock);
 	spin_unlock(&fm->lock);
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
@@ -125,37 +127,29 @@ tifm_7xx1_sock_addr(char __iomem *base_addr, unsigned int sock_num)
 	return base_addr + ((sock_num + 1) << 10);
 	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 long flags;
 	unsigned char media_id;
 	unsigned char media_id;
 	char *card_name = "xx";
 	char *card_name = "xx";
-	int cnt, rc;
+	int cnt;
 	struct tifm_dev *sock;
 	struct tifm_dev *sock;
 	unsigned int socket_change_set;
 	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);
 		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++) {
 		for (cnt = 0; cnt < fm->num_sockets; cnt++) {
 			if (!(socket_change_set & (1 << cnt)))
 			if (!(socket_change_set & (1 << cnt)))
 				continue;
 				continue;
@@ -172,8 +166,6 @@ static int tifm_7xx1_switch_media(void *data)
 				       tifm_7xx1_sock_addr(fm->addr, cnt)
 				       tifm_7xx1_sock_addr(fm->addr, cnt)
 				       + SOCK_CONTROL);
 				       + SOCK_CONTROL);
 			}
 			}
-			if (kthread_should_stop())
-				continue;
 
 
 			spin_unlock_irqrestore(&fm->lock, flags);
 			spin_unlock_irqrestore(&fm->lock, flags);
 			media_id = tifm_7xx1_toggle_sock_power(
 			media_id = tifm_7xx1_toggle_sock_power(
@@ -222,30 +214,16 @@ static int tifm_7xx1_switch_media(void *data)
 			}
 			}
 		}
 		}
 
 
-		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
 #ifdef CONFIG_PM
@@ -267,6 +245,7 @@ static int tifm_7xx1_resume(struct pci_dev *dev)
 	int cnt, rc;
 	int cnt, rc;
 	unsigned long flags;
 	unsigned long flags;
 	unsigned char new_ids[fm->num_sockets];
 	unsigned char new_ids[fm->num_sockets];
+	DECLARE_COMPLETION_ONSTACK(finish_resume);
 
 
 	pci_set_power_state(dev, PCI_D0);
 	pci_set_power_state(dev, PCI_D0);
 	pci_restore_state(dev);
 	pci_restore_state(dev);
@@ -299,12 +278,14 @@ static int tifm_7xx1_resume(struct pci_dev *dev)
 		return 0;
 		return 0;
 	} else {
 	} else {
 		fm->socket_change_set = 0;
 		fm->socket_change_set = 0;
+		fm->finish_me = &finish_resume;
 		spin_unlock_irqrestore(&fm->lock, flags);
 		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);
 	spin_lock_irqsave(&fm->lock, flags);
+	fm->finish_me = NULL;
 	writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set)
 	writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set)
 	       | TIFM_IRQ_CARDMASK(fm->socket_change_set),
 	       | TIFM_IRQ_CARDMASK(fm->socket_change_set),
 	       fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
 	       fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
@@ -365,6 +346,7 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
 	if (!fm->sockets)
 	if (!fm->sockets)
 		goto err_out_free;
 		goto err_out_free;
 
 
+	INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media);
 	fm->eject = tifm_7xx1_eject;
 	fm->eject = tifm_7xx1_eject;
 	pci_set_drvdata(dev, fm);
 	pci_set_drvdata(dev, fm);
 
 
@@ -377,15 +359,14 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
 	if (rc)
 	if (rc)
 		goto err_out_unmap;
 		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)
 	if (rc)
 		goto err_out_irq;
 		goto err_out_irq;
 
 
 	writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
 	writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
 	writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
 	writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
 	       fm->addr + FM_SET_INTERRUPT_ENABLE);
 	       fm->addr + FM_SET_INTERRUPT_ENABLE);
-	wake_up_process(fm->media_switcher);
+
 	return 0;
 	return 0;
 
 
 err_out_irq:
 err_out_irq:
@@ -417,8 +398,6 @@ static void tifm_7xx1_remove(struct pci_dev *dev)
 	fm->socket_change_set = (1 << fm->num_sockets) - 1;
 	fm->socket_change_set = (1 << fm->num_sockets) - 1;
 	spin_unlock_irqrestore(&fm->lock, flags);
 	spin_unlock_irqrestore(&fm->lock, flags);
 
 
-	kthread_stop(fm->media_switcher);
-
 	tifm_remove_adapter(fm);
 	tifm_remove_adapter(fm);
 
 
 	pci_set_drvdata(dev, NULL);
 	pci_set_drvdata(dev, NULL);

+ 34 - 17
drivers/misc/tifm_core.c

@@ -16,6 +16,7 @@
 #define DRIVER_NAME "tifm_core"
 #define DRIVER_NAME "tifm_core"
 #define DRIVER_VERSION "0.8"
 #define DRIVER_VERSION "0.8"
 
 
+static struct workqueue_struct *workqueue;
 static DEFINE_IDR(tifm_adapter_idr);
 static DEFINE_IDR(tifm_adapter_idr);
 static DEFINE_SPINLOCK(tifm_adapter_lock);
 static DEFINE_SPINLOCK(tifm_adapter_lock);
 
 
@@ -184,8 +185,7 @@ void tifm_free_adapter(struct tifm_adapter *fm)
 }
 }
 EXPORT_SYMBOL(tifm_free_adapter);
 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;
 	int rc;
 
 
@@ -197,16 +197,13 @@ int tifm_add_adapter(struct tifm_adapter *fm,
 	spin_unlock(&tifm_adapter_lock);
 	spin_unlock(&tifm_adapter_lock);
 	if (!rc) {
 	if (!rc) {
 		snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id);
 		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;
 	return rc;
 }
 }
@@ -214,6 +211,7 @@ EXPORT_SYMBOL(tifm_add_adapter);
 
 
 void tifm_remove_adapter(struct tifm_adapter *fm)
 void tifm_remove_adapter(struct tifm_adapter *fm)
 {
 {
+	flush_workqueue(workqueue);
 	class_device_del(&fm->cdev);
 	class_device_del(&fm->cdev);
 
 
 	spin_lock(&tifm_adapter_lock);
 	spin_lock(&tifm_adapter_lock);
@@ -267,6 +265,12 @@ void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
 }
 }
 EXPORT_SYMBOL(tifm_unmap_sg);
 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)
 int tifm_register_driver(struct tifm_driver *drv)
 {
 {
 	drv->driver.bus = &tifm_bus_type;
 	drv->driver.bus = &tifm_bus_type;
@@ -283,13 +287,25 @@ EXPORT_SYMBOL(tifm_unregister_driver);
 
 
 static int __init tifm_init(void)
 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;
 	return rc;
 }
 }
@@ -298,6 +314,7 @@ static void __exit tifm_exit(void)
 {
 {
 	class_unregister(&tifm_adapter_class);
 	class_unregister(&tifm_adapter_class);
 	bus_unregister(&tifm_bus_type);
 	bus_unregister(&tifm_bus_type);
+	destroy_workqueue(workqueue);
 }
 }
 
 
 subsys_initcall(tifm_init);
 subsys_initcall(tifm_init);

+ 4 - 4
include/linux/tifm.h

@@ -111,11 +111,11 @@ struct tifm_adapter {
 	spinlock_t              lock;
 	spinlock_t              lock;
 	unsigned int            irq_status;
 	unsigned int            irq_status;
 	unsigned int            socket_change_set;
 	unsigned int            socket_change_set;
-	wait_queue_head_t       change_set_notify;
 	unsigned int            id;
 	unsigned int            id;
 	unsigned int            num_sockets;
 	unsigned int            num_sockets;
+	struct completion       *finish_me;
 	struct tifm_dev         **sockets;
 	struct tifm_dev         **sockets;
-	struct task_struct      *media_switcher;
+	struct work_struct      media_switcher;
 	struct class_device     cdev;
 	struct class_device     cdev;
 	struct device           *dev;
 	struct device           *dev;
 
 
@@ -125,7 +125,7 @@ struct tifm_adapter {
 struct tifm_adapter *tifm_alloc_adapter(void);
 struct tifm_adapter *tifm_alloc_adapter(void);
 void tifm_free_device(struct device *dev);
 void tifm_free_device(struct device *dev);
 void tifm_free_adapter(struct tifm_adapter *fm);
 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);
 void tifm_remove_adapter(struct tifm_adapter *fm);
 struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm);
 struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm);
 int tifm_register_driver(struct tifm_driver *drv);
 int tifm_register_driver(struct tifm_driver *drv);
@@ -135,7 +135,7 @@ int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
 		int direction);
 		int direction);
 void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
 void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
 		   int direction);
 		   int direction);
-
+void tifm_queue_work(struct work_struct *work);
 
 
 static inline void *tifm_get_drvdata(struct tifm_dev *dev)
 static inline void *tifm_get_drvdata(struct tifm_dev *dev)
 {
 {