From patchwork Sat May 20 06:49:06 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Li, Yi" X-Patchwork-Id: 9738503 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 9B2D8600C8 for ; Sat, 20 May 2017 06:54:58 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8D5BE285BD for ; Sat, 20 May 2017 06:54:58 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 81F24285C2; Sat, 20 May 2017 06:54:58 +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 D7556285BD for ; Sat, 20 May 2017 06:54:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753509AbdETGy5 (ORCPT ); Sat, 20 May 2017 02:54:57 -0400 Received: from mga02.intel.com ([134.134.136.20]:34555 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751442AbdETGyP (ORCPT ); Sat, 20 May 2017 02:54:15 -0400 Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 19 May 2017 23:54:12 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.38,367,1491289200"; d="scan'208";a="1132798913" Received: from yi-patch.an.intel.com ([10.122.105.158]) by orsmga001.jf.intel.com with ESMTP; 19 May 2017 23:54:11 -0700 From: yi1.li@linux.intel.com To: mcgrof@kernel.org, atull@kernel.org, gregkh@linuxfoundation.org, wagi@monom.org, dwmw2@infradead.org, rafal@milecki.pl, arend.vanspriel@broadcom.com, rjw@rjwysocki.net, moritz.fischer@ettus.com, pmladek@suse.com, johannes.berg@intel.com, emmanuel.grumbach@intel.com, luciano.coelho@intel.com, kvalo@codeaurora.org, luto@kernel.org, takahiro.akashi@linaro.org, dhowells@redhat.com, pjones@redhat.com Cc: linux-kernel@vger.kernel.org, linux-fpga@vger.kernel.org, Yi Li Subject: [PATCHv2 1/3] firmware: Add streaming support for driver_data_request_sync API Date: Sat, 20 May 2017 01:49:06 -0500 Message-Id: <1495262948-1106-2-git-send-email-yi1.li@linux.intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1495262948-1106-1-git-send-email-yi1.li@linux.intel.com> References: <1495262948-1106-1-git-send-email-yi1.li@linux.intel.com> 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 From: Yi Li By setting the driver_data_req_params req flag of DRIVER_DATA_REQ_STREAMING and DRIVER_DATA_REQ_NO_CACHE, caller can streaming firmware image to the pre-allocated buffer in small trunks. Caller also need to setup the img_offset pointer and firmware image **path to avoid searching the firmware folders repeatly. Details and examples please refer to the trigger_config_stream function of test_driver_data.c and fpga_mgr_firmware_stream function of fpga_mgr.c. Signed-off-by: Yi Li --- drivers/base/firmware_class.c | 94 +++++++++++++++++++++++++++++++++++++------ include/linux/driver_data.h | 10 +++++ 2 files changed, 91 insertions(+), 13 deletions(-) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 461c7c2..9a63124 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -152,6 +152,20 @@ struct driver_data_params { DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT, \ } +#define __DATA_REQ_FIRMWARE_STREAM_BUF(buf, size, offset, path) \ + .req_params = { \ + .reqs = DRIVER_DATA_REQ_NO_CACHE | \ + DRIVER_DATA_REQ_STREAMING, \ + .alloc_buf = buf, \ + .alloc_buf_size = size, \ + .img_offset = offset, \ + .path = path, \ + }, \ + .priv_params = { \ + .priv_reqs = DRIVER_DATA_PRIV_REQ_FALLBACK | \ + DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT, \ + } + #define __DATA_REQ_FIRMWARE_NOWAIT(module, uevent, gfp, async_cb, async_ctx) \ .req_params = { \ .hold_module = module, \ @@ -180,6 +194,8 @@ struct driver_data_params { (!!((params)->priv_reqs & DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT)) #define driver_data_param_nocache(params) \ (!!((params)->reqs & DRIVER_DATA_REQ_NO_CACHE)) +#define driver_data_param_streaming(params) \ + (!!((params)->reqs & DRIVER_DATA_REQ_STREAMING)) #define driver_data_param_optional(params) \ (!!((params)->reqs & DRIVER_DATA_REQ_OPTIONAL)) @@ -625,14 +641,18 @@ module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644); MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path"); static int -fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf) +fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf, + struct driver_data_params *data_params) { loff_t size; int i, len; int rc = -ENOENT; - char *path; + char **path; + char *local_path = NULL; + loff_t *offset = data_params->req_params.img_offset; enum kernel_read_file_id id = READING_FIRMWARE; size_t msize = INT_MAX; + struct file *file; /* Already populated data member means we're loading into a buffer */ if (buf->data) { @@ -640,16 +660,58 @@ fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf) msize = buf->allocated_size; } - path = __getname(); - if (!path) + buf->size = 0; + + /* Save path for streaming case */ + if (driver_data_param_streaming(&data_params->req_params)) { + path = data_params->req_params.path; + if (!path) { + dev_err(device, "req_params.path not initialized\n"); + rc = -ENOENT; + return rc; + } + } else { + path = &local_path; + } + +streaming: + /* Skip the repeating folder searching, direct to load*/ + if (driver_data_param_streaming(&data_params->req_params) && *path) { + if (!offset) { + dev_err(device, "img_offset not initialized\n"); + rc = -ENOENT; + return rc; + } + + file = filp_open(*path, O_RDONLY, 0); + if (IS_ERR(file)) { + rc = -ENOENT; + return rc; + } + + buf->size = kernel_read(file, *offset, (char *)buf->data, + msize); + fput(file); + if (buf->size < msize) { + fw_state_done(&buf->fw_st); + __putname(*path); + } + return 0; + } + + /* First time to load the firmware */ + *path = __getname(); + if (!*path) { + dev_err(device, "cannot getname\n"); return -ENOMEM; + } for (i = 0; i < ARRAY_SIZE(fw_path); i++) { /* skip the unset customized path */ if (!fw_path[i][0]) continue; - len = snprintf(path, PATH_MAX, "%s/%s", + len = snprintf(*path, PATH_MAX, "%s/%s", fw_path[i], buf->fw_id); if (len >= PATH_MAX) { rc = -ENAMETOOLONG; @@ -657,23 +719,29 @@ fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf) } buf->size = 0; - rc = kernel_read_file_from_path(path, &buf->data, &size, msize, - id); + rc = kernel_read_file_from_path(*path, &buf->data, &size, + msize, id); if (rc) { - if (rc == -ENOENT) + if (driver_data_param_streaming(&data_params->req_params) + && (rc == -EFBIG)) { + rc = 0; + goto streaming; + } else if (rc == -ENOENT) dev_dbg(device, "loading %s failed with error %d\n", - path, rc); + *path, rc); else dev_warn(device, "loading %s failed with error %d\n", - path, rc); + *path, rc); continue; } dev_dbg(device, "direct-loading %s\n", buf->fw_id); buf->size = size; - fw_state_done(&buf->fw_st); + if (buf->size < msize) { + fw_state_done(&buf->fw_st); + __putname(*path); + } break; } - __putname(path); return rc; } @@ -1466,7 +1534,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name, goto out; } - ret = fw_get_filesystem_firmware(device, fw->priv); + ret = fw_get_filesystem_firmware(device, fw->priv, data_params); if (ret) { if (!driver_data_param_optional(&data_params->req_params)) dev_warn(device, diff --git a/include/linux/driver_data.h b/include/linux/driver_data.h index a3a5fbe..e9e828a 100644 --- a/include/linux/driver_data.h +++ b/include/linux/driver_data.h @@ -120,12 +120,16 @@ union driver_data_cbs { * @DRIVER_DATA_REQ_NO_CACHE: indicates that the driver data request * should not set up and use the internal caching mechanism to assist * drivers from fetching driver data at resume time after suspend. + * @DRIVER_DATA_REQ_STREAMING: indicates that the driver data request + * is in the streaming mode, the buffer is allocated outside the + * firmware driver. */ enum driver_data_reqs { DRIVER_DATA_REQ_OPTIONAL = 1 << 0, DRIVER_DATA_REQ_KEEP = 1 << 1, DRIVER_DATA_REQ_USE_API_VERSIONING = 1 << 2, DRIVER_DATA_REQ_NO_CACHE = 1 << 3, + DRIVER_DATA_REQ_STREAMING = 1 << 4, }; /** @@ -147,6 +151,10 @@ enum driver_data_reqs { * @alloc_buf: pointer of pointer to the buffer area allocated by the caller * so we can place the respective driver data * @alloc_buf_size: size of the @alloc_buf + * @img_offset: optional for streaming mode, define which offset address the + * firmware should be read from + * @path: optional for streaming mode, save the file path to avoid search over + * and over again * * This data structure is intended to carry all requirements and specifications * required to complete the task to get the requested driver date file to the @@ -162,6 +170,8 @@ struct driver_data_req_params { const union driver_data_cbs cbs; void **alloc_buf; size_t alloc_buf_size; + loff_t *img_offset; + char **path; }; /*