From patchwork Fri Jan 6 21:14:15 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Gunthorpe X-Patchwork-Id: 9501855 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 0135D606B4 for ; Fri, 6 Jan 2017 21:14:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E681B2853A for ; Fri, 6 Jan 2017 21:14:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DB71328541; Fri, 6 Jan 2017 21:14:51 +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_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID 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 F15982853E for ; Fri, 6 Jan 2017 21:14:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932517AbdAFVOk (ORCPT ); Fri, 6 Jan 2017 16:14:40 -0500 Received: from quartz.orcorp.ca ([184.70.90.242]:50734 "EHLO quartz.orcorp.ca" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753331AbdAFVO2 (ORCPT ); Fri, 6 Jan 2017 16:14:28 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=obsidianresearch.com; s=rsa1; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=mDUnMhb+J+gEsi7Hf61Rod/DJIF6Lfh52P2dY4PU0eo=; b=pkAKpoJXUoQp2+7TwVj0C66YRgFIu8hXFkLCLoib23ctYkNkNjHFQe4YkHtcTEOKYGmNWqjUbzZ2qbr9uzL9BkTrOq2kruJ2sCj57i4A7LJXkBxY3dHrAZaRJTUEAWKpDXK2V7NWtdFYDyQ+ZduAE7mg8+CCi/WT5/E6kldKQTw=; Received: from [10.0.0.156] (helo=jggl.edm.orcorp.ca) by quartz.orcorp.ca with esmtps (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA256:128) (Exim 4.84_2) (envelope-from ) id 1cPbqB-0007E9-52; Fri, 06 Jan 2017 14:14:23 -0700 From: Jason Gunthorpe To: atull@opensource.altera.com, Moritz Fischer Cc: linux-kernel@vger.kernel.org, linux-fpga@vger.kernel.org Subject: [PATCH v3 fpga 3/4] fpga: Add scatterlist based programming Date: Fri, 6 Jan 2017 14:14:15 -0700 Message-Id: <1483737256-18581-4-git-send-email-jgunthorpe@obsidianresearch.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1483737256-18581-1-git-send-email-jgunthorpe@obsidianresearch.com> References: <1483737256-18581-1-git-send-email-jgunthorpe@obsidianresearch.com> X-Broken-Reverse-DNS: no host name found for IP address 10.0.0.156 Sender: linux-fpga-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Requiring contiguous kernel memory is not a good idea, this is a limited resource and allocation can fail under normal work loads. This introduces a .write_sg op that supporting drivers can provide to DMA directly from dis-contiguous memory and a new entry point fpga_mgr_buf_load_sg that users can call to directly provide page lists. The full matrix of compatibility is provided, either the linear or sg interface can be used by the user with a driver supporting either interface. A notable change for drivers is that the .write op can now be called multiple times. Signed-off-by: Jason Gunthorpe Acked-by: Alan Tull --- Documentation/fpga/fpga-mgr.txt | 19 +++- drivers/fpga/fpga-mgr.c | 238 ++++++++++++++++++++++++++++++++++------ include/linux/fpga/fpga-mgr.h | 5 + 3 files changed, 228 insertions(+), 34 deletions(-) diff --git a/Documentation/fpga/fpga-mgr.txt b/Documentation/fpga/fpga-mgr.txt index 86ee5078fd034f..78f197fadfd1b6 100644 --- a/Documentation/fpga/fpga-mgr.txt +++ b/Documentation/fpga/fpga-mgr.txt @@ -22,7 +22,16 @@ To program the FPGA from a file or from a buffer: struct fpga_image_info *info, const char *buf, size_t count); -Load the FPGA from an image which exists as a buffer in memory. +Load the FPGA from an image which exists as a contiguous buffer in +memory. Allocating contiguous kernel memory for the buffer should be avoided, +users are encouraged to use the _sg interface instead of this. + + int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, + struct fpga_image_info *info, + struct sg_table *sgt); + +Load the FPGA from an image from non-contiguous in memory. Callers can +construct a sg_table using alloc_page backed memory. int fpga_mgr_firmware_load(struct fpga_manager *mgr, struct fpga_image_info *info, @@ -166,7 +175,7 @@ success or negative error codes otherwise. The programming sequence is: 1. .write_init - 2. .write (may be called once or multiple times) + 2. .write or .write_sg (may be called once or multiple times) 3. .write_complete The .write_init function will prepare the FPGA to receive the image data. The @@ -176,7 +185,11 @@ buffer up at least this much before starting. The .write function writes a buffer to the FPGA. The buffer may be contain the whole FPGA image or may be a smaller chunk of an FPGA image. In the latter -case, this function is called multiple times for successive chunks. +case, this function is called multiple times for successive chunks. This interface +is suitable for drivers which use PIO. + +The .write_sg version behaves the same as .write except the input is a sg_table +scatter list. This interface is suitable for drivers which use DMA. The .write_complete function is called after all the image has been written to put the FPGA into operating mode. diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c index f0a69d3e60a584..30f9778d0632d2 100644 --- a/drivers/fpga/fpga-mgr.c +++ b/drivers/fpga/fpga-mgr.c @@ -1,4 +1,4 @@ -/* + /* * FPGA Manager Core * * Copyright (C) 2013-2015 Altera Corporation @@ -25,16 +25,106 @@ #include #include #include +#include +#include static DEFINE_IDA(fpga_mgr_ida); static struct class *fpga_mgr_class; +/* + * Call the low level driver's write_init function. This will do the + * device-specific things to get the FPGA into the state where it is ready to + * receive an FPGA image. The low level driver only gets to see the first + * initial_header_size bytes in the buffer. + */ +static int fpga_mgr_write_init_buf(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *buf, size_t count) +{ + int ret; + + mgr->state = FPGA_MGR_STATE_WRITE_INIT; + if (!mgr->mops->initial_header_size) + ret = mgr->mops->write_init(mgr, info, NULL, 0); + else + ret = mgr->mops->write_init( + mgr, info, buf, min(mgr->mops->initial_header_size, count)); + + if (ret) { + dev_err(&mgr->dev, "Error preparing FPGA for writing\n"); + mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR; + return ret; + } + + return 0; +} + +static int fpga_mgr_write_init_sg(struct fpga_manager *mgr, + struct fpga_image_info *info, + struct sg_table *sgt) +{ + struct sg_mapping_iter miter; + size_t len; + char *buf; + int ret; + + if (!mgr->mops->initial_header_size) + return fpga_mgr_write_init_buf(mgr, info, NULL, 0); + + /* + * First try to use miter to map the first fragment to access the + * header, this is the typical path. + */ + sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG); + if (sg_miter_next(&miter) && + miter.length >= mgr->mops->initial_header_size) { + ret = fpga_mgr_write_init_buf(mgr, info, miter.addr, + miter.length); + sg_miter_stop(&miter); + return ret; + } + sg_miter_stop(&miter); + + /* Otherwise copy the fragments into temporary memory. */ + buf = kmalloc(mgr->mops->initial_header_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len = sg_copy_to_buffer(sgt->sgl, sgt->nents, buf, + mgr->mops->initial_header_size); + ret = fpga_mgr_write_init_buf(mgr, info, buf, len); + + kfree(buf); + + return ret; +} + +/* + * After all the FPGA image has been written, do the device specific steps to + * finish and set the FPGA into operating mode. + */ +static int fpga_mgr_write_complete(struct fpga_manager *mgr, + struct fpga_image_info *info) +{ + int ret; + + mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE; + ret = mgr->mops->write_complete(mgr, info); + if (ret) { + dev_err(&mgr->dev, "Error after writing image data to FPGA\n"); + mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR; + return ret; + } + mgr->state = FPGA_MGR_STATE_OPERATING; + + return 0; +} + /** - * fpga_mgr_buf_load - load fpga from image in buffer + * fpga_mgr_buf_load_sg - load fpga from image in buffer from a scatter list * @mgr: fpga manager * @info: fpga image specific information - * @buf: buffer contain fpga image - * @count: byte count of buf + * @sgt: scatterlist table * * Step the low level fpga manager through the device-specific steps of getting * an FPGA ready to be configured, writing the image to it, then doing whatever @@ -42,54 +132,139 @@ static struct class *fpga_mgr_class; * mgr pointer from of_fpga_mgr_get() or fpga_mgr_get() and checked that it is * not an error code. * + * This is the preferred entry point for FPGA programming, it does not require + * any contiguous kernel memory. + * * Return: 0 on success, negative error code otherwise. */ -int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info, - const char *buf, size_t count) +int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, struct fpga_image_info *info, + struct sg_table *sgt) { - struct device *dev = &mgr->dev; int ret; - /* - * Call the low level driver's write_init function. This will do the - * device-specific things to get the FPGA into the state where it is - * ready to receive an FPGA image. The low level driver only gets to - * see the first initial_header_size bytes in the buffer. - */ - mgr->state = FPGA_MGR_STATE_WRITE_INIT; - ret = mgr->mops->write_init(mgr, info, buf, - min(mgr->mops->initial_header_size, count)); + ret = fpga_mgr_write_init_sg(mgr, info, sgt); + if (ret) + return ret; + + /* Write the FPGA image to the FPGA. */ + mgr->state = FPGA_MGR_STATE_WRITE; + if (mgr->mops->write_sg) { + ret = mgr->mops->write_sg(mgr, sgt); + } else { + struct sg_mapping_iter miter; + + sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG); + while (sg_miter_next(&miter)) { + ret = mgr->mops->write(mgr, miter.addr, miter.length); + if (ret) + break; + } + sg_miter_stop(&miter); + } + if (ret) { - dev_err(dev, "Error preparing FPGA for writing\n"); - mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR; + dev_err(&mgr->dev, "Error while writing image data to FPGA\n"); + mgr->state = FPGA_MGR_STATE_WRITE_ERR; return ret; } + return fpga_mgr_write_complete(mgr, info); +} +EXPORT_SYMBOL_GPL(fpga_mgr_buf_load_sg); + +static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *buf, size_t count) +{ + int ret; + + ret = fpga_mgr_write_init_buf(mgr, info, buf, count); + if (ret) + return ret; + /* * Write the FPGA image to the FPGA. */ mgr->state = FPGA_MGR_STATE_WRITE; ret = mgr->mops->write(mgr, buf, count); if (ret) { - dev_err(dev, "Error while writing image data to FPGA\n"); + dev_err(&mgr->dev, "Error while writing image data to FPGA\n"); mgr->state = FPGA_MGR_STATE_WRITE_ERR; return ret; } + return fpga_mgr_write_complete(mgr, info); +} + +/** + * fpga_mgr_buf_load - load fpga from image in buffer + * @mgr: fpga manager + * @flags: flags setting fpga confuration modes + * @buf: buffer contain fpga image + * @count: byte count of buf + * + * Step the low level fpga manager through the device-specific steps of getting + * an FPGA ready to be configured, writing the image to it, then doing whatever + * post-configuration steps necessary. This code assumes the caller got the + * mgr pointer from of_fpga_mgr_get() and checked that it is not an error code. + * + * Return: 0 on success, negative error code otherwise. + */ +int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info, + const char *buf, size_t count) +{ + struct page **pages; + struct sg_table sgt; + const void *p; + int nr_pages; + int index; + int rc; + /* - * After all the FPGA image has been written, do the device specific - * steps to finish and set the FPGA into operating mode. + * This is just a fast path if the caller has already created a + * contiguous kernel buffer and the driver doesn't require SG, non-SG + * drivers will still work on the slow path. */ - mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE; - ret = mgr->mops->write_complete(mgr, info); - if (ret) { - dev_err(dev, "Error after writing image data to FPGA\n"); - mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR; - return ret; + if (mgr->mops->write) + return fpga_mgr_buf_load_mapped(mgr, info, buf, count); + + /* + * Convert the linear kernel pointer into a sg_table of pages for use + * by the driver. + */ + nr_pages = DIV_ROUND_UP((unsigned long)buf + count, PAGE_SIZE) - + (unsigned long)buf / PAGE_SIZE; + pages = kmalloc_array(nr_pages, sizeof(struct page *), GFP_KERNEL); + if (!pages) + return -ENOMEM; + + p = buf - offset_in_page(buf); + for (index = 0; index < nr_pages; index++) { + if (is_vmalloc_addr(p)) + pages[index] = vmalloc_to_page(p); + else + pages[index] = kmap_to_page((void *)p); + if (!pages[index]) { + kfree(pages); + return -EFAULT; + } + p += PAGE_SIZE; } - mgr->state = FPGA_MGR_STATE_OPERATING; - return 0; + /* + * The temporary pages list is used to code share the merging algorithm + * in sg_alloc_table_from_pages + */ + rc = sg_alloc_table_from_pages(&sgt, pages, index, offset_in_page(buf), + count, GFP_KERNEL); + kfree(pages); + if (rc) + return rc; + + rc = fpga_mgr_buf_load_sg(mgr, info, &sgt); + sg_free_table(&sgt); + + return rc; } EXPORT_SYMBOL_GPL(fpga_mgr_buf_load); @@ -291,8 +466,9 @@ int fpga_mgr_register(struct device *dev, const char *name, struct fpga_manager *mgr; int id, ret; - if (!mops || !mops->write_init || !mops->write || - !mops->write_complete || !mops->state) { + if (!mops || !mops->write_complete || !mops->state || + !mops->write_init || (!mops->write && !mops->write_sg) || + (mops->write && mops->write_sg)) { dev_err(dev, "Attempt to register without fpga_manager_ops\n"); return -EINVAL; } diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h index 16551d5eac36a7..57beb5d09bfcb2 100644 --- a/include/linux/fpga/fpga-mgr.h +++ b/include/linux/fpga/fpga-mgr.h @@ -22,6 +22,7 @@ #define _LINUX_FPGA_MGR_H struct fpga_manager; +struct sg_table; /** * enum fpga_mgr_states - fpga framework states @@ -88,6 +89,7 @@ struct fpga_image_info { * @state: returns an enum value of the FPGA's state * @write_init: prepare the FPGA to receive confuration data * @write: write count bytes of configuration data to the FPGA + * @write_sg: write the scatter list of configuration data to the FPGA * @write_complete: set FPGA to operating state after writing is done * @fpga_remove: optional: Set FPGA into a specific state during driver remove * @@ -102,6 +104,7 @@ struct fpga_manager_ops { struct fpga_image_info *info, const char *buf, size_t count); int (*write)(struct fpga_manager *mgr, const char *buf, size_t count); + int (*write_sg)(struct fpga_manager *mgr, struct sg_table *sgt); int (*write_complete)(struct fpga_manager *mgr, struct fpga_image_info *info); void (*fpga_remove)(struct fpga_manager *mgr); @@ -129,6 +132,8 @@ struct fpga_manager { int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info, const char *buf, size_t count); +int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, struct fpga_image_info *info, + struct sg_table *sgt); int fpga_mgr_firmware_load(struct fpga_manager *mgr, struct fpga_image_info *info,