Received: by 2002:ac0:a5a7:0:0:0:0:0 with SMTP id m36-v6csp2093929imm; Thu, 16 Aug 2018 05:51:39 -0700 (PDT) X-Google-Smtp-Source: AA+uWPzhml1NPy8SK9x3zWBbAsMD2zoVi9BWDOVBRfD0BS00x+0Z4e31F2bwWpufBhJkAUu2xEcO X-Received: by 2002:a65:5581:: with SMTP id j1-v6mr29621551pgs.203.1534423899029; Thu, 16 Aug 2018 05:51:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1534423898; cv=none; d=google.com; s=arc-20160816; b=MNN5Bfe0Y8uzwYgwPU+5ldfrZ1nUmRPWxjzflNNo32x+4PrqJYtd6e+6A9fJKtbRJH /iVzcOSnvFwvY+AkW6SeA8G+tH23wPAfEPqC/Mdx2CvrtxrVHfDLIv2pyQrb04/jXfXw W5U1Ma6/Nondaxa1lnguq8adg+XCrDHp23QhsJe55HdbqEpdKD8Jg2yfU6fhOe0d4ryz rJYxAQEuHtWzdlAPcU1h+8HWvenNadTHqP54YlNxDn4dw2JUJvgvdBHX+DUkiobQRWmm uI0B8HOlY7Izl8Tf29ldnIKQ0ifHi2ebpPo5MdYJ6swZiUOGHDYDcOm3kAdT8ZWJRB1Q pMGA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:spamdiagnosticmetadata :spamdiagnosticoutput:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:reply-to:to:from :dkim-signature:arc-authentication-results; bh=ZoU1VxlG5v8K2iw2G9Rz60JCQ27M03tbv2mB44YCxiY=; b=yYn3R4k2KD0kL4/TL9mX84CX3cnCQGWSYK8YMvTMRkdGzXFHUx7nVKXI+DmSsk8syH fiL14hNjCkN15N8B+304DwUZxU8UoE8ZDg+bolb5iloCV0ZfkP2NI+FzcxF8JzJfNi0a 1PSaJxkNcLm5BWSG9XMixa5/O4kazhzkcQoYxFJweCAOyyASJtdrb8sfDLv2JFcK0UT0 4ZcPEezCyGpmdA1UnMSHcxA12g0MFcYOkLxSnNX2pLAD+dee7uhFOu7rF8cYRV/yjEgB HKzqCp4jyMqe8S5dPWv4o+dqwgD5jb9IfmJQYGxiMfC2hY1uX14cmkAiAEPWQQQxsNwp jiAg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@cern.onmicrosoft.com header.s=selector1-cern-ch header.b=Xjmm7azJ; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id e12-v6si23729617pfn.322.2018.08.16.05.50.55; Thu, 16 Aug 2018 05:51:38 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@cern.onmicrosoft.com header.s=selector1-cern-ch header.b=Xjmm7azJ; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389290AbeHPKPJ (ORCPT + 99 others); Thu, 16 Aug 2018 06:15:09 -0400 Received: from mail-he1eur01on0051.outbound.protection.outlook.com ([104.47.0.51]:49504 "EHLO EUR01-HE1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389224AbeHPKPI (ORCPT ); Thu, 16 Aug 2018 06:15:08 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cern.onmicrosoft.com; s=selector1-cern-ch; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ZoU1VxlG5v8K2iw2G9Rz60JCQ27M03tbv2mB44YCxiY=; b=Xjmm7azJ2TeMMEfQLhjrkNeMj8FfQ40kGeivpAMQo4tCyQGVBvdbcGbutd3ClbU0ErlzP4rnGqTXOBhVm9y/m0/4hslLZH080Hkdi6YzdU8cT297AzuStB3mGBr9bUw86fwSGiwUZsq3a5ViNJ2eolFbdojFYUkQMBNQAWv05cQ= Received: from HE1PR06CA0132.eurprd06.prod.outlook.com (2603:10a6:7:16::19) by DB6PR0602MB3237.eurprd06.prod.outlook.com (2603:10a6:6:6::25) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.1059.20; Thu, 16 Aug 2018 07:18:29 +0000 Received: from HE1EUR02FT059.eop-EUR02.prod.protection.outlook.com (2a01:111:f400:7e05::202) by HE1PR06CA0132.outlook.office365.com (2603:10a6:7:16::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.20.1059.19 via Frontend Transport; Thu, 16 Aug 2018 07:18:29 +0000 Authentication-Results: spf=pass (sender IP is 188.184.36.46) smtp.mailfrom=cern.ch; kernel.org; dkim=none (message not signed) header.d=none;kernel.org; dmarc=bestguesspass action=none header.from=cern.ch; Received-SPF: Pass (protection.outlook.com: domain of cern.ch designates 188.184.36.46 as permitted sender) receiver=protection.outlook.com; client-ip=188.184.36.46; helo=cernmxgwlb4.cern.ch; Received: from cernmxgwlb4.cern.ch (188.184.36.46) by HE1EUR02FT059.mail.protection.outlook.com (10.152.11.25) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.1059.14 via Frontend Transport; Thu, 16 Aug 2018 07:18:28 +0000 Received: from cernfe06.cern.ch (188.184.36.49) by cernmxgwlb4.cern.ch (188.184.36.46) with Microsoft SMTP Server (TLS) id 14.3.399.0; Thu, 16 Aug 2018 09:18:06 +0200 Received: from pcbe13614.localnet (2001:1458:202:121::100:40) by smtp.cern.ch (2001:1458:201:66::100:14) with Microsoft SMTP Server (TLS) id 14.3.399.0; Thu, 16 Aug 2018 09:18:06 +0200 From: Federico Vaga To: Alan Tull Reply-To: CC: Moritz Fischer , , linux-kernel Subject: Re: fpga: fpga_mgr_get() buggy ? Date: Thu, 16 Aug 2018 09:18:06 +0200 Message-ID: <10047668.mDZqRoECEV@pcbe13614> In-Reply-To: References: <4617134.5euanDEBgJ@pcbe13614> <2076282.DFHNtXLtgF@harkonnen> MIME-Version: 1.0 Content-Transfer-Encoding: 7Bit Content-Type: text/plain; charset="us-ascii" X-Originating-IP: [2001:1458:202:121::100:40] X-EOPAttributedMessage: 0 X-Forefront-Antispam-Report: CIP:188.184.36.46;IPV:NLI;CTRY:CH;EFV:NLI;SFV:NSPM;SFS:(10001)(10009020)(459900002);DIR:OUT;SFP:1101;SCL:1;SRVR:DB6PR0602MB3237;H:cernmxgwlb4.cern.ch;FPR:;SPF:None;LANG:en; X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 3056aa6f-8b62-445f-4c15-08d6034876fb X-Microsoft-Antispam: BCL:0;PCL:0;RULEID:(7020095)(4652040)(8989137)(4534165)(4627221)(201703031133081)(201702281549075)(8990107)(5600074)(711020)(4608076)(5565046)(2017052603328)(7153060)(7193020);SRVR:DB6PR0602MB3237; X-MS-TrafficTypeDiagnostic: DB6PR0602MB3237: X-Microsoft-Exchange-Diagnostics: 1;DB6PR0602MB3237;20:ZUcMLDi6G95VYsQdvbJdHTHzBrZFizoKu8SuSuLbQ1Xs3nWtiYdZObRB8fO7IgffF3RcQVUB0QturXQll+vB0OVIs7DhJI6Mts+RfDlvoFyFqlNFvcpLHiK9Bxw6U6GResw8xqJr49sEaEQZqeQLNgEIlONf2WSdSQiv1yUuPF7/ODjtlHkgDrbxLe4qyUff+e/rpZkgzQTMv4c2T1ccZuBiOLAWXSV6IQBDY5GeYHNYjYSDcXdQVAlMaKCAVroSa19Rx+MhdF1eLvdtMlt/NQRa0PxanBYUbf6uHQVfZZ56TD0qYaDl+i9Z4mHPohD3zUNaGBgN5Gk54IbIbBgjJ3a4xiBMo4rbK6Mp7uDWob+Fb3txP0RBeliXuGJy5SFUtIKACy0oyoiqGQTQ2vgU/X3IDuhXtSpZ80Topc/sX56B3YCib+OfIVoS+ZFk3ng59EZcTHUgxC9HLH2wTcMeLeARfXUUk2fbgYS/ZOM9EqNfWQLSOZsXm5DE66RGAa0k X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(250305191791016)(22074186197030)(166708455590820)(9452136761055)(80048183373757)(17755550239193); X-MS-Exchange-SenderADCheck: 1 X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(8211001083)(6040522)(2401047)(8121501046)(5005006)(93006095)(93004095)(10201501046)(3231311)(7700054)(944501410)(52105095)(3002001)(149027)(150027)(6041310)(20161123564045)(201703131423095)(201702281529075)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123558120)(20161123560045)(20161123562045)(201708071742011)(7699016)(7701012);SRVR:DB6PR0602MB3237;BCL:0;PCL:0;RULEID:;SRVR:DB6PR0602MB3237; X-Microsoft-Exchange-Diagnostics: 1;DB6PR0602MB3237;4:ckwu8rs2iW/mX2hpJ5nqNnVWS75Ckl/VNpx5A2nL8t6NLl9UtXjdm0dM656FYslSOVsl7h+fKoUvg5R2fVmzCzRVHUoS/cIof0ehsSGcv1KEL1Lwzi5vsfO0DHtM67Gh5f3AtBhd95XBd1/cLNz84MAOx8uX2t5ekmp6lQjtrVrXAuBmk9BfwYh5i6dFS7hxOCgk6nGEtS2s5Gy/g6BLF+Py/+8KKiDOOcfvCM8yW8QP4AIAQgmOV7dYpx9UECOTTK2iEUSytaS9P6tF2b9RociiSDTwP44V6/XgIxbkFpWcU5P4rwX9xzWrcpv7+GezzRK9llKPzxtgI1DyrFkUIM9DKooW6Bt+mKbncvWghXlI99lp3AXMdKlSbsCE5sa+SoxAeqprdQel5iZU9AzRxZcWbSqHNL/mKzmiCXRIOiIGs99p5IVVVIZuFuSfQpQixKW+GPBoMYILB9f03jwOYysbetnLSV6TNAu+1BhUf8c=;23:T5tj/uCMF7IVkt8W3XkEMsMYjfu2qx43MKKWHX5khvw17CduLJNzmM7ThiJ+rEmc054y95bh632GbAH2LWc8Oa9awEdKDEtxsplUA64rpzcDMaZPKTC9T6zKpOowyXalDimlYRa/1E2h5FOHxja3ZPf8jKcp3mtUx8JZDI5uZkLxubFeId7uZPAzTKH2HHRJ X-Forefront-PRVS: 07665BE9D1 X-Microsoft-Antispam-Message-Info: PEZBaxo36Ucm4fwcLGUYew7KGxup4v/ySdaKZFs7agoh/PMb29BvFE2jSuA2bUYIRLHXev9wJB50V0GcZ8xLQDcyt8+oFM/aHaQ8AUy1po8KB++TKnNW9YTD1qnh0gTlHdQvkFVFK5eZVK4K6RrWbgPA9rG/Q4njTrqDoYqfSCMRfkDvu1+4gfFjvABwVjBaJ+gvgt0VB7fSxIHD4zhntht3RwAuQcA5LHV1GUwDWRsCjgaF4SjOVbq+9wYOv4adrhXsHgoDrK3bVcY8LKcOqDiLLUj4KXkMq7ZtjV+YkjTdpdSErXXdoD27RmZj7QBdj9W3WwwEGA1OvW0GkuFPo9kY5Bp58EExkuXdJ6u5OzCF2oRA3eLXjKzLfwHXBgroq88dEdo6jHnwMv+vneiQtxsZRq8RssrHoioHxEL3IdZImo2zvRLg8ZOouhNgk4bB5C0IfSq51WP98c1mFjQtE63KDfF7HKAd7EIG8fI2S70= X-Microsoft-Exchange-Diagnostics: 1;DB6PR0602MB3237;6:J2qPMVWlov27JDYJc3jbANFHbnifdPN3YJW6UrCM4aVk8aVMnOLOVgW3zFduMBBTqqtSTO7eIjiv9ZeUkb7WdZyij3JVm3Pd5Tx/s9ds+GgbS8hWIMjZeaK9jOSY99ybwzD9oWW2HOx1WbFmEkdTdgX0KmuUdvVZhw1fh++ZYJUpdwrCrLwoui+N9Rl6KKfyKnt0/OPndfs81Ch6WZoVgENCghep9eChn7nwb8pqQrfb6wIiWQHnJIIXkgP0bEJxcS6eFHGLNRHfCDtc1EMPvCgOcMMmWxvfUJi7uXMUfBIlyZFqgCjeOwRF9Bj1nvVv4bhZQvNU9YnZ0c5LoryLIrutf4K7geG9XTs6DgsNj4RqWmLpnmVk6kuY322ks88pQjLmj6XK1pgrryeWUtlC7x3Wihva/0bvvTFZnv2oBI/xV/zf40bZfPJ0pscXnmeR2iw5YpCZiqicKUoRiewQ3Q==;5:wXkMQNu3BpVWON6ur+9OYpCA3iOzhzR0p7AmM36YBmgGria7xNI7TxeepJN+rZ6WnaHoA+dKXM6pxrlFwXUWBhjPPLWOYldLv3yfVzhCYocVp2k2EuuX0GiQ/PcQbblAdaf1+oqD4tEoPGqOmoOvefNn+bZ//J1cVUEkpkYAX8c=;7:EGAUIwPToIvATHEB8xb7KsWKN5SYXoDv0z7utvqop91at+bccfuBBbdmO+Ew9KxIgeRjS+87TjrEbOPi5NOaEd/J6Nm3TiNft8UmEjKKew58UcsyjnL5A4j/bFhHMxXpNs1cNlnEQFLNDzMCH+GME8ZYjgZwFxpTXnRxC+N6o6CbyRpxRWqgManTESTgX/vVlYJFrO4SlJxX7jOaERdQB1qo37hDh1qVheX2XpbYF8AUO3qeG2XpAH6FraI/mtkE SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: cern.ch X-MS-Exchange-CrossTenant-OriginalArrivalTime: 16 Aug 2018 07:18:28.5087 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 3056aa6f-8b62-445f-4c15-08d6034876fb X-MS-Exchange-CrossTenant-Id: c80d3499-4a40-4a8c-986e-abce017d6b19 X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=c80d3499-4a40-4a8c-986e-abce017d6b19;Ip=[188.184.36.46];Helo=[cernmxgwlb4.cern.ch] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB6PR0602MB3237 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi alan, inline comments On Wednesday, August 15, 2018 11:02:12 PM CEST Alan Tull wrote: > On Wed, Jul 18, 2018 at 4:47 PM, Federico Vaga wrote: > > Hi Alan, > > > > Thanks for your time, comments below > > > > On Wednesday, July 18, 2018 9:47:24 PM CEST Alan Tull wrote: > >> On Thu, Jun 28, 2018 at 2:50 AM, Federico Vaga > > > > wrote: > >> > On Wednesday, 27 June 2018 23:23:07 CEST Alan Tull wrote: > >> >> On Wed, Jun 27, 2018 at 4:25 AM, Federico Vaga > >> > > >> > wrote: > >> >> > Hi Alan, > >> >> > > >> >> > On Tuesday, 26 June 2018 23:00:46 CEST Alan Tull wrote: > >> >> >> On Fri, Jun 22, 2018 at 2:53 AM, Federico Vaga > >> >> >> wrote: > >> >> >> > >> >> >> Hi Federico, > >> >> >> > >> >> >> >> > What is buggy is the function fpga_mgr_get(). > >> >> >> >> > That patch has been done to allow multiple FPGA manager > >> >> >> >> > instances > >> >> >> >> > to be linked to the same device (PCI it says). But function > >> >> >> >> > fpga_mgr_get() will return only the first found: what about > >> >> >> >> > the > >> >> >> >> > others? > >> > >> Looking at this further, no code change is needed in the FPGA API to > >> support multiple managers. A FPGA manager driver is a self-contained > >> platform driver. The PCI driver for a board that contains multiple > >> FPGAs should create a platform device for each manager and save each > >> of those devs in its pdata. > >> > >> >> >> >> > Then, all load kernel-doc comments says: > >> >> >> >> > > >> >> >> >> > "This code assumes the caller got the mgr pointer from > >> >> >> >> > of_fpga_mgr_get() or fpga_mgr_get()" > >> >> >> >> > > >> >> >> >> > but that function does not allow me to get, for instance, > >> >> >> >> > the > >> >> >> >> > second FPGA manager on my card. > >> > >> fpga_mgr_get() will do what you want if your PCI driver creates a > >> platform device per FPGA manager as mentioned above. > >> > >> >> >> >> > Since, thanks to this patch I'm actually the creator of the > >> >> >> >> > fpga_manager structure, I do not need to use fpga_mgr_get() > >> >> >> >> > to > >> >> >> >> > retrieve that data structure. > >> > >> The creator of the FPGA mgr structure is the low level FPGA manager > >> driver, not the PCIe driver. > > > > In my case it is. > > It's a bit like where SPI driver is the low level FPGA manager driver for > > the xilinx-spi.c. But if I understand what you mean, I should always have > > a > > platform_driver just for the FPGA manager even if it has a 1:1 relation > > with its carrier? In other words, I write two drivers: > > - one for the FPGA manager > > - one for the PCI device that then register the FPGA manager driver > > > > In my case the FPGA programmed is also the PCIe bridge (GN4124). Probably > > I > > can do it anyway, because the part dedicated to FPGA programming is quite > > independent from the rest (don't remember all details) > > > >> >> >> >> > Despite this, I believe we still need to increment the > >> >> >> >> > module > >> >> >> >> > reference counter (which is done by fpga_mgr_get()). > >> > >> This is only done in the probe function of a FPGA region driver. > >> > >> >> >> >> > We can fix this function by just replacing the argument from > >> >> >> >> > 'device' to 'fpga_manager' (the one returned by create() ). > >> >> >> >> > >> >> >> >> At first thought, that's what I'd want. > >> >> >> >> > >> >> >> >> > Alternatively, we > >> >> >> >> > can add an 'owner' field in "struct fpga_manager_ops" and > >> >> >> >> > 'get' > >> >> >> >> > it > >> >> >> >> > when we use it. Or again, just an 'owner' argument in the > >> >> >> >> > create() > >> >> >> >> > function. > >> >> >> >> > >> >> >> >> It seems like we shouldn't have to do that. > >> >> >> > > >> >> >> > Why? > >> >> >> > >> >> >> OK yes, I agree; the kernel has a lot of examples of doing this. > >> >> >> > >> >> >> I'll have to play with it, I'll probably add the owner arg to the > >> >> >> create function, add owner to struct fpga_manager, and save. > >> >> > > >> >> > I have two though about this. > >> >> > > >> >> > 1. file_operation like approach. The onwer is associated to the > >> >> > FPGA manager operation. Whenever the FPGA manager wants to use an > >> >> > operation it get/put the module owner of these operations > >> >> > (because this is what we need to protect). With this the user is > >> >> > not directly involved, read it as less code, less things to care > >> >> > about. And probably there is no need for fpga_manager_get(). > >> >> > >> >> How does try_module_get fit into this scheme? I think this proposal > >> >> #1 is missing the point of what the reference count increment is > >> >> meant to do. Or am I misunderstanding? How does that keep the > >> >> module from being unloaded 1/3 way through programming a FPGA? > >> >> IIUC you are saying that the reference count would be incremented > >> >> before doing the manager ops .write_init, then decremented again > >> >> afterwards, incremented before each call to .write, decremented > >> >> afterwards, then the same for .write_complete. > >> > > >> > I'm not saying to do module_get/put just around the mops->XXX(): it's > >> > too much. Just where you have this comment: > >> > > >> > "This code assumes the caller got the mgr pointer from > >> > of_fpga_mgr_get() or fpga_mgr_get()" > >> > > >> > Because, currently, it's here where we do module_get() > >> > >> No it's not. > > > > It is not in the code, but the comment says that we should do it before > > calling fpga_mgr_load() > > > >> fpga_mgr_get() or of_fpga_mgr_get() is called when the region is > >> created such as in of-fpga-region's probe. That way, as long as the > >> region exists, the manager won't be unloaded. If someone wants to > >> start unloading modules, they need to unload higher level ones first, > >> so they'll have to unload the region before mgr. > >> > >> > Most mops are invoked within a set of static functions which are > >> > called only by few exported functions. I'm suggesting to do > >> > module_get/put in those exported function at the beginning (get) and > >> > and the end (put) because we know that within these functions, here > >> > and there, we will use mops which are owned by a different module. > >> > We do not want the module owner of the mops to disappear while someone > >> > is doing fpga_mgr_load(). For example, inside fpga_mgr_load() we use > >> > most of the mops, so we 'get' the module at the beginning and 'put' it > >> > at the end. The same for fpga_region_program_fpga(). > >> > >> If we do it the way you are suggesting, then someone can unload the > >> manager module without unloading the region. The region code will be > >> in for a nasty surprise when programming is attempted. > > > > Of course, this should be taken into account. I was not suggesting a > > precise implementation, but only the idea to hide get/put. Probably there > > other things as well that I'm not considering (indeed I do not have a > > patch, but just a comment) > > > >> > Like this we do not have to ask users to do fpga_mgr_get()/put(). > >> > >> The only user who has to do this is a region's probe function. > >> > >> I'm assuming that only fpga_region is using fpga_mgr_load() and > >> anybody who is creating a region uses fpga_region_program_fpga(). > >> That's what I want to encourage anyway. I should probably move > >> fpga_mgr_load to a private header. > > > > All right, if this is what you want to encourage I do not have anything to > > say because I did exactly what you do not want to encourage :) > > > > For me this change everything because I do not use regions since I do not > > have them. The way the API is exposed to me did not encouraged me to use > > their API. In addition, the documentation guides me directly to the FPGA > > manager. > > The documentation says: > > "If you are adding a new interface to the FPGA framework, add it on > top of a FPGA region to allow the most reuse of your interface." I would change this in something like: "If you are adding a new interface to the FPGA framework, add it on top of a FPGA region." What I understand from the current statement is: "if you care about re-usability, add your interface on top of an FPGA region" Since in my special case, I do not care about re-usability, I skipped the FPGA region. > https://www.kernel.org/doc/html/latest/driver-api/fpga/intro.html > > But the stuff that I submitted yesterday goes back through the docs to > clear out anything that is not clear about this. > > > To be honest I did not have much time to look at the region code. My > > understanding, after a quick look, is that it works great with > > device-tree. > > It is used by the DFL framework which doesn't use device tree. > > > But what if I do not have it? Actually, I cannot use device-tree because > > of > > environment limitations and Linux version in use. Oops, this implies that > > I > > back-ported the FPGA manager to an older Linux version? Yes, guilty :) > > I separated region code from its device-tree dependencies. But if you > can't use device-tree, then you end up having to implement some of the > things DT gives you for free. unfortunately yes > > Anyway, I'm using the API exposed, and if part of the assumptions behind > > this API is that I should use device-tree, then I'm out of scope. > > > > > > Just for chatting. One addition I made for the FPGA manager is to support > > > > dynamic loading of FPGA code using char devices. Something like: > > dd if=binary.bin of=/dev/fpga0 > > cat binary.bin > /dev/fpga0 > > Since it's not handling the bridge, there's some risk involved. > > > We do not have device tree, and we do not have binaries in /lib/firmware. > > It's quite handy to quickly load a binary just synthesized, especially for > > debugging purpose. > > Like a debugfs? > > https://lkml.org/lkml/2018/8/2/125 > > But for a debugging/developing, I have a debugfs that was a downstream > patchset that I'm cleaning for submission. Yes, like that more or less. I did a chardevice in /dev/ because in our environment debugfs is not mounted automatically and we need this feature also operationally. But yes, that's the idea. One comment. The code on github [1] looks like it is assuming that on userspace people write the full image in one shot and it is smaller that 4MiB. What I did is to build a scatterlist in order to support the following case: cat image.bin > /sys/kernel/debug/.../image where `cat` write 4K at time. And it can be bigger than 4M [1] https://github.com/altera-opensource/linux-socfpga/blob/socfpga-4.17/ drivers/fpga/fpga-mgr-debugfs.c Below (end of this email) the patch I quickly did last year. Unfortunately I do not have the time to clean this solution, so it is the very first thing I implemented without many thoughts (that's why I never pushed this patch). > > If you are interested I can try to clean it up (at some > > point, probably in autumn), or I can show you the code in private for a > > quick look. > > > > > >> > > >> > fpga_region_program_fpga() does not do (today) fpga_mgr_get() because > >> > it assumes, but it is not enforced, that both fpga_region and fpga_mgr > >> > are child of the same device. > >> > >> fpga_region_program_fpga() doesn't do fpga_mgr_get() becuase > >> fpga_mgr_get() happens when the fpga_region probes. The assumption I > >> am making is that nobody other than a region is calling > >> fpga_manager_load(). I should create a fpga_private.h and move > >> fpga_manager_load() out of fpga-mgr.h to make that official. > > > > Yes, I agree. If what I'm doing is not supposed to happen I should not be > > allowed to do it :) > > > > > > If you want to encourage people to use regions rather than the manager > > directly, have you though about changing the API and merge in a single > > module fpga-mgr and fpga-region? > > > > Brainstorming. Perhaps, it is possible to have a `struct fpga_region_info` > > and when we register and FPGA manager we use something like: > > > > struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name, > > > > const struct fpga_manager_ops *mops, > > > > struct fpga_region_info *info, int n_regions, > > > > void *priv) > > > > So those regions will be created directly and the interface will be > > smaller > > and easier. > > > > Don't waste much time on this suggestion, as I said before I did not study > > much the fpga-region.c code and perhaps this is not possible and I'm just > > speaking rubbish :) > > > > > >> > Probably this is true 99.99% of the > >> > time. > >> > Let me open in parallel another point: why fpga_region is not a child > >> > of fpga_manager? > >> > >> FPGA regions are children of FPGA bridges. > >> > >> Alan > > > > -- > > Federico Vaga > > [BE-CO-HT] > > > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-fpga" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html ------ From d6a6df9515c5e92cc74b8948c3c10ac9cbeec6d2 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Mon, 20 Nov 2017 14:40:26 +0100 Subject: [PATCH] fpga: program from user-space Signed-off-by: Federico Vaga --- Documentation/fpga/fpga-mgr.txt | 3 + drivers/fpga/fpga-mgr.c | 261 ++++++++++++++++++++++++++++++++++++++ +- include/linux/fpga/fpga-mgr.h | 19 +++ 3 files changed, 281 insertions(+), 2 deletions(-) diff --git a/Documentation/fpga/fpga-mgr.txt b/Documentation/fpga/fpga-mgr.txt index 78f197f..397dae9 100644 --- a/Documentation/fpga/fpga-mgr.txt +++ b/Documentation/fpga/fpga-mgr.txt @@ -197,3 +197,6 @@ to put the FPGA into operating mode. The ops include a .state function which will read the hardware FPGA manager and return a code of type enum fpga_mgr_states. It doesn't result in a change in hardware state. + +Configuring the FPGA from user-space +==================================== diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c index 6441f91..964b7e4 100644 --- a/drivers/fpga/fpga-mgr.c +++ b/drivers/fpga/fpga-mgr.c @@ -27,10 +27,56 @@ #include #include #include +#include #include +#include +#include static DEFINE_IDA(fpga_mgr_ida); static struct class *fpga_mgr_class; +static dev_t fpga_mgr_devt; + +/** + * struct fpga_image_chunck - FPGA configuration chunck + * @data: chunk data + * @size: chunk data size + * @list: for the linked list of chunks + */ +struct fpga_image_chunk { + void *data; + size_t size; + struct list_head list; +}; +#define CHUNK_MAX_SIZE (PAGE_SIZE) + +/** + * struct fpga_user_load - structure to handle configuration from user-space + * @mgr: pointer to the FPGA manager + * @chunk_list: HEAD point of a linked list of FPGA chunks + * @n_chunks: number of chunks in the list + * @lock: it protects: chunk_list, n_chunks + */ +struct fpga_user_load { + struct fpga_manager *mgr; + struct list_head chunk_list; + unsigned int n_chunks; + struct spinlock lock; +}; + + +/** + * It sets by default a huge timeout for configuration coming from user-space + * just to play safe. + * + * FIXME what about sysfs parameters to adjust it? The flag bit in particular + */ +struct fpga_image_info default_user_info = { + .flags = 0, + .enable_timeout_us = 10000000, /* 10s */ + .disable_timeout_us = 10000000, /* 10s */ + .config_complete_timeout_us = 10000000, /* 10s */ +}; + /* * Call the low level driver's write_init function. This will do the @@ -310,6 +356,158 @@ int fpga_mgr_firmware_load(struct fpga_manager *mgr, } EXPORT_SYMBOL_GPL(fpga_mgr_firmware_load); + +static int fpga_mgr_open(struct inode *inode, struct file *file) +{ + struct fpga_manager *mgr = container_of(inode->i_cdev, + struct fpga_manager, + cdev); + struct fpga_user_load *usr; + int ret; + + /* Allow the user-space programming only if user unlocked the FPGA */ + if (!test_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags)) { + dev_info(&mgr->dev, "The FPGA programming is locked\n"); + return -EPERM; + } + + ret = mutex_trylock(&mgr->usr_mutex); + if (ret == 0) + return -EBUSY; + + usr = kzalloc(sizeof(struct fpga_user_load), GFP_KERNEL); + if (!usr) { + ret = -ENOMEM; + goto err_alloc; + } + + usr->mgr = mgr; + spin_lock_init(&usr->lock); + INIT_LIST_HEAD(&usr->chunk_list); + file->private_data = usr; + + return 0; + +err_alloc: + spin_lock(&mgr->lock); + clear_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags); + spin_unlock(&mgr->lock); + mutex_unlock(&mgr->usr_mutex); + return ret; +} + + +static int fpga_mgr_flush(struct file *file, fl_owner_t id) +{ + struct fpga_user_load *usr = file->private_data; + struct fpga_image_chunk *chunk, *tmp; + struct sg_table sgt; + struct scatterlist *sg; + int err = 0; + + if (!usr->n_chunks) + return 0; + + err = sg_alloc_table(&sgt, usr->n_chunks, GFP_KERNEL); + if (err) + goto out; + + sg = sgt.sgl; + list_for_each_entry(chunk, &usr->chunk_list, list) { + sg_set_buf(sg, chunk->data, chunk->size); + sg = sg_next(sg); + if (!sg) + break; + } + + err = fpga_mgr_buf_load_sg(usr->mgr, + &default_user_info, + &sgt); + sg_free_table(&sgt); + +out: + /* Remove all chunks */ + spin_lock(&usr->lock); + list_for_each_entry_safe(chunk, tmp, &usr->chunk_list, list) { + list_del(&chunk->list); + kfree(chunk->data); + kfree(chunk); + usr->n_chunks--; + } + spin_unlock(&usr->lock); + + return err; +} + + +static int fpga_mgr_close(struct inode *inode, struct file *file) +{ + struct fpga_user_load *usr = file->private_data; + struct fpga_manager *mgr = usr->mgr; + + kfree(usr); + + spin_lock(&mgr->lock); + clear_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags); + spin_unlock(&mgr->lock); + + mutex_unlock(&mgr->usr_mutex); + + return 0; +} + + +static ssize_t fpga_mgr_write(struct file *file, const char __user *buf, + size_t count, loff_t *offp) +{ + struct fpga_user_load *usr = file->private_data; + struct fpga_image_chunk *chunk; + int err; + + chunk = kmalloc(sizeof(struct fpga_image_chunk), GFP_KERNEL); + if (!chunk) + return -ENOMEM; + + chunk->size = count > CHUNK_MAX_SIZE ? CHUNK_MAX_SIZE : count; + chunk->data = kmalloc(chunk->size, GFP_KERNEL); + if (!chunk->data) { + err = -ENOMEM; + goto err_buf_alloc; + } + + err = copy_from_user(chunk->data, buf, chunk->size); + if(err) + goto err_buf_copy; + + spin_lock(&usr->lock); + list_add_tail(&chunk->list, &usr->chunk_list); + usr->n_chunks++; + spin_unlock(&usr->lock); + + *offp += count; + + return chunk->size; + +err_buf_copy: + kfree(chunk->data); +err_buf_alloc: + kfree(chunk); + return err; +} + + +/** + * Char device operation + */ +static const struct file_operations fpga_mgr_fops = { + .owner = THIS_MODULE, + .open = fpga_mgr_open, + .flush = fpga_mgr_flush, + .release = fpga_mgr_close, + .write = fpga_mgr_write, +}; + + static const char * const state_str[] = { [FPGA_MGR_STATE_UNKNOWN] = "unknown", [FPGA_MGR_STATE_POWER_OFF] = "power off", @@ -352,13 +550,43 @@ static ssize_t state_show(struct device *dev, return sprintf(buf, "%s\n", state_str[mgr->state]); } +static ssize_t config_lock_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fpga_manager *mgr = to_fpga_manager(dev); + + if (test_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags)) + return sprintf(buf, "unlock\n"); + return sprintf(buf, "lock\n"); +} + +static ssize_t config_lock_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fpga_manager *mgr = to_fpga_manager(dev); + + spin_lock(&mgr->lock); + if (strncmp(buf, "lock" , min(4, (int)count)) == 0) + clear_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags); + else if (strncmp(buf, "unlock" , min(6, (int)count)) == 0) + set_bit(FPGA_MGR_FLAG_UNLOCK, mgr->flags); + else + count = -EINVAL; + spin_unlock(&mgr->lock); + + return count; +} + #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) static DEVICE_ATTR_RO(name); static DEVICE_ATTR_RO(state); +static DEVICE_ATTR_RW(config_lock); static struct attribute *fpga_mgr_attrs[] = { &dev_attr_name.attr, &dev_attr_state.attr, + &dev_attr_lock.attr, NULL, }; ATTRIBUTE_GROUPS(fpga_mgr); @@ -367,6 +595,9 @@ ATTRIBUTE_GROUPS(fpga_mgr); static struct device_attribute fpga_mgr_attrs[] = { __ATTR(name, S_IRUGO, name_show, NULL), __ATTR(state, S_IRUGO, state_show, NULL), + __ATTR(config_lock, S_IRUGO | S_IWUSR | S_IRGRP | S_IWGRP, + config_lock_show, config_lock_store), + __ATTR_NULL, }; #endif @@ -507,6 +738,8 @@ int fpga_mgr_register(struct device *dev, const char *name, } mutex_init(&mgr->ref_mutex); + mutex_init(&mgr->usr_mutex); + spin_lock_init(&mgr->lock); mgr->name = name; mgr->mops = mops; @@ -524,12 +757,19 @@ int fpga_mgr_register(struct device *dev, const char *name, mgr->dev.parent = dev; mgr->dev.of_node = dev->of_node; mgr->dev.id = id; + mgr->dev.devt = MKDEV(MAJOR(fpga_mgr_devt), id); dev_set_drvdata(dev, mgr); ret = dev_set_name(&mgr->dev, "fpga%d", id); if (ret) goto error_device; + cdev_init(&mgr->cdev, &fpga_mgr_fops); + mgr->cdev.owner = THIS_MODULE; + ret = cdev_add(&mgr->cdev, mgr->dev.devt, 1); + if (ret) + goto err_cdev; + ret = device_add(&mgr->dev); if (ret) goto error_device; @@ -539,6 +779,8 @@ int fpga_mgr_register(struct device *dev, const char *name, return 0; error_device: + cdev_del(&mgr->cdev); +err_cdev: ida_simple_remove(&fpga_mgr_ida, id); error_kfree: kfree(mgr); @@ -572,17 +814,27 @@ static void fpga_mgr_dev_release(struct device *dev) { struct fpga_manager *mgr = to_fpga_manager(dev); + cdev_del(&mgr->cdev); ida_simple_remove(&fpga_mgr_ida, mgr->dev.id); kfree(mgr); } static int __init fpga_mgr_class_init(void) { + int err; + pr_info("FPGA manager framework\n"); + err = alloc_chrdev_region(&fpga_mgr_devt, 0, FPGA_MGR_MAX_DEV, + "fpga_mgr"); + if (err) + return err; + fpga_mgr_class = class_create(THIS_MODULE, "fpga_manager"); - if (IS_ERR(fpga_mgr_class)) - return PTR_ERR(fpga_mgr_class); + if (IS_ERR(fpga_mgr_class)) { + err = PTR_ERR(fpga_mgr_class); + goto err_class; + } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) fpga_mgr_class->dev_groups = fpga_mgr_groups; #else @@ -591,12 +843,17 @@ static int __init fpga_mgr_class_init(void) fpga_mgr_class->dev_release = fpga_mgr_dev_release; return 0; + +err_class: + unregister_chrdev_region(fpga_mgr_devt, FPGA_MGR_MAX_DEV); + return err; } static void __exit fpga_mgr_class_exit(void) { class_destroy(fpga_mgr_class); ida_destroy(&fpga_mgr_ida); + unregister_chrdev_region(fpga_mgr_devt, FPGA_MGR_MAX_DEV); } MODULE_AUTHOR("Alan Tull "); diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h index bfa14bc..ae38e48 100644 --- a/include/linux/fpga/fpga-mgr.h +++ b/include/linux/fpga/fpga-mgr.h @@ -15,8 +15,10 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ +#include #include #include +#include #ifndef _LINUX_FPGA_MGR_H #define _LINUX_FPGA_MGR_H @@ -24,6 +26,8 @@ struct fpga_manager; struct sg_table; +#define FPGA_MGR_MAX_DEV (256) + /** * enum fpga_mgr_states - fpga framework states * @FPGA_MGR_STATE_UNKNOWN: can't determine state @@ -118,22 +122,37 @@ struct fpga_manager_ops { void (*fpga_remove)(struct fpga_manager *mgr); }; +/* + * List of status FLAGS for the FPGA manager + */ +#define FPGA_MGR_FLAG_BITS (32) +#define FPGA_MGR_FLAG_UNLOCK BIT(0) + /** * struct fpga_manager - fpga manager structure * @name: name of low level fpga manager + * @cdev: char device interface * @dev: fpga manager device * @ref_mutex: only allows one reference to fpga manager * @state: state of fpga manager * @mops: pointer to struct of fpga manager ops * @priv: low level driver private date + * @flags: manager status bits + * @lock: it protects: flags + * @usr_mutex: only allows one user to program the FPGA */ struct fpga_manager { const char *name; + struct cdev cdev; struct device dev; struct mutex ref_mutex; enum fpga_mgr_states state; const struct fpga_manager_ops *mops; void *priv; + + DECLARE_BITMAP(flags, FPGA_MGR_FLAG_BITS); + struct spinlock lock; + struct mutex usr_mutex; }; #define to_fpga_manager(d) container_of(d, struct fpga_manager, dev) -- 2.15.0