EDAC: Balance workqueue setup and teardown

We use the ->edac_check function pointers to determine whether we need
to setup a polling workqueue. However, the destroy path is not balanced
and we might try to teardown an unitialized workqueue.

Balance init and destroy paths by looking at ->edac_check in both cases.
Set op_state to OP_OFFLINE *before* destroying anything.

Reported-by: Zhiqiang Hou <Zhiqiang.Hou@freescale.com>
Cc: Varun Sethi <Varun.Sethi@freescale.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 8adfc16..50802c1 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -583,8 +583,6 @@
  */
 static void edac_mc_workq_teardown(struct mem_ctl_info *mci)
 {
-	mci->op_state = OP_OFFLINE;
-
 	edac_stop_work(&mci->work);
 }
 
@@ -772,7 +770,7 @@
 	}
 
 	/* If there IS a check routine, then we are running POLLED */
-	if (mci->edac_check != NULL) {
+	if (mci->edac_check) {
 		/* This instance is NOW RUNNING */
 		mci->op_state = OP_RUNNING_POLL;
 
@@ -823,15 +821,16 @@
 		return NULL;
 	}
 
+	/* mark MCI offline: */
+	mci->op_state = OP_OFFLINE;
+
 	if (!del_mc_from_global_list(mci))
 		edac_mc_owner = NULL;
+
 	mutex_unlock(&mem_ctls_mutex);
 
-	/* flush workq processes */
-	edac_mc_workq_teardown(mci);
-
-	/* marking MCI offline */
-	mci->op_state = OP_OFFLINE;
+	if (mci->edac_check)
+		edac_mc_workq_teardown(mci);
 
 	/* remove from sysfs */
 	edac_remove_sysfs_mci_device(mci);
diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c
index 9968538..f0e8c3d 100644
--- a/drivers/edac/edac_pci.c
+++ b/drivers/edac/edac_pci.c
@@ -241,8 +241,6 @@
 {
 	edac_dbg(0, "\n");
 
-	pci->op_state = OP_OFFLINE;
-
 	edac_stop_work(&pci->work);
 }
 
@@ -289,7 +287,7 @@
 		goto fail1;
 	}
 
-	if (pci->edac_check != NULL) {
+	if (pci->edac_check) {
 		pci->op_state = OP_RUNNING_POLL;
 
 		edac_pci_workq_setup(pci, 1000);
@@ -350,8 +348,8 @@
 
 	mutex_unlock(&edac_pci_ctls_mutex);
 
-	/* stop the workq timer */
-	edac_pci_workq_teardown(pci);
+	if (pci->edac_check)
+		edac_pci_workq_teardown(pci);
 
 	edac_printk(KERN_INFO, EDAC_PCI,
 		"Removed device %d for %s %s: DEV %s\n",