wiki:ioc_device_api

Version 10 (modified by alain, 4 years ago) (diff)

--

IOC device API

A) General principles

This device allows the kernel to access various external mass storage peripherals such as a magnetic hard disk or a SD card, that can store blocks of data in a linear array of sectors indexed by a simple lba (logic block address).

The block size is supposed to be 512 bytes.

It supports a first user API, used by the user-level system calls, implementing four operation types : the IOC_READ, IOC_WRITE, IOC_SYNC_READ, and IOC_SYNC_WRITE operations move a given number of contiguous blocks between the block device and a kernel memory buffer. This API is detailed in section C below.

  • The asynchronous READ and WRITE operations are not directly executed by the client thread. The READ and WRITE requests are registered in the waiting queue rooted in the IOC chdev descriptor. These requests are actually handled by a dedicated server thread running in the cluster containing the chdev descriptor, that call the ioc_driver_cmd() function.
  • The synchronous SYNC_READ and SYNC_WRITE operations does not use the waiting queue, and does not use the server thread. The client thread calls directly the ioc_driver_cmd() function.

Most IOC device implementation have a DMA capability, and the data transfer is not done by the software, but is actually done by the hardware device. The IOC IRQ signaling the transfer completion is routed to the core executing the server thread, and handled by the ioc_driver_isr() function (ISR stand for Interrupt Service Routine).

In the IOC-RDK implementation the IOC block device is actually implemented as physical memory. This IOC-RDK implementation does not use DMA, and does not use an IRQ. The data transfer is directly executed by the ioc_driver_cmd() software function.

To access the various drivers, the IOC device defines a lower-level driver API, that is detailed in section D below.

All IOC device structures and access functions are defined in the dev_ioc.c et dev_ioc.h files.

B) Initialisation

The IOC device dev_ioc_init() function makes the following initializations :

  • It selects a core in cluster containing the IOC chdev to execute the server thread.
  • it links the IOC IRQ to the core executing the server thread.
  • it initialises the IOC specific fields of the chdev descriptor.
  • it initialises the implementation specific IOC hardware device,
  • it initializes the specific software data structures required by the hardware implementation.

It must be called by a local thread.

C) User API

The void dev_ioc_move_data() blocking function moves <count> contiguous blocks of data between the block device, indexed by the <lba> argument, and a kernel buffer, identified by the <buffer_xp> argument.

  • The cmd_type argument defines the operation type.
  • The buffer_xp argument is an extended pointer on the kernel buffer.
  • The lba argument is the first block index in block device.
  • The count argument is the number of blocks to move.

The I/O operation is always blocking for the calling thread, but the blocking policy depends on the operation type.

C.1) Asynchronous operations

Almos-mkh uses the asynchronous, READ & WRITE operations, for most data transfers between the file system on the IOC, and the file system cache in memory. The scenario is the following :

  1. When a client thread request an I/O operation, the request is registered in the ioc_command_t structure embedded in the client thread descriptor, and the client thread registers itself in the waiting queue rooted in the IOC chdev. Then the client thread blocks on the THREAD_BLOCKED_IO condition, and deschedules.
  2. The DEV server thread attached to the IOC device descriptor handles all commands registered in the IOC device waiting queue. For each pending request, it calls the IOC driver CMD (command) function to ask the hardware device to do the transfer. Then, the server thread blocks on the THREAD_BLOCKED_ISR condition, and deschedules.
  3. When the I/O operation completed, the IOC driver ISR (Interrupt Service Routine) signaling completion reactivates the server thread.
  4. The server thread reactivates the client thread, and handle the next request in the IOC waiting queue, or reschedules if the queue is empty.

Note : According to the scheduler policy, the DEV thread has an higher priority than any user thread, but it is not selected when the associated waiting queue is empty.

C.2) Synchronous operations

Almost-mkh uses the synchronous SYNC_READ and SYNC_WRITE operations in the kernel_init() function, or to synchronously update the FAT (both the FAT mapper in memory, and the FAT on IOC device), or the directory files on IOC device.

These synchronous operations use neither the IOC device waiting queue, nor the DEV server thread, and the IRQ. The client thread does not deschedules : it registers the arguments in the IOC command structure embedded in the client thread descriptor, and calls directly the blocking IOC driver CMD function,

D) Driver API

All IOC drivers must define three functions :

  • void ioc_driver_init( chdev_t *ioc_chdev )
  • void ioc_driver_cmd( xptr_t thread_xp )
  • void ioc_driver_isr( chdev_t * ioc_chdev )

The ioc_driver_cmd() function arguments are actually defined in the ioc_command_t structure embedded in the client thread descriptor. One command contains four informations:

  • type : operation type (defined below)
  • count : number of contiguous blocks to be moved.
  • buffer_xp : extended pointer on kernel buffer.
  • lba : logic block address (index of block in hardware device).

The four operation types for the IOC driver(s) are the following:

  • IOC_WRITE : move blocks from a kernel buffer to the hardware device, with a descheduling policy.
  • IOC_READ : move blocks from the hardware device to a kernel buffer, with a descheduling policy.
  • IOC_SYNC_WRITE : move blocks from a kernel buffer to the hardware device, without descheduling.
  • IOC_SYNC_READ : move blocks from the hardware device to a kernel buffer, without descheduling.

For asynchronous operations the ioc_driver_cmd() function is called by the server thread. It must block and deschedule after launching the I/O transfer. The I/O operation status is reported in the command by the ISR, and the server thread is re-activated by the ISR.

For synchronous operations, the ioc_driver_cmd() function is called by the client thread. It mask the IOC IRQ, poll the BDV status register until I/O transfer completion, and report status in the command.