}
EXPORT_SYMBOL_GPL(edac_mc_free);
+
+/*
+ * find_mci_by_dev
+ *
+ * scan list of controllers looking for the one that manages
+ * the 'dev' device
+ */
static struct mem_ctl_info *find_mci_by_dev(struct device *dev)
{
struct mem_ctl_info *mci;
mutex_lock(&mem_ctls_mutex);
+ /* if this control struct has movd to offline state, we are done */
+ if (mci->op_state == OP_OFFLINE) {
+ mutex_unlock(&mem_ctls_mutex);
+ return;
+ }
+
/* Only poll controllers that are running polled and have a check */
if (edac_mc_assert_error_check_and_clear() && (mci->edac_check != NULL))
mci->edac_check(mci);
- /*
- * FIXME: temp place holder for PCI checks,
- * goes away when we break out PCI
- */
- edac_pci_do_parity_check();
-
mutex_unlock(&mem_ctls_mutex);
/* Reschedule */
* edac_mc_workq_setup
* initialize a workq item for this mci
* passing in the new delay period in msec
+ *
+ * locking model:
+ *
+ * called with the mem_ctls_mutex held
*/
-void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
+static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
{
debugf0("%s()\n", __func__);
+ /* if this instance is not in the POLL state, then simply return */
+ if (mci->op_state != OP_RUNNING_POLL)
+ return;
+
INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
queue_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec));
}
/*
* edac_mc_workq_teardown
* stop the workq processing on this mci
+ *
+ * locking model:
+ *
+ * called WITHOUT lock held
*/
-void edac_mc_workq_teardown(struct mem_ctl_info *mci)
+static void edac_mc_workq_teardown(struct mem_ctl_info *mci)
{
int status;
status = cancel_delayed_work(&mci->work);
if (status == 0) {
+ debugf0("%s() not canceled, flush the queue\n",
+ __func__);
+
/* workq instance might be running, wait for it */
flush_workqueue(edac_workqueue);
}
}
/*
- * edac_reset_delay_period
+ * edac_mc_reset_delay_period(unsigned long value)
+ *
+ * user space has updated our poll period value, need to
+ * reset our workq delays
*/
-
-void edac_reset_delay_period(struct mem_ctl_info *mci, unsigned long value)
+void edac_mc_reset_delay_period(int value)
{
+ struct mem_ctl_info *mci;
+ struct list_head *item;
+
mutex_lock(&mem_ctls_mutex);
- /* cancel the current workq request */
- edac_mc_workq_teardown(mci);
+ /* scan the list and turn off all workq timers, doing so under lock
+ */
+ list_for_each(item, &mc_devices) {
+ mci = list_entry(item, struct mem_ctl_info, link);
- /* restart the workq request, with new delay value */
- edac_mc_workq_setup(mci, value);
+ if (mci->op_state == OP_RUNNING_POLL)
+ cancel_delayed_work(&mci->work);
+ }
+
+ mutex_unlock(&mem_ctls_mutex);
+
+
+ /* re-walk the list, and reset the poll delay */
+ mutex_lock(&mem_ctls_mutex);
+
+ list_for_each(item, &mc_devices) {
+ mci = list_entry(item, struct mem_ctl_info, link);
+
+ edac_mc_workq_setup(mci, (unsigned long) value);
+ }
mutex_unlock(&mem_ctls_mutex);
}
+
+
/* Return 0 on success, 1 on failure.
* Before calling this function, caller must
* assign a unique value to mci->mc_idx.
+ *
+ * locking model:
+ *
+ * called with the mem_ctls_mutex lock held
*/
static int add_mc_to_global_list(struct mem_ctl_info *mci)
{
insert_before = &mc_devices;
- if (unlikely((p = find_mci_by_dev(mci->dev)) != NULL))
+ p = find_mci_by_dev(mci->dev);
+ if (unlikely(p != NULL))
goto fail0;
list_for_each(item, &mc_devices) {
}
/* Report action taken */
- edac_mc_printk(mci, KERN_INFO, "Giving out device to %s %s: DEV %s\n",
- mci->mod_name, mci->ctl_name, dev_name(mci));
+ edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':"
+ " DEV %s\n", mci->mod_name, mci->ctl_name, dev_name(mci));
mutex_unlock(&mem_ctls_mutex);
return 0;
{
struct mem_ctl_info *mci;
- debugf0("MC: %s()\n", __func__);
+ debugf0("%s()\n", __func__);
+
mutex_lock(&mem_ctls_mutex);
- if ((mci = find_mci_by_dev(dev)) == NULL) {
+ /* find the requested mci struct in the global list */
+ mci = find_mci_by_dev(dev);
+ if (mci == NULL) {
mutex_unlock(&mem_ctls_mutex);
return NULL;
}
/* marking MCI offline */
mci->op_state = OP_OFFLINE;
- /* flush workq processes */
- edac_mc_workq_teardown(mci);
-
- edac_remove_sysfs_mci_device(mci);
del_mc_from_global_list(mci);
mutex_unlock(&mem_ctls_mutex);
+
+ /* flush workq processes and remove sysfs */
+ edac_mc_workq_teardown(mci);
+ edac_remove_sysfs_mci_device(mci);
+
edac_printk(KERN_INFO, EDAC_MC,
"Removed device %d for %s %s: DEV %s\n", mci->mc_idx,
mci->mod_name, mci->ctl_name, dev_name(mci));
+
return mci;
}
EXPORT_SYMBOL_GPL(edac_mc_del_mc);