我正在实现单生产者,单消费者的ptr_ring缓冲区。 this linux driver code中是否真的存在数据竞赛?
我之所以这样问,是因为ThreadSanitizer gives me error并且在启动ptr_ring代码实现后系统崩溃了。
我在push_circ_queue(),pop_circ_queue()和free_circ_queue()中添加了锁。为什么仍然发生数据竞争?
编辑:
我想我为什么仍然有数据竞赛
我有两个不同的ptr_ring实例,分别用于发送(chnl_send)和接收(chnl_recv)。
同一ptr_ring的
push_circ_queue()和pop_circ_queue()不应由Interupt处理程序(push)和相应的线程(pop)同时执行
在这种情况下,我该如何编码互斥锁代码?
使push_circ_queue(ptr_ring A)和pop_circ_queue(ptr_ring A)互斥体不能同时执行
与push_circ_queue(ptr_ring B)和pop_circ_queue(ptr_ring B)相同
但是当push_circ_queue()位于中断处理程序内部时,问题变得更加严重
有帮助吗?
/*
* Filename: circ_ring.c
* Version: 1.0
* Description: A circular buffer using API from
* https://github.com/torvalds/linux/blob/master/include/linux/ptr_ring.h
*/
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ptr_ring.h>
#include "circ_ring.h"
//#include <assert.h>
#define DEBUG 1
#ifdef DEBUG
#define DEBUG_MSG(...) printk(__VA_ARGS__)
#else
#define DEBUG_MSG(...)
#endif
struct mutex lock_push;
struct mutex lock_pop;
struct mutex lock_cleanup;
struct ptr_ring * init_circ_queue(int len)
{
struct ptr_ring * q;
q = kzalloc(sizeof(struct ptr_ring), GFP_KERNEL);
if (q == NULL) {
DEBUG_MSG(KERN_ERR "Not enough memory to allocate ptr_ring");
return NULL;
}
// creates an array of length 'len' where each array location can store a struct * item
if(ptr_ring_init(q, len, GFP_KERNEL) != 0) {
DEBUG_MSG(KERN_ERR "Not enough memory to allocate ptr_ring array");
return NULL;
}
return q;
}
inline int push_circ_queue(struct ptr_ring * buffer, struct item * item_push)
{
mutex_lock(&lock_push);
/* insert one item into the buffer */
if(ptr_ring_produce_any(buffer, item_push) == 0) // operation is successful
{
DEBUG_MSG(KERN_INFO "Successfully pushed val1 = %u and val2 = %u\n", item_push->val1, item_push->val2);
mutex_unlock(&lock_push);
return 0;
}
else {
DEBUG_MSG(KERN_INFO "full, not enough buffer space\n");
mutex_unlock(&lock_push);
return 1;
}
}
inline int pop_circ_queue(struct ptr_ring * buffer, struct item * item_pop)
{
mutex_lock(&lock_pop);
struct item * item_temp;
/* extract one item struct containing two unsigned integers from the buffer */
item_temp = (struct item *)ptr_ring_consume_any(buffer);
if(item_temp) // (!= NULL)
{
item_pop->val1 = item_temp->val1;
item_pop->val2 = item_temp->val2;
// val1 will never be zero since the event number starts from 1 (so, val1 in push_circ_queue() will not be zero,
// same case after pop_circ_queue()), and 0 is only possible during initialization, not during pop_circ_queue()
//assert(item_pop->val1 != 0);
DEBUG_MSG(KERN_INFO "Before pop, head = %u , tail = %u\n", buffer->consumer_head, buffer->consumer_tail);
DEBUG_MSG(KERN_INFO "val1 = %u , val2 = %u\n", item_pop->val1, item_pop->val2);
DEBUG_MSG(KERN_INFO "After pop, head = %u , tail = %u\n", buffer->consumer_head, buffer->consumer_tail);
mutex_unlock(&lock_pop);
return 0;
}
else {
//DEBUG_MSG(KERN_INFO "empty, nothing to pop from the ring\n");
mutex_unlock(&lock_pop);
return 1;
}
}
void free_circ_queue(struct ptr_ring * q)
{
mutex_lock(&lock_cleanup);
ptr_ring_cleanup(q, NULL);
mutex_unlock(&lock_cleanup);
}