Skip to content

Commit

Permalink
drivers/edac: mod MC to use workq instead of kthread
Browse files Browse the repository at this point in the history
Move the memory controller object to work queue based implementation from the
kernel thread based.

Signed-off-by: Dave Jiang <djiang@mvista.com>
Signed-off-by: Douglas Thompson <dougthompson@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Dave Jiang authored and Linus Torvalds committed Jul 19, 2007
1 parent 535c6a5 commit 81d87cb
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 97 deletions.
14 changes: 14 additions & 0 deletions drivers/edac/edac_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,15 @@ struct mem_ctl_info {
/* edac sysfs device control */
struct kobject edac_mci_kobj;
struct completion kobj_complete;

/* work struct for this MC */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
struct delayed_work work;
#else
struct work_struct work;
#endif
/* the internal state of this controller instance */
int op_state;
};

/*
Expand Down Expand Up @@ -573,6 +582,9 @@ struct edac_device_ctl_info {
};

/* To get from the instance's wq to the beginning of the ctl structure */
#define to_edac_mem_ctl_work(w) \
container_of(w, struct mem_ctl_info, work)

#define to_edac_device_ctl_work(w) \
container_of(w,struct edac_device_ctl_info,work)

Expand All @@ -584,6 +596,8 @@ static inline void edac_device_calc_delay(
edac_dev->delay = edac_dev->poll_msec * HZ / 1000;
}

#define edac_calc_delay(dev) dev->delay = dev->poll_msec * HZ / 1000;

/*
* The alloc() and free() functions for the 'edac_device' control info
* structure. A MC driver will allocate one of these for each edac_device
Expand Down
36 changes: 20 additions & 16 deletions drivers/edac/edac_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -332,17 +332,17 @@ EXPORT_SYMBOL(edac_device_find);


/*
* edac_workq_function
* edac_device_workq_function
* performs the operation scheduled by a workq request
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
static void edac_workq_function(struct work_struct *work_req)
static void edac_device_workq_function(struct work_struct *work_req)
{
struct delayed_work *d_work = (struct delayed_work*) work_req;
struct edac_device_ctl_info *edac_dev =
to_edac_device_ctl_work(d_work);
#else
static void edac_workq_function(void *ptr)
static void edac_device_workq_function(void *ptr)
{
struct edac_device_ctl_info *edac_dev =
(struct edac_device_ctl_info *) ptr;
Expand All @@ -364,30 +364,31 @@ static void edac_workq_function(void *ptr)
}

/*
* edac_workq_setup
* edac_device_workq_setup
* initialize a workq item for this edac_device instance
* passing in the new delay period in msec
*/
void edac_workq_setup(struct edac_device_ctl_info *edac_dev, unsigned msec)
void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
unsigned msec)
{
debugf0("%s()\n", __func__);

edac_dev->poll_msec = msec;
edac_device_calc_delay(edac_dev); /* Calc delay jiffies */
edac_calc_delay(edac_dev); /* Calc delay jiffies */

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
INIT_DELAYED_WORK(&edac_dev->work,edac_workq_function);
INIT_DELAYED_WORK(&edac_dev->work, edac_device_workq_function);
#else
INIT_WORK(&edac_dev->work,edac_workq_function,edac_dev);
INIT_WORK(&edac_dev->work, edac_device_workq_function, edac_dev);
#endif
queue_delayed_work(edac_workqueue,&edac_dev->work, edac_dev->delay);
queue_delayed_work(edac_workqueue, &edac_dev->work, edac_dev->delay);
}

/*
* edac_workq_teardown
* edac_device_workq_teardown
* stop the workq processing on this edac_dev
*/
void edac_workq_teardown(struct edac_device_ctl_info *edac_dev)
void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
{
int status;

Expand All @@ -409,10 +410,10 @@ void edac_device_reset_delay_period(
lock_device_list();

/* cancel the current workq request */
edac_workq_teardown(edac_dev);
edac_device_workq_teardown(edac_dev);

/* restart the workq request, with new delay value */
edac_workq_setup(edac_dev, value);
edac_device_workq_setup(edac_dev, value);

unlock_device_list();
}
Expand Down Expand Up @@ -479,8 +480,11 @@ int edac_device_add_device(struct edac_device_ctl_info *edac_dev, int edac_idx)
/* This instance is NOW RUNNING */
edac_dev->op_state = OP_RUNNING_POLL;

/* enable workq processing on this instance, default = 1000 msec */
edac_workq_setup(edac_dev, 1000);
/*
* enable workq processing on this instance,
* default = 1000 msec
*/
edac_device_workq_setup(edac_dev, 1000);
} else {
edac_dev->op_state = OP_RUNNING_INTERRUPT;
}
Expand Down Expand Up @@ -538,7 +542,7 @@ struct edac_device_ctl_info * edac_device_del_device(struct device *dev)
edac_dev->op_state = OP_OFFLINE;

