Skip to content

Commit 3358f55

Browse files
mhennerichdNechita
authored andcommitted
libiio: backend: Add atomic register access support for local backend
This patch introduces thread-safe register read/write operations for the local backend using `direct_reg_access` via debugfs. It adds two new backend ops: - `reg_read()` - `reg_write()` These functions use file locking (`flock`) to ensure atomic access and prevent race conditions when multiple threads interact with the same register interface. The fallback logic in `iio_device_reg_read()` and `iio_device_reg_write()` is preserved for backends that do not implement these atomic operations. This improves reliability for multi-threaded applications using libiio with the local backend. Signed-off-by: Michael Hennerich <[email protected]>
1 parent 7371098 commit 3358f55

File tree

3 files changed

+135
-5
lines changed

3 files changed

+135
-5
lines changed

device.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,15 @@ int iio_device_reg_write(struct iio_device *dev,
299299
uint32_t address, uint32_t value)
300300
{
301301
const struct iio_attr *attr;
302+
const struct iio_backend_ops *ops = dev->ctx->ops;
303+
304+
/* Use atomic backend operation if available (thread-safe for local backend) */
305+
if (ops && ops->reg_write) {
306+
return ops->reg_write(dev, address, value);
307+
}
308+
309+
/* Fallback to the original implementation for other backends.
310+
* NOTE: This has a potential race condition with reg_read operations. */
302311
ssize_t ret;
303312
char buf[1024];
304313

@@ -317,11 +326,16 @@ int iio_device_reg_write(struct iio_device *dev,
317326
int iio_device_reg_read(struct iio_device *dev,
318327
uint32_t address, uint32_t *value)
319328
{
320-
/* NOTE: There is a race condition here. But it is extremely unlikely to
321-
* happen, and as this is a debug function, it shouldn't be used for
322-
* something else than debug. */
323-
324329
const struct iio_attr *attr;
330+
const struct iio_backend_ops *ops = dev->ctx->ops;
331+
332+
/* Use atomic backend operation if available (thread-safe for local backend) */
333+
if (ops && ops->reg_read) {
334+
return ops->reg_read(dev, address, value);
335+
}
336+
337+
/* Fallback to the original implementation for other backends.
338+
* NOTE: This has a race condition but it's unlikely to happen in practice. */
325339
long long val;
326340
int ret;
327341

include/iio/iio-backend.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ struct iio_backend_ops {
138138
int (*read_ev)(struct iio_event_stream_pdata *pdata,
139139
struct iio_event *out_event,
140140
bool nonblock);
141+
142+
/* Atomic register operations - optional, for thread-safe local backend */
143+
int (*reg_read)(const struct iio_device *dev, uint32_t address, uint32_t *value);
144+
int (*reg_write)(const struct iio_device *dev, uint32_t address, uint32_t value);
141145
};
142146

143147
/**

local.c

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
#include <time.h>
3535
#include <unistd.h>
3636
#include <fcntl.h>
37+
#include <sys/file.h>
38+
#include <inttypes.h>
3739

3840
#define NB_BLOCKS 4
3941

@@ -495,7 +497,6 @@ static ssize_t local_do_read_dev_attr(const char *id, unsigned int buf_id,
495497
else
496498
dst[0] = '\0';
497499

498-
fflush(f);
499500
if (ferror(f))
500501
ret = -errno;
501502
fclose(f);
@@ -639,6 +640,115 @@ static int local_set_trigger(const struct iio_device *dev,
639640
return 0;
640641
}
641642

643+
static int local_reg_read(const struct iio_device *dev,
644+
uint32_t address, uint32_t *value)
645+
{
646+
char buf[1024];
647+
char addr_str[32];
648+
char val_str[32];
649+
FILE *f;
650+
int fd;
651+
ssize_t ret;
652+
653+
iio_snprintf(buf, sizeof(buf), "/sys/kernel/debug/iio/%s/direct_reg_access",
654+
dev->id);
655+
656+
f = fopen(buf, "r+");
657+
if (!f)
658+
return -errno;
659+
660+
fd = fileno(f);
661+
662+
/* Get exclusive lock for atomic write-then-read */
663+
if (flock(fd, LOCK_EX) < 0) {
664+
ret = -errno;
665+
fclose(f);
666+
return ret;
667+
}
668+
669+
iio_snprintf(addr_str, sizeof(addr_str), "0x%" PRIx32, address);
670+
ret = fwrite(addr_str, 1, strlen(addr_str), f);
671+
fflush(f);
672+
if ((size_t)ret < strlen(addr_str)) {
673+
if (ferror(f))
674+
ret = -errno;
675+
else
676+
ret = -EIO;
677+
678+
goto unlock;
679+
}
680+
681+
rewind(f);
682+
ret = fread(val_str, 1, sizeof(val_str) - 1, f);
683+
if (!feof(f)){
684+
ret = -EFBIG;
685+
goto unlock;
686+
}
687+
if (ferror(f)) {
688+
ret = -errno;
689+
goto unlock;
690+
}
691+
692+
if (ret > 0)
693+
val_str[ret - 1] = '\0';
694+
else
695+
val_str[0] = '\0';
696+
697+
if (sscanf(val_str, "0x%x", value) != 1) {
698+
ret = -EIO;
699+
goto unlock;
700+
}
701+
702+
ret = 0;
703+
704+
unlock:
705+
flock(fd, LOCK_UN);
706+
fclose(f);
707+
return ret;
708+
}
709+
710+
static int local_reg_write(const struct iio_device *dev,
711+
uint32_t address, uint32_t value)
712+
{
713+
char buf[1024];
714+
char write_str[64];
715+
FILE *f;
716+
int fd;
717+
ssize_t ret;
718+
719+
iio_snprintf(buf, sizeof(buf), "/sys/kernel/debug/iio/%s/direct_reg_access",
720+
dev->id);
721+
722+
f = fopen(buf, "w");
723+
if (!f)
724+
return -errno;
725+
726+
fd = fileno(f);
727+
728+
/* Get exclusive lock for atomic write operation */
729+
if (flock(fd, LOCK_EX) < 0) {
730+
ret = -errno;
731+
fclose(f);
732+
return ret;
733+
}
734+
735+
iio_snprintf(write_str, sizeof(write_str), "0x%" PRIx32 " 0x%" PRIx32, address, value);
736+
ret = fwrite(write_str, 1, strlen(write_str), f);
737+
fflush(f);
738+
if ((size_t)ret < strlen(write_str)) {
739+
if (ferror(f))
740+
ret = -errno;
741+
else
742+
ret = -EIO;
743+
} else {
744+
ret = 0;
745+
}
746+
747+
flock(fd, LOCK_UN);
748+
fclose(f);
749+
return ret;
750+
}
751+
642752
static bool is_channel(const struct iio_device *dev, const char *attr, bool strict)
643753
{
644754
char *ptr = NULL;
@@ -1768,6 +1878,8 @@ static const struct iio_backend_ops local_ops = {
17681878

17691879
.get_dmabuf_fd = local_get_dmabuf_fd,
17701880
.disable_cpu_access = local_disable_cpu_access,
1881+
.reg_read = local_reg_read,
1882+
.reg_write = local_reg_write,
17711883
};
17721884

17731885
const struct iio_backend iio_local_backend = {

0 commit comments

Comments
 (0)