kxo Code Write-Up

What is cdev?

Code Tracing

main.c

重要的變數包含:

kxo_init

kxo 模組第一個被呼叫的函式,呼叫時機是在使用者使用 sudo insmod 時。流程圖如下:







g


cluster_goto

Error



kxo_init

kxo_init



kfifo_alloc

kfifo_alloc



kxo_init->kfifo_alloc





if_kfifo_failed

ret < 0



kfifo_alloc->if_kfifo_failed





alloc_chrdev_region

alloc_chrdev_region



if_chrdev_region_failed

ret == 0



alloc_chrdev_region->if_chrdev_region_failed





cdev_init

cdev_init



cdev_add

cdev_add



cdev_init->cdev_add





if_cdev_add_failed

ret == 0



cdev_add->if_cdev_add_failed





kobject_put

kobject_put



goto

error_cdev

error_region

error_alloc



kobject_put->goto:f1





class_create

class_create



if_class_create_failed

IS_ERR(kxo_class)



class_create->if_class_create_failed





device_create

device_create



device_create_file

device_create_file



device_create->device_create_file





if_device_create_file_failed

ret < 0



device_create_file->if_device_create_file_failed





vmalloc

vmalloc



if_vmalloc_failed

!fast_buf.buf



vmalloc->if_vmalloc_failed





vfree

vfree



device_destroy

device_destroy



vfree->device_destroy





class_destroy

class_destroy



device_destroy->class_destroy





class_destroy->goto:f0





alloc_workqueue

alloc_workqueue



if_alloc_workqueue_failed

!kxo_workqueue



alloc_workqueue->if_alloc_workqueue_failed





negamax_init

negamax_init



mcts_init

mcts_init



negamax_init->mcts_init





init_attr_lock

init_attr_lock



mcts_init->init_attr_lock





init_timer

init_timer



init_attr_lock->init_timer





init_open_cnt

init_open_cnt



init_timer->init_open_cnt





ret

ret



init_open_cnt->ret





MAJOR

MAJOR



MAJOR->cdev_init


  major



out

out



goto:f2->out





if_kfifo_failed->alloc_chrdev_region


false



if_kfifo_failed->ret


  -ENOMEM
true



if_chrdev_region_failed->MAJOR


  dev_id
true



if_chrdev_region_failed->goto:f2


  ret
false



if_cdev_add_failed->kobject_put


  &kxo_cdev.kobj
false



if_cdev_add_failed->class_create


true



if_class_create_failed->device_create


false



if_class_create_failed->goto:f0


  PTR_ERR(kxo_class)
true



if_device_create_file_failed->vmalloc


false



if_device_create_file_failed->goto:f0


true



if_vmalloc_failed->device_destroy


true



if_vmalloc_failed->alloc_workqueue


false



if_alloc_workqueue_failed->vfree


true



if_alloc_workqueue_failed->negamax_init


false



out->ret





