From patchwork Fri May 5 02:51:26 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Xiubo Li X-Patchwork-Id: 9713023 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 032D76020B for ; Fri, 5 May 2017 02:51:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DB87A223B2 for ; Fri, 5 May 2017 02:51:39 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CE98428676; Fri, 5 May 2017 02:51:39 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id ED2EE223B2 for ; Fri, 5 May 2017 02:51:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754369AbdEECvi (ORCPT ); Thu, 4 May 2017 22:51:38 -0400 Received: from cmccmta3.chinamobile.com ([221.176.66.81]:19241 "EHLO cmccmta3.chinamobile.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754206AbdEECvh (ORCPT ); Thu, 4 May 2017 22:51:37 -0400 Received: from spf.mail.chinamobile.com (unknown[172.16.121.7]) by rmmx-syy-dmz-app10-12010 (RichMail) with SMTP id 2eea590be8b445c-8ae1a; Fri, 05 May 2017 10:51:32 +0800 (CST) X-RM-TRANSID: 2eea590be8b445c-8ae1a X-RM-SPAM-FLAG: 00000000 Received: from promote.cache-dns.local.cache-dns.local (unknown[223.105.0.130]) by rmsmtp-syy-appsvr04-12004 (RichMail) with SMTP id 2ee4590be8ae9aa-e6d7c; Fri, 05 May 2017 10:51:28 +0800 (CST) X-RM-TRANSID: 2ee4590be8ae9aa-e6d7c From: lixiubo@cmss.chinamobile.com To: mchristi@redhat.com, nab@linux-iscsi.org Cc: agrover@redhat.com, namei.unix@gmail.com, sheng@yasker.org, linux-scsi@vger.kernel.org, target-devel@vger.kernel.org, linux-kernel@vger.kernel.org, Xiubo Li Subject: [PATCH] tcmu: Add fifo type waiter list support to avoid starvation Date: Fri, 5 May 2017 10:51:26 +0800 Message-Id: <1493952686-29396-1-git-send-email-lixiubo@cmss.chinamobile.com> X-Mailer: git-send-email 1.8.3.1 Sender: target-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: target-devel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Xiubo Li The fifo type waiter list will hold the udevs who are waiting for the blocks from the data global pool. The unmap thread will try to feed the first udevs in waiter list, if the global free blocks available are not enough, it will stop traversing the list and abort waking up the others. Signed-off-by: Xiubo Li --- drivers/target/target_core_user.c | 82 +++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 9045837..10c99e7 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -97,6 +97,7 @@ struct tcmu_hba { struct tcmu_dev { struct list_head node; + struct list_head waiter; struct se_device se_dev; @@ -123,7 +124,7 @@ struct tcmu_dev { wait_queue_head_t wait_cmdr; struct mutex cmdr_lock; - bool waiting_global; + uint32_t waiting_blocks; uint32_t dbi_max; uint32_t dbi_thresh; DECLARE_BITMAP(data_bitmap, DATA_BLOCK_BITS); @@ -165,6 +166,10 @@ struct tcmu_cmd { static DEFINE_MUTEX(root_udev_mutex); static LIST_HEAD(root_udev); +/* The data blocks global pool waiter list */ +static DEFINE_MUTEX(root_udev_waiter_mutex); +static LIST_HEAD(root_udev_waiter); + static atomic_t global_db_count = ATOMIC_INIT(0); static struct kmem_cache *tcmu_cmd_cache; @@ -195,6 +200,11 @@ enum tcmu_multicast_groups { #define tcmu_cmd_set_dbi(cmd, index) ((cmd)->dbi[(cmd)->dbi_cur++] = (index)) #define tcmu_cmd_get_dbi(cmd) ((cmd)->dbi[(cmd)->dbi_cur++]) +static inline bool is_in_waiter_list(struct tcmu_dev *udev) +{ + return !!udev->waiting_blocks; +} + static void tcmu_cmd_free_data(struct tcmu_cmd *tcmu_cmd, uint32_t len) { struct tcmu_dev *udev = tcmu_cmd->tcmu_dev; @@ -250,8 +260,6 @@ static bool tcmu_get_empty_blocks(struct tcmu_dev *udev, { int i; - udev->waiting_global = false; - for (i = tcmu_cmd->dbi_cur; i < tcmu_cmd->dbi_cnt; i++) { if (!tcmu_get_empty_block(udev, tcmu_cmd)) goto err; @@ -259,9 +267,7 @@ static bool tcmu_get_empty_blocks(struct tcmu_dev *udev, return true; err: - udev->waiting_global = true; - /* Try to wake up the unmap thread */ - wake_up(&unmap_wait); + udev->waiting_blocks += tcmu_cmd->dbi_cnt - i; return false; } @@ -671,6 +677,7 @@ static inline size_t tcmu_cmd_get_cmd_size(struct tcmu_cmd *tcmu_cmd, while (!is_ring_space_avail(udev, tcmu_cmd, command_size, data_length)) { int ret; + bool is_waiting_blocks = !!udev->waiting_blocks; DEFINE_WAIT(__wait); prepare_to_wait(&udev->wait_cmdr, &__wait, TASK_INTERRUPTIBLE); @@ -688,6 +695,18 @@ static inline size_t tcmu_cmd_get_cmd_size(struct tcmu_cmd *tcmu_cmd, return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; } + /* + * Try to insert the current udev to waiter list and + * then wake up the unmap thread + */ + if (is_waiting_blocks) { + mutex_lock(&root_udev_waiter_mutex); + list_add_tail(&udev->waiter, &root_udev_waiter); + mutex_unlock(&root_udev_waiter_mutex); + + wake_up(&unmap_wait); + } + mutex_lock(&udev->cmdr_lock); /* We dropped cmdr_lock, cmd_head is stale */ @@ -902,8 +921,6 @@ static unsigned int tcmu_handle_completions(struct tcmu_dev *udev) if (mb->cmd_tail == mb->cmd_head) del_timer(&udev->timeout); /* no more pending cmds */ - wake_up(&udev->wait_cmdr); - return handled; } @@ -996,7 +1013,17 @@ static int tcmu_irqcontrol(struct uio_info *info, s32 irq_on) struct tcmu_dev *tcmu_dev = container_of(info, struct tcmu_dev, uio_info); mutex_lock(&tcmu_dev->cmdr_lock); - tcmu_handle_completions(tcmu_dev); + /* + * If the current udev is also in waiter list, this will + * make sure that the other waiters in list be feeded ahead + * of it. + */ + if (is_in_waiter_list(tcmu_dev)) { + wake_up(&unmap_wait); + } else { + tcmu_handle_completions(tcmu_dev); + wake_up(&tcmu_dev->wait_cmdr); + } mutex_unlock(&tcmu_dev->cmdr_lock); return 0; @@ -1231,7 +1258,7 @@ static int tcmu_configure_device(struct se_device *dev) udev->data_off = CMDR_SIZE; udev->data_size = DATA_SIZE; udev->dbi_thresh = 0; /* Default in Idle state */ - udev->waiting_global = false; + udev->waiting_blocks = 0; /* Initialise the mailbox of the ring buffer */ mb = udev->mb_addr; @@ -1345,6 +1372,13 @@ static void tcmu_free_device(struct se_device *dev) list_del(&udev->node); mutex_unlock(&root_udev_mutex); + mutex_lock(&root_udev_waiter_mutex); + mutex_lock(&udev->cmdr_lock); + if (is_in_waiter_list(udev)) + list_del(&udev->waiter); + mutex_unlock(&udev->cmdr_lock); + mutex_unlock(&root_udev_waiter_mutex); + vfree(udev->mb_addr); /* Upper layer should drain all requests before calling this */ @@ -1550,6 +1584,7 @@ static int unmap_thread_fn(void *data) struct tcmu_dev *udev; loff_t off; uint32_t start, end, block; + static uint32_t free_blocks; struct page *page; int i; @@ -1571,7 +1606,7 @@ static int unmap_thread_fn(void *data) tcmu_handle_completions(udev); /* Skip the udevs waiting the global pool or in idle */ - if (udev->waiting_global || !udev->dbi_thresh) { + if (is_in_waiter_list(udev) || !udev->dbi_thresh) { mutex_unlock(&udev->cmdr_lock); continue; } @@ -1607,17 +1642,32 @@ static int unmap_thread_fn(void *data) } } mutex_unlock(&udev->cmdr_lock); + + free_blocks += end - start; } + mutex_unlock(&root_udev_mutex); /* * Try to wake up the udevs who are waiting - * for the global data pool. + * for the global data pool blocks. */ - list_for_each_entry(udev, &root_udev, node) { - if (udev->waiting_global) - wake_up(&udev->wait_cmdr); + mutex_lock(&root_udev_waiter_mutex); + list_for_each_entry(udev, &root_udev_waiter, waiter) { + mutex_lock(&udev->cmdr_lock); + if (udev->waiting_blocks < free_blocks) { + mutex_unlock(&udev->cmdr_lock); + break; + } + + free_blocks -= udev->waiting_blocks; + udev->waiting_blocks = 0; + mutex_unlock(&udev->cmdr_lock); + + list_del(&udev->waiter); + + wake_up(&udev->wait_cmdr); } - mutex_unlock(&root_udev_mutex); + mutex_unlock(&root_udev_waiter_mutex); } return 0;