Reputation: 179
ALL~!
In the linux system, There is a shared data that should be mutual exclusion. And, that data is shared between kernel space and user space by mmap. So I have programmed as below.
/////////////USER SPACE
void access_shared_data_user(void)
{
//snip..
ioctl(test_fd, IOCTL_DOWN_SEM);
shared_data++;
//some access to shared_data
ioctl(test_fd, IOCTL_UP_SEM);
}<
/////////////KERNEL SPACE
static DEFINE_SEMAPHORE(test_sem);
static long test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)<br>
{
//snip..
switch(cmd)
{
case IOCTL_DOWN_SEM:
down(&test_sem);
break;
case IOCTL_UP_SEM:
up(&test_sem);
break;
//snip..
}
void access_shared_data_kernel(void)
{
//snip..
down(&test_sem);
shared_data++;
//some access to shared_data
up(&test_sem);
}
Above program have worked well as I expected and shared data was protected from many threads.
BTW, above program have a problem. If access_shared_data_kernel is called in atomic context such as IRQ handler then this program has potential for deadlock to happen because semaphore use sleep wait method.
So I tried to use spinlock(spin_lock_irqsave) but it seemed there is no way to use spinlock with user space.
Question: Is there any solution to share the data between user and kernel in atomic context?
Upvotes: 0
Views: 1004
Reputation: 179
Dear Mats,
Thank you for your answer.
I think, down_trylock is the best solution to fix my program.
Actually, I unconditionally tried to return fail in access_shared_data_kernel function in case of calling atomic conterxt.
But when I use down_trylock, it does not have to be unconditional return and it's more efficient.
So, I modified codes as below.
void access_shared_data_kernel(void)
{
//snip..
if(down_trylock(&mmclog_sem)){
if(in_atomic() || in_interrupt() || irqs_disabled()){
printk("Fail to acquire semaphore in atomic context..!\n");
return -1;
}
else
down(&mmclog_sem);
}
shared_data++;
//some access to shared_data
up(&test_sem);
}
Thanks Mats,
Upvotes: 0
Reputation: 129344
If all you want to do is increment or decrement or otherwise trivially modify a single integer type value, then you could use a locked operation. It should work well to use atomic_t
in the kernel, and atmoic builtins in the user-mode application.
Obviously, if you need to do more complex things, such as strcpy
or "replace all A with B" in a string, modify a binary tree or some other work that is "not easy to make into a single operation", then the solution is to use a semaphore. But as you say, that's not going to work from an IRQ (or some other situation when the scheduler is "locked").
Further, you don't really want to "wait in an IRQ for the usermode to rebalance a tree".
I don't think there is any easy solution to fix this from an IRQ - basically, you shouldn't be doing that. You can use down_trylock
in your IRQ - that won't cause your IRQ to sleep, and if the semaphore isn't available, it returns 0 if successfully acquired the lock, non-zero if the lock was already held by something else.
Now, the tricky part is of course "what do you do if the lock is not available" - and I can't really give a proper answer for your particular case. The typical solution is to queue up a workitem and process it at a later point.
Edit: The standard way of performing the "queue a workite and process later" is to use a tasklet
Upvotes: 2