summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--drivers/edac/edac_mc.c76
-rw-r--r--drivers/edac/edac_mc.h9
2 files changed, 42 insertions, 43 deletions
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index e6ecc7da38a5..7ee9419234aa 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -139,6 +139,12 @@ static struct sysdev_class edac_class = {
 static struct kobject edac_memctrl_kobj;
 static struct kobject edac_pci_kobj;
 
+/* We use these to wait for the reference counts on edac_memctrl_kobj and
+ * edac_pci_kobj to reach 0.
+ */
+static struct completion edac_memctrl_kobj_complete;
+static struct completion edac_pci_kobj_complete;
+
 /*
  * /sys/devices/system/edac/mc;
  * 	data structures and methods
@@ -244,6 +250,7 @@ static struct memctrl_dev_attribute *memctrl_attr[] = {
 static void edac_memctrl_master_release(struct kobject *kobj)
 {
 	debugf1("%s()\n", __func__);
+	complete(&edac_memctrl_kobj_complete);
 }
 
 static struct kobj_type ktype_memctrl = {
@@ -309,8 +316,12 @@ static void edac_sysfs_memctrl_teardown(void)
 #ifndef DISABLE_EDAC_SYSFS
 	debugf0("MC: " __FILE__ ": %s()\n", __func__);
 
-	/* Unregister the MC's kobject */
+	/* Unregister the MC's kobject and wait for reference count to reach
+	 * 0.
+	 */
+	init_completion(&edac_memctrl_kobj_complete);
 	kobject_unregister(&edac_memctrl_kobj);
+	wait_for_completion(&edac_memctrl_kobj_complete);
 
 	/* Unregister the 'edac' object */
 	sysdev_class_unregister(&edac_class);
@@ -563,6 +574,7 @@ static struct edac_pci_dev_attribute *edac_pci_attr[] = {
 static void edac_pci_release(struct kobject *kobj)
 {
 	debugf1("%s()\n", __func__);
+	complete(&edac_pci_kobj_complete);
 }
 
 static struct kobj_type ktype_edac_pci = {
@@ -610,8 +622,9 @@ static void edac_sysfs_pci_teardown(void)
 {
 #ifndef DISABLE_EDAC_SYSFS
 	debugf0("%s()\n", __func__);
-
+	init_completion(&edac_pci_kobj_complete);
 	kobject_unregister(&edac_pci_kobj);
+	wait_for_completion(&edac_pci_kobj_complete);
 #endif
 }
 
@@ -800,7 +813,11 @@ static struct csrowdev_attribute *csrow_attr[] = {
 /* No memory to release */
 static void edac_csrow_instance_release(struct kobject *kobj)
 {
+	struct csrow_info *cs;
+
 	debugf1("%s()\n", __func__);
+	cs = container_of(kobj, struct csrow_info, kobj);
+	complete(&cs->kobj_complete);
 }
 
 static struct kobj_type ktype_csrow = {
@@ -1055,11 +1072,10 @@ static struct mcidev_attribute *mci_attr[] = {
 static void edac_mci_instance_release(struct kobject *kobj)
 {
 	struct mem_ctl_info *mci;
-	mci = container_of(kobj,struct mem_ctl_info,edac_mci_kobj);
 
-	debugf0("%s() idx=%d calling kfree\n", __func__, mci->mc_idx);
-
-	kfree(mci);
+	mci = to_mci(kobj);
+	debugf0("%s() idx=%d\n", __func__, mci->mc_idx);
+	complete(&mci->kobj_complete);
 }
 
 static struct kobj_type ktype_mci = {
@@ -1131,21 +1147,23 @@ static int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
 		}
 	}
 
-	/* Mark this MCI instance as having sysfs entries */
-	mci->sysfs_active = MCI_SYSFS_ACTIVE;
-
 	return 0;
 
 
 	/* CSROW error: backout what has already been registered,  */
 fail1:
 	for ( i--; i >= 0; i--) {
-		if (csrow->nr_pages > 0)
+		if (csrow->nr_pages > 0) {
+			init_completion(&csrow->kobj_complete);
 			kobject_unregister(&mci->csrows[i].kobj);
+			wait_for_completion(&csrow->kobj_complete);
+		}
 	}
 
 fail0:
+	init_completion(&mci->kobj_complete);
 	kobject_unregister(edac_mci_kobj);
+	wait_for_completion(&mci->kobj_complete);
 
 	return err;
 }
@@ -1163,13 +1181,17 @@ static void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
 
 	/* remove all csrow kobjects */
 	for (i = 0; i < mci->nr_csrows; i++) {
-		if (mci->csrows[i].nr_pages > 0)
+		if (mci->csrows[i].nr_pages > 0) {
+			init_completion(&mci->csrows[i].kobj_complete);
 			kobject_unregister(&mci->csrows[i].kobj);
+			wait_for_completion(&mci->csrows[i].kobj_complete);
+		}
 	}
 
 	sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK);
-
+	init_completion(&mci->kobj_complete);
 	kobject_unregister(&mci->edac_mci_kobj);
+	wait_for_completion(&mci->kobj_complete);
 #endif  /* DISABLE_EDAC_SYSFS */
 }
 
@@ -1342,31 +1364,10 @@ EXPORT_SYMBOL(edac_mc_free);
 /**
  * edac_mc_free:  Free a previously allocated 'mci' structure
  * @mci: pointer to a struct mem_ctl_info structure
- *
- * Free up a previously allocated mci structure
- * A MCI structure can be in 2 states after being allocated
- * by edac_mc_alloc().
- *	1) Allocated in a MC driver's probe, but not yet committed
- *	2) Allocated and committed, by a call to  edac_mc_add_mc()
- * edac_mc_add_mc() is the function that adds the sysfs entries
- * thus, this free function must determine which state the 'mci'
- * structure is in, then either free it directly or
- * perform kobject cleanup by calling edac_remove_sysfs_mci_device().
- *
- * VOID Return
  */
 void edac_mc_free(struct mem_ctl_info *mci)
 {
-	/* only if sysfs entries for this mci instance exist
-	 * do we remove them and defer the actual kfree via
-	 * the kobject 'release()' callback.
- 	 *
-	 * Otherwise, do a straight kfree now.
-	 */
-	if (mci->sysfs_active == MCI_SYSFS_ACTIVE)
-		edac_remove_sysfs_mci_device(mci);
-	else
-		kfree(mci);
+	kfree(mci);
 }
 
 
@@ -1456,7 +1457,8 @@ static void del_mc_from_global_list (struct mem_ctl_info *mci)
 EXPORT_SYMBOL(edac_mc_add_mc);
 
 /**
- * edac_mc_add_mc: Insert the 'mci' structure into the mci global list
+ * edac_mc_add_mc: Insert the 'mci' structure into the mci global list and
+ *                 create sysfs entries associated with mci structure
  * @mci: pointer to the mci structure to be added to the list
  *
  * Return:
@@ -1516,7 +1518,8 @@ fail0:
 EXPORT_SYMBOL(edac_mc_del_mc);
 
 /**
- * edac_mc_del_mc:  Remove the specified mci structure from global list
+ * edac_mc_del_mc: Remove sysfs entries for specified mci structure and
+ *                 remove mci structure from global list
  * @mci:	Pointer to struct mem_ctl_info structure
  *
  * Returns:
@@ -1528,6 +1531,7 @@ int edac_mc_del_mc(struct mem_ctl_info *mci)
 	int rc = 1;
 
 	debugf0("MC%d: %s()\n", mci->mc_idx, __func__);
+	edac_remove_sysfs_mci_device(mci);
 	down(&mem_ctls_mutex);
 	del_mc_from_global_list(mci);
 	edac_printk(KERN_INFO, EDAC_MC,
diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h
index 0bcc3797c753..c9c1590db721 100644
--- a/drivers/edac/edac_mc.h
+++ b/drivers/edac/edac_mc.h
@@ -185,11 +185,6 @@ enum scrub_type {
 #define SCRUB_FLAG_HW_PROG_SRC	BIT(SCRUB_HW_PROG_SRC_CORR)
 #define SCRUB_FLAG_HW_TUN	BIT(SCRUB_HW_TUNABLE)
 
-enum mci_sysfs_status {
-	MCI_SYSFS_INACTIVE = 0,	/* sysfs entries NOT registered */
-	MCI_SYSFS_ACTIVE	/* sysfs entries ARE registered */
-};
-
 /* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */
 
 /*
@@ -299,6 +294,7 @@ struct csrow_info {
 	struct mem_ctl_info *mci;	/* the parent */
 
 	struct kobject kobj;	/* sysfs kobject for this csrow */
+	struct completion kobj_complete;
 
 	/* FIXME the number of CHANNELs might need to become dynamic */
 	u32 nr_channels;
@@ -320,8 +316,6 @@ struct mem_ctl_info {
 	unsigned long scrub_cap;	/* chipset scrub capabilities */
 	enum scrub_type scrub_mode;	/* current scrub mode */
 
-	enum mci_sysfs_status sysfs_active;	/* status of sysfs */
-
 	/* pointer to edac checking routine */
 	void (*edac_check) (struct mem_ctl_info * mci);
 	/*
@@ -359,6 +353,7 @@ struct mem_ctl_info {
 
 	/* edac sysfs device control */
 	struct kobject edac_mci_kobj;
+	struct completion kobj_complete;
 };