參考以下連結:

  1. 呼叫 kfifo_alloc 配置 fifo 記憶體

    • 返回值 < 0 : 失敗, 結束並返回 -ENOMEM,第一個離開點
    • 其他 : do nothing
  2. 呼叫 alloc_chrdev_region。我們不在意 major number 所以交給內核處理,如果在意則要使用 register_chrdev_region

    ​​​​/**
    ​​​​ * alloc_chrdev_region() - register a range of char device numbers
    ​​​​ * @dev: output parameter for first assigned number
    ​​​​ * @baseminor: first of the requested range of minor numbers
    ​​​​ * @count: the number of minor numbers required
    ​​​​ * @name: the name of the associated device or driver
    ​​​​ *
    ​​​​ * Allocates a range of char device numbers.  The major number will be
    ​​​​ * chosen dynamically, and returned (along with the first minor number)
    ​​​​ * in @dev.  Returns zero or a negative error code.
    ​​​​ */
    ​​​​int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
    ​​​​            const char *name)
    

    此處配置名為 kxo 的裝置數量為 NR_KMLDRV (1)。

    • 返回值 0 : 正確,獲取主裝置號
    • 其他 : goto error_alloc
  3. 呼叫 cdev_init 初始化 kxo_cdev 結構體。

    ​​​​/**
    ​​​​ * cdev_init() - initialize a cdev structure
    ​​​​ * @cdev: the structure to initialize
    ​​​​ * @fops: the file_operations for this device
    ​​​​ *
    ​​​​ * Initializes @cdev, remembering @fops, making it ready to add to the
    ​​​​ * system with cdev_add().
    ​​​​ */
    ​​​​void cdev_init(struct cdev *cdev, const struct file_operations *fops)
    
  4. 呼叫 cdev_add 嘗試添加 char device,以 dev_id 作為第一個設備號,連續增加 NR_KMLDRV 個同類型設備。

    ​​​​/**
    ​​​​ * cdev_add() - add a char device to the system
    ​​​​ * @p: the cdev structure for the device
    ​​​​ * @dev: the first device number for which this device is responsible
    ​​​​ * @count: the number of consecutive minor numbers corresponding to this
    ​​​​ *         device
    ​​​​ *
    ​​​​ * cdev_add() adds the device represented by @p to the system, making it
    ​​​​ * live immediately.  A negative error code is returned on failure.
    ​​​​ */
    ​​​​int cdev_add(struct cdev *p, dev_t dev, unsigned count) 
    
    • 返回值 0 : do nothing
    • 其他 : 先用 kobject_putkxo_cdev.kobj 參考記數減去 1,再 goto error region
  5. 呼叫 class_create

    • class 能讓用戶方便管理同類型的裝置
    • 會在 sys/class/<class-name> 下建立目錄
    ​​​​/**
    ​​​​ * class_create - create a struct class structure
    ​​​​ * @name: pointer to a string for the name of this class.
    ​​​​ *
    ​​​​ * This is used to create a struct class pointer that can then be used
    ​​​​ * in calls to device_create().
    ​​​​ *
    ​​​​ * Returns &struct class pointer on success, or ERR_PTR() on error.
    ​​​​ *
    ​​​​ * Note, the pointer created here is to be destroyed when finished by
    ​​​​ * making a call to class_destroy().
    ​​​​ */
    ​​​​struct class *class_create(const char *name)
    
    • 返回值是無效指標 (IS_ERR) : 先獲取錯誤碼後,goto error_cdev
  6. 呼叫 device_create 建立 kxo_dev

    該函式負責註冊設備到 sysfs 中,並在 /dev 建立設備節點。

    此處沒有檢查返回值,是將檢查步驟與以下合併。

  7. 呼叫 device_create_file 在對應 /sys/class/<class-name> 建立文件。

    • 返回值 < 0 : goto error_cdev
    • 其他 : do nothing
  8. 呼叫 vmalloc,大小為一個頁表 (4K)。

    在 Linux 核心中可以透過 kmallocvmalloc 配置記憶體。在老師的共筆中也有談到。

    但建議是 128 KiB 以上才使用 kmalloc?

    • 返回值為空指標 : device_destroy -> class_destroy -> goto error_cdev
    • 其他
  9. 呼叫 alloc_workqueue 建立 kxo_workqueue

    workqueue 允許不同的 work_struct 實例並行,但對於同一個 work_struct 實例同時間僅會由最多一個 CPU 運行。

    注意目前的 WQ_MAX_ACTIVE (512) 是偏好值。

    • 返回值為空指標: 釋放以配置的虛擬記憶體 (vfree) -> device_destroy -> class_destroy -> goto error_cdev
    • 其他: do nothing
  10. 初始化演算法 negamaxmcts

  11. 初始化棋盤 table 和相關變數

由於建立字符設備的步驟是層層推進的,這裡使用 goto 語法,只要將 error 的釋放處理和前面初始化的順序相反,便可以優雅達成釋放與 char device 相關資源也是程式的第二個離開點。

out:
    return ret;
error_cdev:
    cdev_del(&kxo_cdev);
error_region:
    unregister_chrdev_region(dev_id, NR_KMLDRV);
error_alloc:
    kfifo_free(&rx_fifo);
    goto out;

或許能將所有釋放資源部分移動到 goto?

kxo_state_show & kxo_state_store

這兩個函式是透過:

static DEVICE_ATTR_RW(kxo_state);

被註冊到:

struct device_attribute dev_attr_kxo_state

後者也是包含在 DEVICE_ATTR_RW 中。RW 指的是擁有者的權限,該設備權限可被寫為 0644。

game.[ch]

mcts.[ch]

negamax.[ch]

util.h

xo-user.c

xoroshiro.[ch]

zobrist.[ch]