K1 TCM驱动崩溃

K1的TCM对单次申请的空间大小是否有尺寸要求?当我先申请32KB并释放,再申请512KB空间时,会稳定触发一个Oops。假如我先申请的是64KB或128KB,则不会产生这个问题。

Oops信息如下:

[  274.717106] Unable to handle kernel access to user memory without uaccess routines at virtual address 0000000000060010
[  274.727961] Oops [#1]
[  274.730253] Modules linked in: algif_hash algif_skcipher af_alg 8852bs binfmt_misc sch_fq_codel ip_tables autofs4
[  274.740637] CPU: 1 PID: 1707 Comm: tcm-crash Not tainted 6.6.63 #2.2.7.2
[  274.747399] Hardware name: spacemit k1-x deb1 board (DT)
[  274.752755] epc : add_node.isra.0+0x30/0x120
[  274.757052]  ra : node_fission.isra.0+0xae/0xe2
[  274.761611] epc : ffffffff80894dee ra : ffffffff808959ce sp : ffffffc80813bb60
[  274.768898]  gp : ffffffff82301cf8 tp : ffffffd90e790000 t0 : 0000000000000003
[  274.776165]  t1 : ffffffc80813bc78 t2 : 0000000000000002 s0 : ffffffc80813bb80
[  274.783443]  s1 : ffffffd904889c38 a0 : ffffffd904889c38 a1 : ffffffd904889c20
[  274.790709]  a2 : ffffffff81b1dfb8 a3 : ffffffd90eb3a800 a4 : 0000000000000000
[  274.797975]  a5 : 0000000000060000 a6 : ffffffd90eb3a800 a7 : 0000000000060fff
[  274.805240]  s2 : ffffffd904889c20 s3 : ffffffd90eadae80 s4 : 0000000000060000
[  274.812516]  s5 : ffffffff81cd6d70 s6 : 0000000000000fff s7 : 0000000000060000
[  274.819793]  s8 : ffffffd904889c00 s9 : 0000000000000000 s10: ffffffd920d3de20
[  274.827059]  s11: fffffffffffff000 t3 : ffffffd904889c00 t4 : ffffffc80813bc78
[  274.834325]  t5 : 0000000000000002 t6 : ffffffff81e016a8
[  274.839657] status: 0000000200000120 badaddr: 0000000000060010 cause: 000000000000000d
[  274.847620] [<ffffffff80894dee>] add_node.isra.0+0x30/0x120
[  274.853249] [<ffffffff808959ce>] node_fission.isra.0+0xae/0xe2
[  274.859117] [<ffffffff80895b62>] tcm_mmap+0x160/0x30a
[  274.864199] [<ffffffff801d4304>] mmap_region+0x1d6/0x698
[  274.869550] [<ffffffff801d49e0>] do_mmap+0x21a/0x3a8
[  274.874560] [<ffffffff801adf26>] vm_mmap_pgoff+0xb4/0x138
[  274.879994] [<ffffffff801d1ab2>] ksys_mmap_pgoff+0x11e/0x178
[  274.885689] [<ffffffff800054a0>] __riscv_sys_mmap+0x2a/0x36
[  274.891304] [<ffffffff81017c0a>] do_trap_ecall_u+0x114/0x128
[  274.897003] [<ffffffff810213f2>] ret_from_exception+0x0/0x6e
[  274.902719] Code: 0363 0cf5 611c 0c63 02f5 6998 a021 639c 8763 02f4 (6b88) 7ce3
[  274.910505] ---[ end trace 0000000000000000 ]---
[  274.911614] kauditd_printk_skb: 115 callbacks suppressed
[  274.911626] audit: type=1400 audit(1772776417.007:127): apparmor="DENIED" operation="open" class="file" profile="rsyslogd" name="/run/systemd/sessions/" pid=836 comm=72733A6D61696E20513A526567 requested_mask="r" denied_mask="r" fsuid=102 ouid=0
[  274.942452] audit: type=1400 audit(1772776417.007:128): apparmor="DENIED" operation="open" class="file" profile="rsyslogd" name="/run/systemd/sessions/" pid=836 comm=72733A6D61696E20513A526567 requested_mask="r" denied_mask="r" fsuid=102 ouid=0

复现代码如下:

/// ==== BEGIN list.h ====
#ifndef __AIMM_LIST_H__
#define __AIMM_LIST_H__

#ifdef __cplusplus
extern "C" {
#endif

struct list_head {
    struct list_head *next, *prev;
};

#define offsetof(type, member) ((size_t)&(((type *)0)->member))

#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))

#define list_entry(ptr, type, member) container_of(ptr, type, member)

#define list_first_entry(ptr, type, member) list_entry((ptr)->next, type, member)

#define list_next_entry(pos, member) list_entry((pos)->member.next, typeof(*(pos)), member)

#define list_for_each_entry(pos, head, member) for (pos = list_first_entry(head, typeof(*pos), member); &pos->member != (head); pos = list_next_entry(pos, member))

#define list_for_each_entry_safe(pos, n, head, member)                                                                                                                                                 \
    for (pos = list_first_entry(head, typeof(*pos), member), n = list_next_entry(pos, member); &pos->member != (head); pos = n, n = list_next_entry(n, member))

#define list_prev_entry(pos, member) list_entry((pos)->member.prev, typeof(*(pos)), member)

static inline int list_empty(const struct list_head *head) { return head->next == head; }

static inline int list_is_last(const struct list_head *list, const struct list_head *head) { return list->next == head; }

static void __list_add(struct list_head *node, struct list_head *prev, struct list_head *next) {
    node->next = next;
    node->prev = prev;

    prev->next = node;
    next->prev = node;
}

static void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); }

