up 1 down

Я реализую буферы ptr_ring для одного производителя и одного потребителя. Есть ли на самом деле гонки данных внутри этот код драйвера Linux ?

Я спросил это, потому что ThreadSanitizer выдает мне ошибку и моя система падает после того, как я начал реализацию кода ptr_ring.

Я добавил блокировки для push_circ_queue(), pop_circ_queue() и free_circ_queue(). Почему гонка данных все еще происходит?

Редактировать:

Я думаю, я понял, почему у меня все еще есть гонка данных

У меня есть два разных экземпляра ptr_ring, для отправки (chnl_send) и получения (chnl_recv) соответственно.

push_circ_queue() и pop_circ_queue() для одного и того же ptr_ring не должны выполняться одновременно обработчиком прерывания (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);
}