Commit 3388e161 authored by Federico Vaga's avatar Federico Vaga

sw: new sync message implementation

Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent 58d2c00a
......@@ -218,6 +218,9 @@ This *char device* provides a set of ``ioctl(2)`` commands:
.. doxygendefine:: TRTL_IOCTL_MSG_FILTER_CLEAN
.. doxygendefine:: TRTL_IOCTL_HMQ_SYNC_SET
.. doxygendefine:: TRTL_IOCTL_MSG_SYNC_ABORT
You can find the HMQ *sysfs* attributes at::
......
......@@ -227,6 +227,8 @@ enum trtl_ioctl_commands {
TRTL_SMEM_IO, /**< access to shared memory */
TRTL_MSG_FILTER_ADD, /**< add a message filter */
TRTL_MSG_FILTER_CLEAN, /**< remove all filters */
TRTL_MSG_SYNC_ABORT, /**< abort sync message*/
TRTL_HMQ_SYNC_SET, /**< Mark HMQ user context as synchronous */
};
......@@ -247,7 +249,19 @@ enum trtl_ioctl_commands {
#define TRTL_IOCTL_MSG_FILTER_CLEAN _IOW(TRTL_IOCTL_MAGIC, \
TRTL_MSG_FILTER_CLEAN, \
struct trtl_msg_filter)
/**
* The IOCTL command to add a filter to an HMQ
*/
#define TRTL_IOCTL_HMQ_SYNC_SET _IOW(TRTL_IOCTL_MAGIC, \
TRTL_HMQ_SYNC_SET, \
unsigned int)
/**
* The IOCTL command to add a filter to an HMQ
*/
#define TRTL_IOCTL_MSG_SYNC_ABORT _IOW(TRTL_IOCTL_MAGIC, \
TRTL_MSG_SYNC_ABORT, \
unsigned int)
/**
* Messages descriptor for host
......
......@@ -177,11 +177,22 @@ struct trtl_hmq {
struct mturtle_hmq_buffer buf_out;
};
/**
* User do only sync messages
*/
#define TRTL_HMQ_USER_FLAG_SYNC BIT(0)
/**
* User is waiting for a sync answer message
*/
#define TRTL_HMQ_USER_FLAG_SYNC_WAIT BIT(1)
/**
* It describes the consumer of the output slot
* @list: to keep it in our local queue
* @hmq: reference to opened HMQ
* @lock: to protect filters
* @lock: to protect filters, flags, wait_id
* @list_filters: list of filters to apply
* @n_filters: number of filters
* @idx_r: index read pointer for the message circular buffer. This is
......@@ -194,9 +205,11 @@ struct trtl_hmq_user {
struct list_head list;
struct trtl_hmq *hmq;
spinlock_t lock;
unsigned long flags;
struct list_head list_filters;
unsigned int n_filters;
unsigned int idx_r;
uint16_t wait_id;
};
......
......@@ -29,6 +29,54 @@ MODULE_PARM_DESC(hmq_buf_max_msg,
static void trtl_message_push(struct trtl_hmq *hmq, struct trtl_msg *msg);
static bool trtl_hmq_user_is_sync(struct trtl_hmq_user *user)
{
return (user->flags & TRTL_HMQ_USER_FLAG_SYNC);
}
static bool trtl_hmq_user_is_waiting(struct trtl_hmq_user *user)
{
return (user->flags & TRTL_HMQ_USER_FLAG_SYNC_WAIT);
}
static bool trtl_msg_is_ack(struct trtl_msg *msg)
{
return (msg->hdr.flags & TRTL_HMQ_HEADER_FLAG_ACK);
}
static bool trtl_hmq_user_msg_is_answer(struct trtl_hmq_user *user,
struct trtl_msg *msg)
{
return (user->wait_id == msg->hdr.sync_id);
}
/**
* Set the user in waiting mode for a sync answer
* @user user
* @sync_id synchronous message identifier
*
* Return: 0 on success otherwise a negative error number
*/
static void trtl_hmq_user_sync_wait_set(struct trtl_hmq_user *user,
uint16_t id)
{
spin_lock(&user->lock);
user->flags |= TRTL_HMQ_USER_FLAG_SYNC_WAIT;
user->wait_id = id;
spin_unlock(&user->lock);
}
/**
* User received the messages, do not wait anymore
* @user user
*/
static void trtl_hmq_user_sync_wait_clr(struct trtl_hmq_user *user)
{
spin_lock(&user->lock);
user->flags &= ~TRTL_HMQ_USER_FLAG_SYNC_WAIT;
spin_unlock(&user->lock);
}
/**
* Get a valid sequence number
* @trtl: trtl device
......@@ -380,6 +428,27 @@ static int trtl_hmq_filter_check(struct trtl_hmq_user *user,
return passed;
}
/**
* Tell if user accepts the given message
* @user: HMQ user
* @msg: message
*
* Return: true if the user can read the message, otherwise false
*/
static bool trtl_hmq_user_filter_one(struct trtl_hmq_user *user,
struct trtl_msg *msg)
{
if (trtl_hmq_user_is_sync(user)) {
if (trtl_hmq_user_is_waiting(user) &&
trtl_msg_is_ack(msg) &&
trtl_hmq_user_msg_is_answer(user, msg))
return true;
} else {
if (trtl_hmq_filter_check(user, msg))
return true;
}
return false;
}
/**
* It clears the content of the HMQ
......@@ -591,9 +660,10 @@ static int trtl_hmq_release(struct inode *inode, struct file *f)
* @buf: source buffer
* Return: 0 on success, otherwise a negative error number
*/
static int trtl_hmq_write_one(struct trtl_hmq *hmq,
static int trtl_hmq_write_one(struct trtl_hmq_user *user,
const char __user *ubuf)
{
struct trtl_hmq *hmq = user->hmq;
struct trtl_dev *trtl = to_trtl_dev(hmq->dev.parent->parent);
struct trtl_msg *msg;
int err = 0, copy_size;
......@@ -601,6 +671,9 @@ static int trtl_hmq_write_one(struct trtl_hmq *hmq,
struct mturtle_hmq_buffer *buf = &hmq->buf_out;
unsigned long flags;
if (trtl_hmq_user_is_sync(user) && trtl_hmq_user_is_waiting(user))
return -EBUSY;
/* Here we can safely sleep */
size = TRTL_CONFIG_ROM_MQ_SIZE_PAYLOAD(hmq->cfg->sizes);
copy_size = sizeof(struct trtl_hmq_header);
......@@ -617,6 +690,13 @@ static int trtl_hmq_write_one(struct trtl_hmq *hmq,
}
buf->msg_tmp.hdr.seq = trtl_msg_seq_get(trtl);
if (trtl_hmq_user_is_sync(user)) {
uint16_t sync_id = buf->msg_tmp.hdr.seq & 0xFFFF;
buf->msg_tmp.hdr.flags |= TRTL_HMQ_HEADER_FLAG_SYNC;
buf->msg_tmp.hdr.sync_id = sync_id;
trtl_hmq_user_sync_wait_set(user, sync_id);
}
/* don't sleep here */
spin_lock_irqsave(&buf->lock, flags);
......@@ -659,7 +739,7 @@ static ssize_t trtl_hmq_write(struct file *f, const char __user *buf,
count = 0;
mutex_lock(&hmq->mtx);
for (i = 0; i < n_msg; i++, curbuf += sizeof(struct trtl_msg)) {
err = trtl_hmq_write_one(hmq, curbuf);
err = trtl_hmq_write_one(user, curbuf);
if (err)
break;
}
......@@ -789,6 +869,7 @@ static long trtl_hmq_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
return -EFAULT;
/* Perform commands */
mutex_lock(&user->hmq->mtx);
switch (cmd) {
case TRTL_IOCTL_MSG_FILTER_ADD:
err = trtl_ioctl_msg_filter_add(user, uarg);
......@@ -796,10 +877,24 @@ static long trtl_hmq_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
case TRTL_MSG_FILTER_CLEAN:
trtl_ioctl_msg_filter_clean(user, uarg);
break;
case TRTL_IOCTL_MSG_SYNC_ABORT:
if (trtl_hmq_user_is_sync(user)) {
if (trtl_hmq_user_is_waiting(user))
trtl_hmq_user_sync_wait_clr(user);
} else {
err = -EPERM;
}
break;
case TRTL_IOCTL_HMQ_SYNC_SET:
spin_lock(&user->lock);
user->flags |= TRTL_HMQ_USER_FLAG_SYNC;
spin_unlock(&user->lock);
break;
default:
pr_warn("trtl: invalid ioctl command %d\n", cmd);
return -EINVAL;
}
mutex_unlock(&user->hmq->mtx);
return err;
}
......@@ -835,15 +930,20 @@ static ssize_t trtl_hmq_read(struct file *f, char __user *ubuf,
spin_lock_irqsave(&buf->lock, flags);
msg = &buf->msg[usr->idx_r++ & (hmq_buf_max_msg - 1)];
if (!trtl_hmq_filter_check(usr, msg)) {
if (!trtl_hmq_user_filter_one(usr, msg)) {
spin_unlock_irqrestore(&buf->lock, flags);
continue;
}
copy_size = sizeof(struct trtl_hmq_header);
copy_size += (msg->hdr.len * 4);
memcpy(&buf->msg_tmp, msg, copy_size);
spin_unlock_irqrestore(&buf->lock, flags);
if (trtl_hmq_user_is_sync(usr))
trtl_hmq_user_sync_wait_clr(usr);
/* now we copy to user space and we can safely sleep */
if (copy_to_user(ubuf + count, &buf->msg_tmp, copy_size)) {
err = -EFAULT;
......@@ -858,6 +958,7 @@ static ssize_t trtl_hmq_read(struct file *f, char __user *ubuf,
return count ? count : err;
}
/**
* It filters out messages until a valid one
* @usr: pointer to a user HMQ instance
......@@ -873,7 +974,7 @@ static void trtl_hmq_user_filter(struct trtl_hmq_user *usr)
/* Loop until we find a valid message for the user */
while (buf->idx_w != usr->idx_r) {
msg = &buf->msg[usr->idx_r & (hmq_buf_max_msg - 1)];
if (trtl_hmq_filter_check(usr, msg))
if (trtl_hmq_user_filter_one(usr, msg))
break;
usr->idx_r++;
}
......
......@@ -31,9 +31,8 @@ struct trtl_desc {
int fd_dev; /**< File Descriptor of the device */
int fd_cpu[TRTL_MAX_CPU]; /**< File Descriptor of the CPUs */
int fd_hmq[TRTL_MAX_CPU][TRTL_MAX_MQ_CHAN]; /**< File Descriptors for the HMQ */
int fd_hmq_sync[TRTL_MAX_CPU][TRTL_MAX_MQ_CHAN]; /**< File Descriptors for the HMQ in sync mode */
struct trtl_config_rom cfgrom;/**< synthesis configuration */
struct trtl_dev *trtl_sync; /**< token for synchronous operations */
};
#endif
......@@ -51,37 +51,6 @@ static const char * const trtl_error_str[] = {
NULL,
};
/**
* Enumeration for predefined filters
*/
enum trtl_msg_filter_predefined {
TRTL_MSG_FILTER_ASYNC = 0, /**< filter for ASYNC messages */
TRTL_MSG_FILTER_SYNC, /**< filter for SYNC messages */
};
/**
* List predefined filters used within the library
*/
static struct trtl_msg_filter filters[] = {
[TRTL_MSG_FILTER_ASYNC] = {
.flags = TRTL_MSG_FILTER_FLAG_HEADER,
.operation = TRTL_MSG_FILTER_AND,
.word_offset = 0, /* ATTENTION: flags in the message header */
.mask = (TRTL_HMQ_HEADER_FLAG_SYNC | TRTL_HMQ_HEADER_FLAG_ACK) << 16,
.value = 0,
},
[TRTL_MSG_FILTER_SYNC] = {
.flags = TRTL_MSG_FILTER_FLAG_HEADER,
.operation = TRTL_MSG_FILTER_AND,
.word_offset = 0, /* ATTENTION: flags in the message header */
.mask = TRTL_HMQ_HEADER_FLAG_ACK << 16,
.value = TRTL_HMQ_HEADER_FLAG_ACK << 16,
},
};
static int trtl_sysfs_read(char *path, void *buf, size_t len);
static int trtl_sysfs_write(char *path, void *buf, size_t len);
......@@ -207,15 +176,13 @@ void trtl_list_free(char **list)
free(list);
}
/**
* It opens a TRTL device using a string descriptor. The descriptor correspond
* to the main char device name of the Mock-Turtle.
* @param[in] device name of the device to open
*
* @return TRTL token, NULL on error and errno is appropriately set
* @return the TRTL token, NULL on error and errno is appropriately set
*/
static struct trtl_dev *__trtl_open(const char *device)
struct trtl_dev *trtl_open(const char *device)
{
struct trtl_desc *trtl;
char path[TRTL_PATH_LEN + TRTL_NAME_LEN];
......@@ -263,6 +230,20 @@ static struct trtl_dev *__trtl_open(const char *device)
if (fd < 0)
goto out_hmq_fd;
trtl->fd_hmq[i][k] = fd;
fd = open(path, O_RDWR);
if (fd < 0) {
close(trtl->fd_hmq[i][k]);
goto out_hmq_fd;
}
trtl->fd_hmq_sync[i][k] = fd;
err = ioctl(trtl->fd_hmq_sync[i][k],
TRTL_IOCTL_HMQ_SYNC_SET, 1);
if (err) {
close(trtl->fd_hmq[i][k]);
close(trtl->fd_hmq_sync[i][k]);
goto out_hmq_fd;
}
}
}
......@@ -274,6 +255,7 @@ out_hmq_fd:
while(i >= 0) {
while(--k >= 0) {
close(trtl->fd_hmq[i][k]);
close(trtl->fd_hmq_sync[i][k]);
}
--i;
}
......@@ -283,52 +265,6 @@ out_stat:
return NULL;
}
/**
* It opens a TRTL device using a string descriptor. The descriptor correspond
* to the main char device name of the Mock-Turtle.
* @param[in] device name of the device to open
* @return the TRTL token, NULL on error and errno is appropriately set
*/
struct trtl_dev *trtl_open(const char *device)
{
struct trtl_desc *wdesc;
struct trtl_dev *trtl;
int ret, i , k;
trtl = __trtl_open(device);
if (!trtl)
return NULL;
/* quick hack to have a separete buffering for
synchronous messages */
wdesc = (struct trtl_desc *)trtl;
wdesc->trtl_sync = __trtl_open(device);
if (!wdesc->trtl_sync)
goto err_open_sync;
/* set up the filters in order to have synchorouns messages on
a different buffer */
for (i = 0; i < wdesc->cfgrom.n_cpu; ++i) {
for (k = 0; k < wdesc->cfgrom.n_hmq[i]; ++k) {
ret = trtl_hmq_filter_add(trtl, i, k,
&filters[TRTL_MSG_FILTER_ASYNC]);
if (ret < 0)
goto err_filter;
ret = trtl_hmq_filter_add(wdesc->trtl_sync, i, k,
&filters[TRTL_MSG_FILTER_SYNC]);
if (ret < 0)
goto err_filter;
}
}
return trtl;
err_filter:
trtl_close(wdesc->trtl_sync);
err_open_sync:
trtl_close(trtl);
return NULL;
}
/**
* It opens a TRTL device using its device_id. The Mock-Turtle
......@@ -960,6 +896,38 @@ int trtl_hmq_fd(struct trtl_dev *trtl,
return wdesc->fd_hmq[idx_cpu][idx_hmq];
}
/**
* It returns the HMQ File Descriptor
* @param[in] trtl device token
* @param[in] idx_cpu CPU index
* @param[in] idx_hmq HMQ index
* @return the file descriptor
*/
int trtl_hmq_fd_sync(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq)
{
struct trtl_desc *wdesc = (struct trtl_desc *)trtl;
return wdesc->fd_hmq_sync[idx_cpu][idx_hmq];
}
static int trtl_msg_read(int fd, struct trtl_msg *msg, unsigned int n)
{
size_t size = sizeof(*msg);
int ret;
ret = read(fd, msg, size * n);
if (ret < 0)
return ret;
if (ret % size) {
/* most likely a driver problem */
errno = ETRTL_INVALID_MESSAGE;
return -1;
}
return (ret / size);
}
/**
* It reads messages from a given HMQ
......@@ -977,10 +945,7 @@ int trtl_msg_async_recv(struct trtl_dev *trtl,
struct trtl_msg *msg,
unsigned int n)
{
struct trtl_desc *wdesc = (struct trtl_desc *)trtl;
int ret, size, fd;
fd = wdesc->fd_hmq[idx_cpu][idx_hmq];
int fd = trtl_hmq_fd(trtl, idx_cpu, idx_hmq);
/* validation */
if (fd < 0) {
......@@ -988,12 +953,18 @@ int trtl_msg_async_recv(struct trtl_dev *trtl,
return -1;
}
/* Get a message from the driver */
size = sizeof(struct trtl_msg);
ret = read(fd, msg, size * n);
return trtl_msg_read(fd, msg, n);
}
static int trtl_msg_write(int fd, struct trtl_msg *msg, unsigned int n)
{
size_t size = sizeof(*msg);
int ret;
ret = write(fd, msg, size * n);
if (ret < 0)
return ret;
if (ret % sizeof(struct trtl_msg)) {
return -1;
if (ret % size) {
/* most likely a driver problem */
errno = ETRTL_INVALID_MESSAGE;
return -1;
......@@ -1001,8 +972,6 @@ int trtl_msg_async_recv(struct trtl_dev *trtl,
return (ret / size);
}
/**
* It writes messages to a given HMQ
* @param[in] trtl device token
......@@ -1019,31 +988,32 @@ int trtl_msg_async_send(struct trtl_dev *trtl,
struct trtl_msg *msg,
unsigned int n)
{
struct trtl_desc *wdesc = (struct trtl_desc *)trtl;
int ret, size, fd;
fd = wdesc->fd_hmq[idx_cpu][idx_hmq];
int fd = trtl_hmq_fd(trtl, idx_cpu, idx_hmq);
/* validation */
if (fd < 0) {
errno = ETRTL_HMQ_CLOSE;
return -1;
}
/* Get a message from the driver */
size = sizeof(struct trtl_msg);
ret = write(fd, msg, size * n);
if (ret < 0)
return -1;
if (ret % sizeof(struct trtl_msg)) {
/* most likely a driver problem */
errno = ETRTL_INVALID_MESSAGE;
return -1;
}
return (ret / size);
return trtl_msg_write(fd, msg, n);
}
/**
* It adds a new filter to the given hmq descriptor
* @param[in] trtl device token
* @param[in] idx_cpu CPU index
* @param[in] idx_hmq HMQ index
* @param[in] err error code
* @return 0 on success, -1 otherwise and errno is set appropriately
*/
int trtl_msg_sync_abort(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq,
unsigned int err)
{
return ioctl(trtl_hmq_fd_sync(trtl, idx_cpu, idx_hmq),
TRTL_IOCTL_MSG_SYNC_ABORT, err);
}
/**
* It sends and receives a synchronous message. It is up to the user to set the
......@@ -1066,79 +1036,55 @@ int trtl_msg_sync(struct trtl_dev *trtl,
struct trtl_msg *msg_r,
int timeout)
{
struct trtl_desc *wdesc = (struct trtl_desc *)trtl;
struct polltrtl p;
#if 0
struct trtl_msg_filter f_sync = {
.flags = TRTL_MSG_FILTER_FLAG_HEADER,
.operation = TRTL_MSG_FILTER_AND,
.word_offset = 1, /* ATTENTION: sync_id in the message header */
.mask = 0xFFFF0000,
};
#endif
int ret;
msg_s->hdr.flags |= TRTL_HMQ_HEADER_FLAG_SYNC;
#if 0
/* FIXME: commented out as the filter is never removed.
The filter shouldn't be needed as the synchronous messages are
always sent sequentially - if there is only one process. */
f_sync.value = (msg_s->hdr.sync_id << 16);
ret = trtl_hmq_filter_add(wdesc->trtl_sync, idx_cpu, idx_hmq,
&f_sync);
if (ret < 0)
return -1;
#endif
struct pollfd p;
int ret, fd;
/* send message */
ret = trtl_msg_async_send(wdesc->trtl_sync, idx_cpu, idx_hmq, msg_s, 1);
fd = trtl_hmq_fd_sync(trtl, idx_cpu, idx_hmq);
ret = trtl_msg_write(fd, msg_s, 1);
if (ret < 0)
return -1;
goto err;
if (ret == 0) {
errno = ETRTL_MSG_SYNC_FAILED_SEND;
return -1;
goto err;
}
/* wait answer */
p.trtl = wdesc->trtl_sync;
p.idx_cpu = idx_cpu;
p.idx_hmq = idx_hmq;
p.fd = fd;
p.events = POLLIN | POLLERR;
ret = trtl_msg_poll(&p, 1, timeout);
ret = poll(&p, 1, timeout);
if (ret < 0)
return -1;
/* I (git blame) know, this if is over-complicated */
if (ret == 0) {
errno = ETRTL_MSG_SYNC_FAILED_RECV_TIMEOUT;
return -1;
goto err_abort;
}
if ((p.revents & POLLERR)) {
errno = ETRTL_MSG_SYNC_FAILED_RECV_POLLERR;
return -1;
goto err_abort;
}
if (!(p.revents & POLLIN)) {
errno = ETRTL_MSG_SYNC_FAILED_RECV;
return -1;
goto err_abort;
}
/* read the answer */
ret = trtl_msg_async_recv(wdesc->trtl_sync, idx_cpu, idx_hmq, msg_r, 1);
ret = trtl_msg_read(fd, msg_r, 1);
if (ret < 0)
return -1;
goto err;
if (ret == 0) {
errno = ETRTL_MSG_SYNC_FAILED_RECV;
return -1;
}
/* paranoid check, the driver should be right */
if (msg_s->hdr.sync_id != msg_r->hdr.sync_id) {
errno = ETRTL_MSG_SYNC_FAILED_INVAL;
return -1;
goto err;
}
return 0;
err_abort:
trtl_msg_sync_abort(trtl, idx_cpu, idx_hmq, errno);
err:
return -1;
}
......
......@@ -161,6 +161,9 @@ extern int trtl_hmq_filter_clean(struct trtl_dev *trtl,
extern int trtl_hmq_fd(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq);
extern int trtl_hmq_fd_sync(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq);
extern int trtl_msg_async_send(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment