Make ecrt_master_reset available in RT context

It takes the master_sem, so calling it from RT context is not an option.
The reset is done by a workqueue now, which is triggered in a NMI-safe way.

Fixes #103
This commit is contained in:
Bjarne von Horn 2024-02-08 08:57:43 +01:00
parent 1226b9d745
commit d0f4aed39b
3 changed files with 47 additions and 10 deletions

View File

@ -2282,9 +2282,12 @@ static ATTRIBUTES int ec_ioctl_reset(
ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
)
{
down(&master->master_sem);
ecrt_master_reset(master);
up(&master->master_sem);
#ifdef EC_IOCTL_RTDM
/* Xenomai/LXRT is like NMI context, so we do a two-stage schedule. */
irq_work_queue(&master->sc_reset_work_kicker);
#else
schedule_work(&master->sc_reset_work);
#endif
return 0;
}
@ -4720,6 +4723,13 @@ static long ec_ioctl_both(
case EC_IOCTL_MASTER_LINK_STATE:
ret = ec_ioctl_master_link_state(master, arg, ctx);
break;
case EC_IOCTL_RESET:
if (!ctx->writable) {
ret = -EPERM;
break;
}
ret = ec_ioctl_reset(master, arg, ctx);
break;
case EC_IOCTL_REF_CLOCK_TIME:
if (!ctx->writable) {
ret = -EPERM;
@ -5204,13 +5214,6 @@ static long ec_ioctl_nrt
}
ret = ec_ioctl_deactivate(master, arg, ctx);
break;
case EC_IOCTL_RESET:
if (!ctx->writable) {
ret = -EPERM;
break;
}
ret = ec_ioctl_reset(master, arg, ctx);
break;
case EC_IOCTL_SC_SYNC:
if (!ctx->writable) {
ret = -EPERM;

View File

@ -107,6 +107,8 @@ static int ec_master_eoe_thread(void *);
void ec_master_find_dc_ref_clock(ec_master_t *);
void ec_master_clear_device_stats(ec_master_t *);
void ec_master_update_device_stats(ec_master_t *);
static void sc_reset_task_kicker(struct irq_work *work);
static void sc_reset_task(struct work_struct *work);
/****************************************************************************/
@ -316,6 +318,9 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
master->dc_ref_config = NULL;
master->dc_ref_clock = NULL;
INIT_WORK(&master->sc_reset_work, sc_reset_task);
init_irq_work(&master->sc_reset_work_kicker, sc_reset_task_kicker);
// init character device
ret = ec_cdev_init(&master->cdev, master, device_number);
if (ret)
@ -383,6 +388,9 @@ void ec_master_clear(
ec_cdev_clear(&master->cdev);
irq_work_sync(&master->sc_reset_work_kicker);
cancel_work_sync(&master->sc_reset_work);
#ifdef EC_EOE
ec_master_clear_eoe_handlers(master);
#endif
@ -3257,6 +3265,27 @@ void ecrt_master_reset(ec_master_t *master)
/****************************************************************************/
static void sc_reset_task_kicker(struct irq_work *work)
{
struct ec_master *master =
container_of(work, struct ec_master, sc_reset_work_kicker);
schedule_work(&master->sc_reset_work);
}
/****************************************************************************/
static void sc_reset_task(struct work_struct *work)
{
struct ec_master *master =
container_of(work, struct ec_master, sc_reset_work);
down(&master->master_sem);
ecrt_master_reset(master);
up(&master->master_sem);
}
/****************************************************************************/
/** \cond */
EXPORT_SYMBOL(ecrt_master_create_domain);

View File

@ -30,11 +30,13 @@
#define __EC_MASTER_H__
#include <linux/version.h>
#include <linux/irq_work.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/kthread.h>
#include <linux/rtmutex.h>
#include <linux/workqueue.h>
#include "device.h"
#include "domain.h"
@ -294,6 +296,9 @@ struct ec_master {
wait_queue_head_t request_queue; /**< Wait queue for external requests
from user space. */
struct work_struct sc_reset_work; /**< Task to reset slave configuration. */
struct irq_work sc_reset_work_kicker; /**< NMI-Safe kicker to trigger
reset task above. */
};
/****************************************************************************/