static void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); }

static void list_del(struct list_head *node) {
    struct list_head *prev = node->prev;
    struct list_head *next = node->next;

    prev->next = next;
    next->prev = prev;
}

static inline void INIT_LIST_HEAD(struct list_head *list) {
    list->next = list;
    list->prev = list;
}

#ifdef __cplusplus
}
#endif

#endif
/// ==== END list.h ====

/// ==== BEGIN tcm.c ====

#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#define TCM_NAME "tcm"
#define IOC_MAGIC 'c'
#define TCM_MEM_SHOW _IOR(IOC_MAGIC, 2, int)
#define TCM_VA_TO_PA _IOR(IOC_MAGIC, 4, int)
#define TCM_REQUEST_MEM _IOR(IOC_MAGIC, 5, int)
#define TCM_RELEASE_MEM _IOR(IOC_MAGIC, 6, int)

#define tcm_mutex_init() pthread_mutex_init(&tcm.mutex, NULL)
#define tcm_mutex_lock() pthread_mutex_lock(&tcm.mutex)
#define tcm_mutex_try_lock() pthread_mutex_trylock(&tcm.mutex)
#define tcm_mutex_unlock() pthread_mutex_unlock(&tcm.mutex)
#define tcm_mutex_deinit() pthread_mutex_destroy(&tcm.mutex)

#define tcm_check_return_val(X, ret)                                                                                                                                                                   \
    do {                                                                                                                                                                                               \
        if (!(X)) {                                                                                                                                                                                    \
            printf("tcm check param err--->fun:%s + line:%d", __func__, __LINE__);                                                                                                                     \
            return ret;                                                                                                                                                                                \
        }                                                                                                                                                                                              \
    } while (0)

#define tcm_check_return(X)                                                                                                                                                                            \
    do {                                                                                                                                                                                               \
        if (!(X)) {                                                                                                                                                                                    \
            printf("tcm check param err--->fun:%s + line:%d", __func__, __LINE__);                                                                                                                     \
            return;                                                                                                                                                                                    \
        }                                                                                                                                                                                              \
    } while (0)

#define timeval_sub(a, b, res)                                                                                                                                                                         \
    do {                                                                                                                                                                                               \
        (res)->tv_sec = (a)->tv_sec - (b)->tv_sec;                                                                                                                                                     \
        (res)->tv_usec = (a)->tv_usec - (b)->tv_usec;                                                                                                                                                  \
        if ((res)->tv_usec < 0) {                                                                                                                                                                      \
            (res)->tv_sec--;                                                                                                                                                                           \
            (res)->tv_usec += 1000000;                                                                                                                                                                 \
        }                                                                                                                                                                                              \
    } while (0)

typedef struct {
    struct list_head list;
    void *ptr;
    size_t size;
} mm_node_t;

typedef struct {
    int inited;
    int debug;
    int fd;
    int block_num;
    pthread_mutex_t mutex;
    struct list_head head;
} tcm_t;

typedef struct {
    void *vaddr;
    void *paddr;
} va_to_pa_msg_t;

static tcm_t tcm;

static void mem_show(void) { ioctl(tcm.fd, TCM_MEM_SHOW, NULL); }

static int mem_open(void) {
    int fd = open("/dev/" TCM_NAME, (O_RDWR | O_SYNC));
    if (fd < 0) {
        printf("open failed(%d)\n", fd);
        return -1;
    }

    tcm.fd = fd;

    return 0;
}

static int mem_close(void) {
    close(tcm.fd);
    return 0;
}

static int add_node(mm_node_t *node) {
    list_add(&node->list, &tcm.head);
    return 0;
}

static int del_node(mm_node_t *node) {
    list_del(&node->list);
    return 0;
}

static mm_node_t *get_node(void *ptr) {
    mm_node_t *node;
    list_for_each_entry(node, &tcm.head, list) {
        if (node->ptr == ptr) {
            return node;
        }
    }

    return NULL;
}

static mm_node_t *match_node(void *ptr) {
    mm_node_t *node;
    list_for_each_entry(node, &tcm.head, list) {
        if ((size_t)ptr >= (size_t)node->ptr && (size_t)ptr < ((size_t)node->ptr + node->size)) {
            return node;
        }
    }

    return NULL;
}