/* clear workq processing on this instance */
edac_workq_teardown(edac_dev);
edac_device_workq_teardown(edac_dev);

/* Tear down the sysfs entries for this instance */
edac_device_remove_sysfs(edac_dev);
Expand Down
119 changes: 119 additions & 0 deletions drivers/edac/edac_mc.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
}
}

mci->op_state = OP_ALLOC;

return mci;
}
EXPORT_SYMBOL_GPL(edac_mc_alloc);
Expand Down Expand Up @@ -215,6 +217,107 @@ static struct mem_ctl_info *find_mci_by_dev(struct device *dev)
return NULL;
}

/*
* handler for EDAC to check if NMI type handler has asserted interrupt
*/
static int edac_mc_assert_error_check_and_clear(void)
{
int vreg;

if(edac_op_state == EDAC_OPSTATE_POLL)
return 1;

vreg = atomic_read(&edac_err_assert);
if(vreg) {
atomic_set(&edac_err_assert, 0);
return 1;
}

return 0;
}

/*
* edac_mc_workq_function
* performs the operation scheduled by a workq request
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
static void edac_mc_workq_function(struct work_struct *work_req)
{
struct delayed_work *d_work = (struct delayed_work*) work_req;
struct mem_ctl_info *mci = to_edac_mem_ctl_work(d_work);
#else
static void edac_mc_workq_function(void *ptr)
{
struct mem_ctl_info *mci = (struct mem_ctl_info *) ptr;
#endif

mutex_lock(&mem_ctls_mutex);

/* 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 */
queue_delayed_work(edac_workqueue, &mci->work, edac_mc_get_poll_msec());
}

/*
* edac_mc_workq_setup
* initialize a workq item for this mci
* passing in the new delay period in msec
*/
void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
{
debugf0("%s()\n", __func__);

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
#else
INIT_WORK(&mci->work, edac_mc_workq_function, mci);
#endif
queue_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec));
}

/*
* edac_mc_workq_teardown
* stop the workq processing on this mci
*/
void edac_mc_workq_teardown(struct mem_ctl_info *mci)
{
int status;

status = cancel_delayed_work(&mci->work);
if (status == 0) {
/* workq instance might be running, wait for it */
flush_workqueue(edac_workqueue);
}
}

/*
* edac_reset_delay_period
*/

void edac_reset_delay_period(struct mem_ctl_info *mci, unsigned long value)
{
mutex_lock(&mem_ctls_mutex);

/* cancel the current workq request */
edac_mc_workq_teardown(mci);

/* restart the workq request, with new delay value */
edac_mc_workq_setup(mci, 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.
Expand Down Expand Up @@ -351,6 +454,16 @@ int edac_mc_add_mc(struct mem_ctl_info *mci, int mc_idx)
goto fail1;
}

/* If there IS a check routine, then we are running POLLED */
if (mci->edac_check != NULL) {
/* This instance is NOW RUNNING */
mci->op_state = OP_RUNNING_POLL;

edac_mc_workq_setup(mci, edac_mc_get_poll_msec());
} else {
mci->op_state = OP_RUNNING_INTERRUPT;
}

/* 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));
Expand Down Expand Up @@ -386,6 +499,12 @@ struct mem_ctl_info * edac_mc_del_mc(struct device *dev)
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);
Expand Down
14 changes: 10 additions & 4 deletions drivers/edac/edac_mc_sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,28 @@ static int panic_on_ue;
static int poll_msec = 1000;

/* Getter functions for above */
int edac_get_log_ue()
int edac_get_log_ue(void)
{
return log_ue;
}

int edac_get_log_ce()
int edac_get_log_ce(void)
{
return log_ce;
}

int edac_get_panic_on_ue()
int edac_get_panic_on_ue(void)
{
return panic_on_ue;
}

int edac_get_poll_msec()
/* this is temporary */
int edac_mc_get_poll_msec(void)
{
return edac_get_poll_msec();
}

int edac_get_poll_msec(void)
{
return poll_msec;
}
Expand Down
Loading

0 comments on commit 81d87cb

Please sign in to comment.