18c2ecf20Sopenharmony_ci====================
28c2ecf20Sopenharmony_ciTCM Userspace Design
38c2ecf20Sopenharmony_ci====================
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci.. Contents:
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci   1) Design
98c2ecf20Sopenharmony_ci     a) Background
108c2ecf20Sopenharmony_ci     b) Benefits
118c2ecf20Sopenharmony_ci     c) Design constraints
128c2ecf20Sopenharmony_ci     d) Implementation overview
138c2ecf20Sopenharmony_ci        i. Mailbox
148c2ecf20Sopenharmony_ci        ii. Command ring
158c2ecf20Sopenharmony_ci        iii. Data Area
168c2ecf20Sopenharmony_ci     e) Device discovery
178c2ecf20Sopenharmony_ci     f) Device events
188c2ecf20Sopenharmony_ci     g) Other contingencies
198c2ecf20Sopenharmony_ci   2) Writing a user pass-through handler
208c2ecf20Sopenharmony_ci     a) Discovering and configuring TCMU uio devices
218c2ecf20Sopenharmony_ci     b) Waiting for events on the device(s)
228c2ecf20Sopenharmony_ci     c) Managing the command ring
238c2ecf20Sopenharmony_ci   3) A final note
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ciDesign
278c2ecf20Sopenharmony_ci======
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciTCM is another name for LIO, an in-kernel iSCSI target (server).
308c2ecf20Sopenharmony_ciExisting TCM targets run in the kernel.  TCMU (TCM in Userspace)
318c2ecf20Sopenharmony_ciallows userspace programs to be written which act as iSCSI targets.
328c2ecf20Sopenharmony_ciThis document describes the design.
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ciThe existing kernel provides modules for different SCSI transport
358c2ecf20Sopenharmony_ciprotocols.  TCM also modularizes the data storage.  There are existing
368c2ecf20Sopenharmony_cimodules for file, block device, RAM or using another SCSI device as
378c2ecf20Sopenharmony_cistorage.  These are called "backstores" or "storage engines".  These
388c2ecf20Sopenharmony_cibuilt-in modules are implemented entirely as kernel code.
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ciBackground
418c2ecf20Sopenharmony_ci----------
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ciIn addition to modularizing the transport protocol used for carrying
448c2ecf20Sopenharmony_ciSCSI commands ("fabrics"), the Linux kernel target, LIO, also modularizes
458c2ecf20Sopenharmony_cithe actual data storage as well. These are referred to as "backstores"
468c2ecf20Sopenharmony_cior "storage engines". The target comes with backstores that allow a
478c2ecf20Sopenharmony_cifile, a block device, RAM, or another SCSI device to be used for the
488c2ecf20Sopenharmony_cilocal storage needed for the exported SCSI LUN. Like the rest of LIO,
498c2ecf20Sopenharmony_cithese are implemented entirely as kernel code.
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ciThese backstores cover the most common use cases, but not all. One new
528c2ecf20Sopenharmony_ciuse case that other non-kernel target solutions, such as tgt, are able
538c2ecf20Sopenharmony_cito support is using Gluster's GLFS or Ceph's RBD as a backstore. The
548c2ecf20Sopenharmony_citarget then serves as a translator, allowing initiators to store data
558c2ecf20Sopenharmony_ciin these non-traditional networked storage systems, while still only
568c2ecf20Sopenharmony_ciusing standard protocols themselves.
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ciIf the target is a userspace process, supporting these is easy. tgt,
598c2ecf20Sopenharmony_cifor example, needs only a small adapter module for each, because the
608c2ecf20Sopenharmony_cimodules just use the available userspace libraries for RBD and GLFS.
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciAdding support for these backstores in LIO is considerably more
638c2ecf20Sopenharmony_cidifficult, because LIO is entirely kernel code. Instead of undertaking
648c2ecf20Sopenharmony_cithe significant work to port the GLFS or RBD APIs and protocols to the
658c2ecf20Sopenharmony_cikernel, another approach is to create a userspace pass-through
668c2ecf20Sopenharmony_cibackstore for LIO, "TCMU".
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ciBenefits
708c2ecf20Sopenharmony_ci--------
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ciIn addition to allowing relatively easy support for RBD and GLFS, TCMU
738c2ecf20Sopenharmony_ciwill also allow easier development of new backstores. TCMU combines
748c2ecf20Sopenharmony_ciwith the LIO loopback fabric to become something similar to FUSE
758c2ecf20Sopenharmony_ci(Filesystem in Userspace), but at the SCSI layer instead of the
768c2ecf20Sopenharmony_cifilesystem layer. A SUSE, if you will.
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ciThe disadvantage is there are more distinct components to configure, and
798c2ecf20Sopenharmony_cipotentially to malfunction. This is unavoidable, but hopefully not
808c2ecf20Sopenharmony_cifatal if we're careful to keep things as simple as possible.
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ciDesign constraints
838c2ecf20Sopenharmony_ci------------------
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci- Good performance: high throughput, low latency
868c2ecf20Sopenharmony_ci- Cleanly handle if userspace:
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci   1) never attaches
898c2ecf20Sopenharmony_ci   2) hangs
908c2ecf20Sopenharmony_ci   3) dies
918c2ecf20Sopenharmony_ci   4) misbehaves
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci- Allow future flexibility in user & kernel implementations
948c2ecf20Sopenharmony_ci- Be reasonably memory-efficient
958c2ecf20Sopenharmony_ci- Simple to configure & run
968c2ecf20Sopenharmony_ci- Simple to write a userspace backend
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ciImplementation overview
1008c2ecf20Sopenharmony_ci-----------------------
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ciThe core of the TCMU interface is a memory region that is shared
1038c2ecf20Sopenharmony_cibetween kernel and userspace. Within this region is: a control area
1048c2ecf20Sopenharmony_ci(mailbox); a lockless producer/consumer circular buffer for commands
1058c2ecf20Sopenharmony_cito be passed up, and status returned; and an in/out data buffer area.
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ciTCMU uses the pre-existing UIO subsystem. UIO allows device driver
1088c2ecf20Sopenharmony_cidevelopment in userspace, and this is conceptually very close to the
1098c2ecf20Sopenharmony_ciTCMU use case, except instead of a physical device, TCMU implements a
1108c2ecf20Sopenharmony_cimemory-mapped layout designed for SCSI commands. Using UIO also
1118c2ecf20Sopenharmony_cibenefits TCMU by handling device introspection (e.g. a way for
1128c2ecf20Sopenharmony_ciuserspace to determine how large the shared region is) and signaling
1138c2ecf20Sopenharmony_cimechanisms in both directions.
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciThere are no embedded pointers in the memory region. Everything is
1168c2ecf20Sopenharmony_ciexpressed as an offset from the region's starting address. This allows
1178c2ecf20Sopenharmony_cithe ring to still work if the user process dies and is restarted with
1188c2ecf20Sopenharmony_cithe region mapped at a different virtual address.
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ciSee target_core_user.h for the struct definitions.
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ciThe Mailbox
1238c2ecf20Sopenharmony_ci-----------
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ciThe mailbox is always at the start of the shared memory region, and
1268c2ecf20Sopenharmony_cicontains a version, details about the starting offset and size of the
1278c2ecf20Sopenharmony_cicommand ring, and head and tail pointers to be used by the kernel and
1288c2ecf20Sopenharmony_ciuserspace (respectively) to put commands on the ring, and indicate
1298c2ecf20Sopenharmony_ciwhen the commands are completed.
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_civersion - 1 (userspace should abort if otherwise)
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ciflags:
1348c2ecf20Sopenharmony_ci    - TCMU_MAILBOX_FLAG_CAP_OOOC:
1358c2ecf20Sopenharmony_ci	indicates out-of-order completion is supported.
1368c2ecf20Sopenharmony_ci	See "The Command Ring" for details.
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cicmdr_off
1398c2ecf20Sopenharmony_ci	The offset of the start of the command ring from the start
1408c2ecf20Sopenharmony_ci	of the memory region, to account for the mailbox size.
1418c2ecf20Sopenharmony_cicmdr_size
1428c2ecf20Sopenharmony_ci	The size of the command ring. This does *not* need to be a
1438c2ecf20Sopenharmony_ci	power of two.
1448c2ecf20Sopenharmony_cicmd_head
1458c2ecf20Sopenharmony_ci	Modified by the kernel to indicate when a command has been
1468c2ecf20Sopenharmony_ci	placed on the ring.
1478c2ecf20Sopenharmony_cicmd_tail
1488c2ecf20Sopenharmony_ci	Modified by userspace to indicate when it has completed
1498c2ecf20Sopenharmony_ci	processing of a command.
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ciThe Command Ring
1528c2ecf20Sopenharmony_ci----------------
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ciCommands are placed on the ring by the kernel incrementing
1558c2ecf20Sopenharmony_cimailbox.cmd_head by the size of the command, modulo cmdr_size, and
1568c2ecf20Sopenharmony_cithen signaling userspace via uio_event_notify(). Once the command is
1578c2ecf20Sopenharmony_cicompleted, userspace updates mailbox.cmd_tail in the same way and
1588c2ecf20Sopenharmony_cisignals the kernel via a 4-byte write(). When cmd_head equals
1598c2ecf20Sopenharmony_cicmd_tail, the ring is empty -- no commands are currently waiting to be
1608c2ecf20Sopenharmony_ciprocessed by userspace.
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ciTCMU commands are 8-byte aligned. They start with a common header
1638c2ecf20Sopenharmony_cicontaining "len_op", a 32-bit value that stores the length, as well as
1648c2ecf20Sopenharmony_cithe opcode in the lowest unused bits. It also contains cmd_id and
1658c2ecf20Sopenharmony_ciflags fields for setting by the kernel (kflags) and userspace
1668c2ecf20Sopenharmony_ci(uflags).
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ciCurrently only two opcodes are defined, TCMU_OP_CMD and TCMU_OP_PAD.
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ciWhen the opcode is CMD, the entry in the command ring is a struct
1718c2ecf20Sopenharmony_citcmu_cmd_entry. Userspace finds the SCSI CDB (Command Data Block) via
1728c2ecf20Sopenharmony_citcmu_cmd_entry.req.cdb_off. This is an offset from the start of the
1738c2ecf20Sopenharmony_cioverall shared memory region, not the entry. The data in/out buffers
1748c2ecf20Sopenharmony_ciare accessible via tht req.iov[] array. iov_cnt contains the number of
1758c2ecf20Sopenharmony_cientries in iov[] needed to describe either the Data-In or Data-Out
1768c2ecf20Sopenharmony_cibuffers. For bidirectional commands, iov_cnt specifies how many iovec
1778c2ecf20Sopenharmony_cientries cover the Data-Out area, and iov_bidi_cnt specifies how many
1788c2ecf20Sopenharmony_ciiovec entries immediately after that in iov[] cover the Data-In
1798c2ecf20Sopenharmony_ciarea. Just like other fields, iov.iov_base is an offset from the start
1808c2ecf20Sopenharmony_ciof the region.
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ciWhen completing a command, userspace sets rsp.scsi_status, and
1838c2ecf20Sopenharmony_cirsp.sense_buffer if necessary. Userspace then increments
1848c2ecf20Sopenharmony_cimailbox.cmd_tail by entry.hdr.length (mod cmdr_size) and signals the
1858c2ecf20Sopenharmony_cikernel via the UIO method, a 4-byte write to the file descriptor.
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ciIf TCMU_MAILBOX_FLAG_CAP_OOOC is set for mailbox->flags, kernel is
1888c2ecf20Sopenharmony_cicapable of handling out-of-order completions. In this case, userspace can
1898c2ecf20Sopenharmony_cihandle command in different order other than original. Since kernel would
1908c2ecf20Sopenharmony_cistill process the commands in the same order it appeared in the command
1918c2ecf20Sopenharmony_ciring, userspace need to update the cmd->id when completing the
1928c2ecf20Sopenharmony_cicommand(a.k.a steal the original command's entry).
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ciWhen the opcode is PAD, userspace only updates cmd_tail as above --
1958c2ecf20Sopenharmony_ciit's a no-op. (The kernel inserts PAD entries to ensure each CMD entry
1968c2ecf20Sopenharmony_ciis contiguous within the command ring.)
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ciMore opcodes may be added in the future. If userspace encounters an
1998c2ecf20Sopenharmony_ciopcode it does not handle, it must set UNKNOWN_OP bit (bit 0) in
2008c2ecf20Sopenharmony_cihdr.uflags, update cmd_tail, and proceed with processing additional
2018c2ecf20Sopenharmony_cicommands, if any.
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciThe Data Area
2048c2ecf20Sopenharmony_ci-------------
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ciThis is shared-memory space after the command ring. The organization
2078c2ecf20Sopenharmony_ciof this area is not defined in the TCMU interface, and userspace
2088c2ecf20Sopenharmony_cishould access only the parts referenced by pending iovs.
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ciDevice Discovery
2128c2ecf20Sopenharmony_ci----------------
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ciOther devices may be using UIO besides TCMU. Unrelated user processes
2158c2ecf20Sopenharmony_cimay also be handling different sets of TCMU devices. TCMU userspace
2168c2ecf20Sopenharmony_ciprocesses must find their devices by scanning sysfs
2178c2ecf20Sopenharmony_ciclass/uio/uio*/name. For TCMU devices, these names will be of the
2188c2ecf20Sopenharmony_ciformat::
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	tcm-user/<hba_num>/<device_name>/<subtype>/<path>
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ciwhere "tcm-user" is common for all TCMU-backed UIO devices. <hba_num>
2238c2ecf20Sopenharmony_ciand <device_name> allow userspace to find the device's path in the
2248c2ecf20Sopenharmony_cikernel target's configfs tree. Assuming the usual mount point, it is
2258c2ecf20Sopenharmony_cifound at::
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/sys/kernel/config/target/core/user_<hba_num>/<device_name>
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ciThis location contains attributes such as "hw_block_size", that
2308c2ecf20Sopenharmony_ciuserspace needs to know for correct operation.
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci<subtype> will be a userspace-process-unique string to identify the
2338c2ecf20Sopenharmony_ciTCMU device as expecting to be backed by a certain handler, and <path>
2348c2ecf20Sopenharmony_ciwill be an additional handler-specific string for the user process to
2358c2ecf20Sopenharmony_ciconfigure the device, if needed. The name cannot contain ':', due to
2368c2ecf20Sopenharmony_ciLIO limitations.
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ciFor all devices so discovered, the user handler opens /dev/uioX and
2398c2ecf20Sopenharmony_cicalls mmap()::
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ciwhere size must be equal to the value read from
2448c2ecf20Sopenharmony_ci/sys/class/uio/uioX/maps/map0/size.
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ciDevice Events
2488c2ecf20Sopenharmony_ci-------------
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ciIf a new device is added or removed, a notification will be broadcast
2518c2ecf20Sopenharmony_ciover netlink, using a generic netlink family name of "TCM-USER" and a
2528c2ecf20Sopenharmony_cimulticast group named "config". This will include the UIO name as
2538c2ecf20Sopenharmony_cidescribed in the previous section, as well as the UIO minor
2548c2ecf20Sopenharmony_cinumber. This should allow userspace to identify both the UIO device and
2558c2ecf20Sopenharmony_cithe LIO device, so that after determining the device is supported
2568c2ecf20Sopenharmony_ci(based on subtype) it can take the appropriate action.
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ciOther contingencies
2608c2ecf20Sopenharmony_ci-------------------
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ciUserspace handler process never attaches:
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci- TCMU will post commands, and then abort them after a timeout period
2658c2ecf20Sopenharmony_ci  (30 seconds.)
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ciUserspace handler process is killed:
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci- It is still possible to restart and re-connect to TCMU
2708c2ecf20Sopenharmony_ci  devices. Command ring is preserved. However, after the timeout period,
2718c2ecf20Sopenharmony_ci  the kernel will abort pending tasks.
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ciUserspace handler process hangs:
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci- The kernel will abort pending tasks after a timeout period.
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ciUserspace handler process is malicious:
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci- The process can trivially break the handling of devices it controls,
2808c2ecf20Sopenharmony_ci  but should not be able to access kernel memory outside its shared
2818c2ecf20Sopenharmony_ci  memory areas.
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ciWriting a user pass-through handler (with example code)
2858c2ecf20Sopenharmony_ci=======================================================
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ciA user process handing a TCMU device must support the following:
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cia) Discovering and configuring TCMU uio devices
2908c2ecf20Sopenharmony_cib) Waiting for events on the device(s)
2918c2ecf20Sopenharmony_cic) Managing the command ring: Parsing operations and commands,
2928c2ecf20Sopenharmony_ci   performing work as needed, setting response fields (scsi_status and
2938c2ecf20Sopenharmony_ci   possibly sense_buffer), updating cmd_tail, and notifying the kernel
2948c2ecf20Sopenharmony_ci   that work has been finished
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ciFirst, consider instead writing a plugin for tcmu-runner. tcmu-runner
2978c2ecf20Sopenharmony_ciimplements all of this, and provides a higher-level API for plugin
2988c2ecf20Sopenharmony_ciauthors.
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ciTCMU is designed so that multiple unrelated processes can manage TCMU
3018c2ecf20Sopenharmony_cidevices separately. All handlers should make sure to only open their
3028c2ecf20Sopenharmony_cidevices, based opon a known subtype string.
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cia) Discovering and configuring TCMU UIO devices::
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci      /* error checking omitted for brevity */
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci      int fd, dev_fd;
3098c2ecf20Sopenharmony_ci      char buf[256];
3108c2ecf20Sopenharmony_ci      unsigned long long map_len;
3118c2ecf20Sopenharmony_ci      void *map;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci      fd = open("/sys/class/uio/uio0/name", O_RDONLY);
3148c2ecf20Sopenharmony_ci      ret = read(fd, buf, sizeof(buf));
3158c2ecf20Sopenharmony_ci      close(fd);
3168c2ecf20Sopenharmony_ci      buf[ret-1] = '\0'; /* null-terminate and chop off the \n */
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci      /* we only want uio devices whose name is a format we expect */
3198c2ecf20Sopenharmony_ci      if (strncmp(buf, "tcm-user", 8))
3208c2ecf20Sopenharmony_ci	exit(-1);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci      /* Further checking for subtype also needed here */
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci      fd = open(/sys/class/uio/%s/maps/map0/size, O_RDONLY);
3258c2ecf20Sopenharmony_ci      ret = read(fd, buf, sizeof(buf));
3268c2ecf20Sopenharmony_ci      close(fd);
3278c2ecf20Sopenharmony_ci      str_buf[ret-1] = '\0'; /* null-terminate and chop off the \n */
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci      map_len = strtoull(buf, NULL, 0);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci      dev_fd = open("/dev/uio0", O_RDWR);
3328c2ecf20Sopenharmony_ci      map = mmap(NULL, map_len, PROT_READ|PROT_WRITE, MAP_SHARED, dev_fd, 0);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci      b) Waiting for events on the device(s)
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci      while (1) {
3388c2ecf20Sopenharmony_ci        char buf[4];
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci        int ret = read(dev_fd, buf, 4); /* will block */
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci        handle_device_events(dev_fd, map);
3438c2ecf20Sopenharmony_ci      }
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cic) Managing the command ring::
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci      #include <linux/target_core_user.h>
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci      int handle_device_events(int fd, void *map)
3518c2ecf20Sopenharmony_ci      {
3528c2ecf20Sopenharmony_ci        struct tcmu_mailbox *mb = map;
3538c2ecf20Sopenharmony_ci        struct tcmu_cmd_entry *ent = (void *) mb + mb->cmdr_off + mb->cmd_tail;
3548c2ecf20Sopenharmony_ci        int did_some_work = 0;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci        /* Process events from cmd ring until we catch up with cmd_head */
3578c2ecf20Sopenharmony_ci        while (ent != (void *)mb + mb->cmdr_off + mb->cmd_head) {
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci          if (tcmu_hdr_get_op(ent->hdr.len_op) == TCMU_OP_CMD) {
3608c2ecf20Sopenharmony_ci            uint8_t *cdb = (void *)mb + ent->req.cdb_off;
3618c2ecf20Sopenharmony_ci            bool success = true;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci            /* Handle command here. */
3648c2ecf20Sopenharmony_ci            printf("SCSI opcode: 0x%x\n", cdb[0]);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci            /* Set response fields */
3678c2ecf20Sopenharmony_ci            if (success)
3688c2ecf20Sopenharmony_ci              ent->rsp.scsi_status = SCSI_NO_SENSE;
3698c2ecf20Sopenharmony_ci            else {
3708c2ecf20Sopenharmony_ci              /* Also fill in rsp->sense_buffer here */
3718c2ecf20Sopenharmony_ci              ent->rsp.scsi_status = SCSI_CHECK_CONDITION;
3728c2ecf20Sopenharmony_ci            }
3738c2ecf20Sopenharmony_ci          }
3748c2ecf20Sopenharmony_ci          else if (tcmu_hdr_get_op(ent->hdr.len_op) != TCMU_OP_PAD) {
3758c2ecf20Sopenharmony_ci            /* Tell the kernel we didn't handle unknown opcodes */
3768c2ecf20Sopenharmony_ci            ent->hdr.uflags |= TCMU_UFLAG_UNKNOWN_OP;
3778c2ecf20Sopenharmony_ci          }
3788c2ecf20Sopenharmony_ci          else {
3798c2ecf20Sopenharmony_ci            /* Do nothing for PAD entries except update cmd_tail */
3808c2ecf20Sopenharmony_ci          }
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci          /* update cmd_tail */
3838c2ecf20Sopenharmony_ci          mb->cmd_tail = (mb->cmd_tail + tcmu_hdr_get_len(&ent->hdr)) % mb->cmdr_size;
3848c2ecf20Sopenharmony_ci          ent = (void *) mb + mb->cmdr_off + mb->cmd_tail;
3858c2ecf20Sopenharmony_ci          did_some_work = 1;
3868c2ecf20Sopenharmony_ci        }
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci        /* Notify the kernel that work has been finished */
3898c2ecf20Sopenharmony_ci        if (did_some_work) {
3908c2ecf20Sopenharmony_ci          uint32_t buf = 0;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci          write(fd, &buf, 4);
3938c2ecf20Sopenharmony_ci        }
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci        return 0;
3968c2ecf20Sopenharmony_ci      }
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ciA final note
4008c2ecf20Sopenharmony_ci============
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ciPlease be careful to return codes as defined by the SCSI
4038c2ecf20Sopenharmony_cispecifications. These are different than some values defined in the
4048c2ecf20Sopenharmony_ciscsi/scsi.h include file. For example, CHECK CONDITION's status code
4058c2ecf20Sopenharmony_ciis 2, not 1.
406