# Practical Enhancement of User Experience in NVMe SSDs
## 3 Redesign of the Multi-Queue Block I/O Layer to Improve User Experience  
This section describes the redesign of the multi-queue block I/O layer to improve user experience. The exclusive focus of the multi-queue block I/O layer on the I/O bandwidth may result in a bad user experience. To address this concern, this study aims at optimizing the I/O processing time of user-centric tasks by swiftly sending I/O requests issued by user-centric tasks to the SSD device through the complex multi-queue block I/O layer. We first assign a higher process priority to the user-centric tasks than non-user-centric or background tasks. When a program is launched for the first time or a program running in the background switches to the foreground, the user-centric tasks are automatically assigned a high process priority via a modified shell program. The modified shell can differentiate foreground and background tasks, and easily modify the process priority by using the _septority()_ system calls. This facilitates a faster execution of user-centric tasks compared to non-user-centric ones through the task scheduling of a CPU scheduler, such as the completely fair scheduler. Consequently, a faster issuance of I/O requests from user-centric tasks to the multi-queue block I/O layer is achieved [28; 29]. However, the current Linux kernel does not support any I/O service differentiated by process priority. Thus, this approach is still inadequate to preferentially process I/O requests issued by the user-centric tasks. Furthermore, process priority information disappears at the level of the block I/O layer by default. As the first step to process I/O requests from user-centric tasks preferentially, we passed the priority information to the block I/O layer by adding it to _bio_ and _request_, which are basic structures used for I/O processing in the multi-queue block I/O layer. Once the I/O requests in the SWQ are passed to the SQ via the HWQ, the host loses control over them. To process I/O requests issued by user-centric tasks first, we divide the SWQ into two for every core: one queue is for I/O requests from user-centric tasks, and the other queue is for I/O requests from non-user-centric tasks. By referring the priority information passed through _bio_ and _request_ structure, each I/O request is sent to the appropriate SWQ. If there are I/O requests in the SWQ for user-centric tasks, they are first moved to the HWQ. I/O requests located in the HWQ are moved to the SQ immediately if there is sufficient space in the SQ. If there are other pending I/O requests in the HWQ or/and SQ, such as HWQ\({}_{2}\) and SQ\({}_{2}\) in Figure 2, the I/O requests from user-centric tasks, such as T\({}_{\text{U}}\) in Figure 2, cannot be served until other I/O requests are retrieved from the queue holding them and processed. Moreover, NVMe SSDs typically dispatch I/O requests in multiple SQs in a round-robin fashion. Therefore, the I/O requests from user-centric tasks should be moved to HWQ and SQ with the smallest number of pending I/O requests to facilitate their processing in a minimal time. To this end, we first modified the NVMe device driver of the Linux kernel, as the current NVMe device driver cannot obtain the number of I/O requests pending in the SQ. We measure this number using two pieces of information for each SQ: a _head_ that is recorded to SQ head pointer included in the CQ entry, and a _tail_ managed by the NVMe device driver. Figure 3 demonstrates how latency can be improved in accordance with the proposed scheme. Suppose that a user-centric task T\({}_{\text{U}}\) is running on CPU\({}_{2}\), and other tasks notated as T\({}_{\text{NU}}\) are simultaneously running on other CPUs. In this example, a single I/O request issued by the T\({}_{\text{U}}\) is passed to a separate software queue SWQ\({}_{2}^{\text{U}}\) that is assigned to handle user-centric tasks and mapped to CPU\({}_{2}\).