static void *alloc(size_t size) {
    mm_node_t *node = malloc(sizeof(mm_node_t));
    if (!node)
        return NULL;

    void *p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, tcm.fd, 0);
    if (p == MAP_FAILED) {
        free(node);
        printf("%s failed(%ld)\n", __func__, size);
        return NULL;
    }

    node->ptr = p;
    node->size = size;
    tcm_mutex_lock();
    add_node(node);
    tcm_mutex_unlock();

    return p;
}

void *tcm_malloc_sync(size_t size, int timeout) {
    tcm_check_return_val(tcm.inited, NULL);
    struct timeval now;
    struct timeval end;
    struct timeval sub;
    int remain_wait = timeout;

    gettimeofday(&now, NULL);
    void *p = alloc(size);
    while ((p == NULL) && (timeout != 0)) {
        int ret;
        struct pollfd events;
        events.fd = tcm.fd;
        events.events = POLLIN | POLLERR;

        if (tcm.debug)
            printf("thread(%d) %s timeout:%d(ms)\n", getpid(), __func__, remain_wait);

        tcm_mutex_lock();
        if (ioctl(tcm.fd, TCM_REQUEST_MEM, &size) < 0) {
            tcm_mutex_unlock();
            return NULL;
        }

        ret = poll((struct pollfd *)&events, 1, remain_wait);
        if (ret <= 0 && events.revents == POLLERR) {
            tcm_mutex_unlock();
            break;
        }

        if (ioctl(tcm.fd, TCM_RELEASE_MEM, &size) < 0) {
            tcm_mutex_unlock();
            return NULL;
        }
        tcm_mutex_unlock();

        if (tcm.debug)
            printf("thread(%d) %s wait\n", getpid(), __func__);
        p = alloc(size);
        if (p) {
            break;
        }

        if (tcm.debug)
            printf("thread(%d) %s failed\n", getpid(), __func__);
        if (timeout != -1) {
            gettimeofday(&end, NULL);
            timeval_sub(&end, &now, &sub);
            long elapsed = end.tv_sec * 1000 + end.tv_usec / 1000;
            if (elapsed > timeout) {
                if (tcm.debug)
                    printf("thread(%d) %s timeout\n", getpid(), __func__);
                break;
            } else {
                remain_wait = (timeout - elapsed);
            }
        }
    }

    return p;
}

void *tcm_malloc(size_t size) {
    tcm_check_return_val(tcm.inited, NULL);

    return alloc(size);
}

void *tcm_calloc(size_t nmemb, size_t size) {
    tcm_check_return_val(tcm.inited, NULL);

    return alloc(size * nmemb);
}

void tcm_free(void *ptr) {
    tcm_check_return(tcm.inited);

    mm_node_t *node = get_node(ptr);
    if (!node)
        return;
    munmap(ptr, node->size);
    tcm_mutex_lock();
    del_node(node);
    tcm_mutex_unlock();
    free(node);
}

int is_tcm_mm(void *p) {
    mm_node_t *node = match_node(p);
    return node ? 0 : -1;
}

void *tcm_va_to_pa(void *va) {
    va_to_pa_msg_t msg;

    msg.vaddr = va;
    if (ioctl(tcm.fd, TCM_VA_TO_PA, &msg) < 0) {
        return NULL;
    }

    return msg.paddr;
}

void tcm_mm_show(void) { mem_show(); }

int tcm_init(void) {
    int ret = 0;

    if (!tcm.inited) {
        ret = mem_open();
        if (ret == 0) {
            INIT_LIST_HEAD(&tcm.head);
            tcm_mutex_init();
            tcm.inited = 1;
            tcm.debug = 1;
        }
    }

    return ret;
}

int tcm_deinit(void) {
    if (tcm.inited) {
        mem_close();
        tcm_mutex_deinit();
        tcm.inited = 0;
        return 0;
    }

    return -1;
}

/// ==== END tcm.c ====

#include <stdio.h>

int error_code;

int sizes_kb[] = {32, 512};

int main() {
    if ((error_code = tcm_init())) {
        printf("TCM init failed. (Error code is %d)\n", error_code);
        return 1;
    }
    int num_tests = sizeof(sizes_kb) / sizeof(sizes_kb[0]);
    for (int i = 0; i < num_tests; i++) {
        printf("Running with size = %dKB... ", sizes_kb[i]);
        fflush(stdout);
        int size = sizes_kb[i] * 1024;
        void *buf = tcm_malloc(size);
        if (!buf) {
            printf("malloc failed\n");
            continue;
        }
        tcm_free(buf);
        printf("OK\n");
    }
    if ((error_code = tcm_deinit())) {
        printf("TCM deinit failed. (Error code is %d)\n", error_code);
        return 1;
    }
    return 0;
}

出错的直接原因是alloc的size非tcm的block size,k1的tcm block size是128KB,建议使用aimm_tcm_malloc,如果确实有需求要申请其他的size,可以更改下驱动drivers/misc/tcm.c