From patchwork Thu Jan 5 20:26:36 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Andrey Smirnov X-Patchwork-Id: 9499665 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 EE84C60413 for ; Thu, 5 Jan 2017 20:27:26 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E26A42845E for ; Thu, 5 Jan 2017 20:27:26 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D59942846D; Thu, 5 Jan 2017 20:27:26 +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.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 2C3A02845E for ; Thu, 5 Jan 2017 20:27:24 +0000 (UTC) Received: from localhost ([::1]:48531 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cPEd8-0000HB-Ia for patchwork-qemu-devel@patchwork.kernel.org; Thu, 05 Jan 2017 15:27:22 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41146) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cPEcn-00006R-AI for qemu-devel@nongnu.org; Thu, 05 Jan 2017 15:27:02 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cPEcm-0001P5-4D for qemu-devel@nongnu.org; Thu, 05 Jan 2017 15:27:01 -0500 Received: from mail-pg0-x243.google.com ([2607:f8b0:400e:c05::243]:34331) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1cPEch-0001Oc-BD; Thu, 05 Jan 2017 15:26:55 -0500 Received: by mail-pg0-x243.google.com with SMTP id b1so41666492pgc.1; Thu, 05 Jan 2017 12:26:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=HV6YZAXdW44cWcvdNwJ5HPGsrpV607YqVEWjidGlEqc=; b=i/DECNHT7VUH3BjNz+jewF6HVMsBug8PDXOpcOkU8NFTLcJK7zoaX5qQDz16gLIKZr 2lotf4CZ9VJVvfej6VS/HMu+w+f3VtCePB1pZNDJTpYP0gJkik2dkDdzat1LAWUf/P9C Ji0Ufu46Ul0nLrdCoEKqw61CosJ2EvQftT4frF1Swy8IyZvzSs1WzJkWxPmuOsMA4D2w CVrEI9W13VNH71Cr5NCWN/hyRyM4//QDbxi8sjuz7Ur5TKuyqiY2wGaXwwPhNgsgTwIJ nXnBQdG0hSvUq0jg0Eea99DQRXZdEHMSDwxqXrGRCnbjsEncwdrwLuqu5i73aJBVm3KC b3+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=HV6YZAXdW44cWcvdNwJ5HPGsrpV607YqVEWjidGlEqc=; b=RV1FMGsXEkgENadY5s9/TdMbodo7p58nloUP8ZStlLIABk2xlQnej/HKovM7z/F2pF vEi2Hib4fwMkuiPwjBFBWcj2VKInAWWPQai1sfYzalJJiZgHGdyu3PyTDlMImDJy6Pc0 eAV1/XXoQaxgq2ecrgdlVHoPBe8CeCgNP+OTVpSHdi8sJPobJLkaQN5BjwbQglLD7qWk ZVmpA3+PojtXikksiujQbZSd+ixySoWRKzLac6QiRwl9rl9CIn10DouFPsnvFpisHQaI dKKLRJz+rIeZfX9jv5N8soxjn4+M1M9XGm6yefBG+WJqMiIXMaCQpVo1Ro2CizmpYG67 ruWg== X-Gm-Message-State: AIkVDXIecC932Wdeu0H8uVZUPwk5kMS1cQMJIA1+rPTd9R4+DZs23NU7dJCqj557H/P77A== X-Received: by 10.84.172.131 with SMTP id n3mr157173429plb.5.1483648014313; Thu, 05 Jan 2017 12:26:54 -0800 (PST) Received: from squirtle.hq.andrews-space.com ([173.226.206.194]) by smtp.gmail.com with ESMTPSA id w125sm155269774pfb.8.2017.01.05.12.26.51 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 05 Jan 2017 12:26:53 -0800 (PST) From: Andrey Smirnov To: qemu-ppc@nongnu.org Date: Thu, 5 Jan 2017 12:26:36 -0800 Message-Id: <20170105202636.4003-1-andrew.smirnov@gmail.com> X-Mailer: git-send-email 2.9.3 MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2607:f8b0:400e:c05::243 Subject: [Qemu-devel] [PATCH v3] fsl_etsec: Fix Tx BD ring wrapping handling X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Andrey Smirnov , Scott Wood , Jason Wang , Alexander Graf , qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Current code that handles Tx buffer desciprtor ring scanning employs the following algorithm: 1. Restore current buffer descriptor pointer from TBPTRn 2. Process current descriptor 3. If current descriptor has BD_WRAP flag set set current descriptor pointer to start of the descriptor ring 4. If current descriptor points to start of the ring exit the loop, otherwise increment current descriptor pointer and go to #2 5. Store current descriptor in TBPTRn The way the code is implemented results in buffer descriptor ring being scanned starting at offset/descriptor #0. While covering 99% of the cases, this algorithm becomes problematic for a number of edge cases. Consider the following scenario: guest OS driver initializes descriptor ring to N individual descriptors and starts sending data out. Depending on the volume of traffic and probably guest OS driver implementation it is possible that an edge case where a packet, spread across 2 descriptors is placed in descriptors N - 1 and 0 in that order(it is easy to imagine similar examples involving more than 2 descriptors). What happens then is aforementioned algorithm starts at descriptor 0, sees a descriptor marked as BD_LAST, which it happily sends out as a separate packet(very much malformed at this point) then the iteration continues and the first part of the original packet is tacked to the next transmission which ends up being bogus as well. This behvaiour can be pretty reliably observed when scp'ing data from a guest OS via TAP interface for files larger than 160K (every time for 700K+). This patch changes the scanning algorithm to do the following: 1. Restore "current" buffer descriptor pointer from TBPTRn 2. If "current" descriptor does not have BD_TX_READY set, goto #6 3. Process current descriptor 4. If "current" descriptor has BD_WRAP flag set "current" descriptor pointer to start of the descriptor ring otherwise set increment "current" by the size of one descriptor 5. Goto #1 6. Save "current" buffer descriptor in TBPTRn This way we preserve the information about which descriptor was processed last and always start where we left off avoiding the original problem. On top of that, judging by the following excerpt from MPC8548ERM (p. 14-48): "... When the end of the TxBD ring is reached, eTSEC initializes TBPTRn to the value in the corresponding TBASEn. The TBPTR register is internally written by the eTSEC’s DMA controller during transmission. The pointer increments by eight (bytes) each time a descriptor is closed successfully by the eTSEC..." revised algorithm might also a more correct way of emulating this aspect of eTSEC peripheral. Cc: Alexander Graf Cc: Scott Wood Cc: Jason Wang Cc: qemu-devel@nongnu.org Signed-off-by: Andrey Smirnov --- Changes since v1: - Simplified new algorithm as per Jason Wang's suggestion Changes since v2: - Fixed code style problems hw/net/fsl_etsec/rings.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c index 54c0127..d0f93ee 100644 --- a/hw/net/fsl_etsec/rings.c +++ b/hw/net/fsl_etsec/rings.c @@ -358,25 +358,24 @@ void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr) /* Save flags before BD update */ bd_flags = bd.flags; - if (bd_flags & BD_TX_READY) { - process_tx_bd(etsec, &bd); - - /* Write back BD after update */ - write_buffer_descriptor(etsec, bd_addr, &bd); + if (!(bd_flags & BD_TX_READY)) { + break; } + process_tx_bd(etsec, &bd); + /* Write back BD after update */ + write_buffer_descriptor(etsec, bd_addr, &bd); + /* Wrap or next BD */ if (bd_flags & BD_WRAP) { bd_addr = ring_base; } else { bd_addr += sizeof(eTSEC_rxtx_bd); } + } while (TRUE); - } while (bd_addr != ring_base); - - bd_addr = ring_base; - - /* Save the Buffer Descriptor Pointers to current bd */ + /* Save the Buffer Descriptor Pointers to last bd that was not + * succesfully closed */ etsec->regs[TBPTR0 + ring_nbr].value = bd_addr; /* Set transmit halt THLTx */