From 2bcfdc23c6a418b441a1d17f237e9ea347e2fb1f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 7 Jan 2016 14:46:34 +0200 Subject: mei: bus: remove redundant uuid string in debug messages Remove uuid from the debug messages in bus-fixup.c as this is already part of the device name. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus-fixup.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 020de5919c21..b2d2a6ea576c 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -48,8 +48,7 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; */ static void number_of_connections(struct mei_cl_device *cldev) { - dev_dbg(&cldev->dev, "running hook %s on %pUl\n", - __func__, mei_me_cl_uuid(cldev->me_cl)); + dev_dbg(&cldev->dev, "running hook %s\n", __func__); if (cldev->me_cl->props.max_number_of_connections > 1) cldev->do_match = 0; @@ -62,8 +61,8 @@ static void number_of_connections(struct mei_cl_device *cldev) */ static void blacklist(struct mei_cl_device *cldev) { - dev_dbg(&cldev->dev, "running hook %s on %pUl\n", - __func__, mei_me_cl_uuid(cldev->me_cl)); + dev_dbg(&cldev->dev, "running hook %s\n", __func__); + cldev->do_match = 0; } @@ -208,8 +207,7 @@ static void mei_nfc(struct mei_cl_device *cldev) bus = cldev->bus; - dev_dbg(bus->dev, "running hook %s: %pUl match=%d\n", - __func__, mei_me_cl_uuid(cldev->me_cl), cldev->do_match); + dev_dbg(&cldev->dev, "running hook %s\n", __func__); mutex_lock(&bus->device_lock); /* we need to connect to INFO GUID */ -- cgit v1.2.3 From 13cf988562b0c7f06e0016bea2b5304358397702 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 7 Jan 2016 14:46:36 +0200 Subject: mei: prevent queuing new flow control credit. The MEI FW can receive only one flow control for read. Currently the driver only checks if a flow control credit was already sent and read is pending in the rd_pending queue, but it also has to check if flow control credit already queued in the write control queue to prevent sending more than one flow control credits. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index a6c87c713193..72e32615acd9 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1421,6 +1421,25 @@ out: return 0; } +/** + * mei_cl_is_read_fc_cb - check if read cb is waiting for flow control + * for given host client + * + * @cl: host client + * + * Return: true, if found at least one cb. + */ +static bool mei_cl_is_read_fc_cb(struct mei_cl *cl) +{ + struct mei_device *dev = cl->dev; + struct mei_cl_cb *cb; + + list_for_each_entry(cb, &dev->ctrl_wr_list.list, list) + if (cb->fop_type == MEI_FOP_READ && cb->cl == cl) + return true; + return false; +} + /** * mei_cl_read_start - the start read client message function. * @@ -1445,7 +1464,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp) return -ENODEV; /* HW currently supports only one pending read */ - if (!list_empty(&cl->rd_pending)) + if (!list_empty(&cl->rd_pending) || mei_cl_is_read_fc_cb(cl)) return -EBUSY; if (!mei_me_cl_is_active(cl->me_cl)) { -- cgit v1.2.3 From 8b2458f413c429e4c5c4799e02ec2820396aaac4 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 7 Jan 2016 14:46:37 +0200 Subject: mei: always copy the read buffer if data is ready Copy completed callback content to the user space if we have such callback ready in the beginning of the read. Simplify offset processing logic as byproduct. This is a refinement for: commit 139aacf757fc ("mei: fix read after read scenario") Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 677d0362f334..36ca15234344 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -159,27 +159,22 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } + if (ubuf == NULL) { + rets = -EMSGSIZE; + goto out; + } + if (cl == &dev->iamthif_cl) { rets = mei_amthif_read(dev, file, ubuf, length, offset); goto out; } cb = mei_cl_read_cb(cl, file); - if (cb) { - /* read what left */ - if (cb->buf_idx > *offset) - goto copy_buffer; - /* offset is beyond buf_idx we have no more data return 0 */ - if (cb->buf_idx > 0 && cb->buf_idx <= *offset) { - rets = 0; - goto free; - } - /* Offset needs to be cleaned for contiguous reads*/ - if (cb->buf_idx == 0 && *offset > 0) - *offset = 0; - } else if (*offset > 0) { + if (cb) + goto copy_buffer; + + if (*offset > 0) *offset = 0; - } err = mei_cl_read_start(cl, length, file); if (err && err != -EBUSY) { @@ -231,10 +226,10 @@ copy_buffer: goto free; } - cl_dbg(dev, cl, "buf.size = %d buf.idx = %ld\n", - cb->buf.size, cb->buf_idx); - if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) { - rets = -EMSGSIZE; + cl_dbg(dev, cl, "buf.size = %d buf.idx = %ld offset = %lld\n", + cb->buf.size, cb->buf_idx, *offset); + if (*offset >= cb->buf_idx) { + rets = 0; goto free; } @@ -255,6 +250,7 @@ copy_buffer: free: mei_io_cb_free(cb); + *offset = 0; out: cl_dbg(dev, cl, "end mei read rets = %d\n", rets); -- cgit v1.2.3 From b86d1bd8d1a0f67f12b62e5ee724514f19dc6bf8 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 7 Jan 2016 14:46:39 +0200 Subject: mei: drop nfc leftovers from the mei driver We left few function prototypes in the header file after moving nfc logic to bus. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/mei_dev.h | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 4250555d5e72..b54d9d9cacea 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -649,17 +649,6 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_cl_cb *complete_list); int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); -/* - * NFC functions - */ -int mei_nfc_host_init(struct mei_device *dev, struct mei_me_client *me_cl); -void mei_nfc_host_exit(struct mei_device *dev); - -/* - * NFC Client UUID - */ -extern const uuid_le mei_nfc_guid; - int mei_wd_send(struct mei_device *dev); int mei_wd_stop(struct mei_device *dev); int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl); -- cgit v1.2.3 From fdd9b8655933c3eb3154fe1ed351c17b654258bd Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 8 Jan 2016 00:49:21 +0200 Subject: mei: wd: drop the watchdog code from the core mei driver Instead of integrating the iAMT watchdog in the mei core driver we will create a watchdog device on the mei client bus and create a driver for it. This patch removes the watchdog code from the mei core driver. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/Kconfig | 6 +- drivers/misc/mei/Makefile | 1 - drivers/misc/mei/client.c | 12 +- drivers/misc/mei/client.h | 4 - drivers/misc/mei/init.c | 10 +- drivers/misc/mei/interrupt.c | 15 -- drivers/misc/mei/mei_dev.h | 61 +------ drivers/misc/mei/wd.c | 391 ------------------------------------------- 8 files changed, 9 insertions(+), 491 deletions(-) delete mode 100644 drivers/misc/mei/wd.c (limited to 'drivers/misc') diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index d23384dde73b..c49e1d2269af 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -1,6 +1,6 @@ config INTEL_MEI tristate "Intel Management Engine Interface" - depends on X86 && PCI && WATCHDOG_CORE + depends on X86 && PCI help The Intel Management Engine (Intel ME) provides Manageability, Security and Media services for system containing Intel chipsets. @@ -12,7 +12,7 @@ config INTEL_MEI config INTEL_MEI_ME tristate "ME Enabled Intel Chipsets" select INTEL_MEI - depends on X86 && PCI && WATCHDOG_CORE + depends on X86 && PCI help MEI support for ME Enabled Intel chipsets. @@ -37,7 +37,7 @@ config INTEL_MEI_ME config INTEL_MEI_TXE tristate "Intel Trusted Execution Environment with ME Interface" select INTEL_MEI - depends on X86 && PCI && WATCHDOG_CORE + depends on X86 && PCI help MEI Support for Trusted Execution Environment device on Intel SoCs diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 01447ca21c26..59e6b0aede34 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -9,7 +9,6 @@ mei-objs += interrupt.o mei-objs += client.o mei-objs += main.o mei-objs += amthif.o -mei-objs += wd.o mei-objs += bus.o mei-objs += bus-fixup.o mei-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 72e32615acd9..e069fcaed7aa 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -648,7 +648,7 @@ int mei_cl_unlink(struct mei_cl *cl) if (!cl) return 0; - /* wd and amthif might not be initialized */ + /* amthif might not be initialized */ if (!cl->dev) return 0; @@ -679,17 +679,11 @@ void mei_host_client_init(struct work_struct *work) mutex_lock(&dev->device_lock); - me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); if (me_cl) mei_amthif_host_init(dev, me_cl); mei_me_cl_put(me_cl); - me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid); - if (me_cl) - mei_wd_host_init(dev, me_cl); - mei_me_cl_put(me_cl); - dev->dev_state = MEI_DEV_ENABLED; dev->reset_count = 0; mutex_unlock(&dev->device_lock); @@ -1153,7 +1147,7 @@ err: * * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. */ -int mei_cl_flow_ctrl_creds(struct mei_cl *cl) +static int mei_cl_flow_ctrl_creds(struct mei_cl *cl) { int rets; @@ -1186,7 +1180,7 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl) * 0 on success * -EINVAL when ctrl credits are <= 0 */ -int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) +static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) { if (WARN_ON(!cl || !cl->me_cl)) return -EINVAL; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 04e1aa39243f..2e90a25f896e 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -18,7 +18,6 @@ #define _MEI_CLIENT_H_ #include -#include #include #include @@ -120,9 +119,6 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, enum mei_cb_file_ops type, struct file *fp); int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp); -int mei_cl_flow_ctrl_creds(struct mei_cl *cl); - -int mei_cl_flow_ctrl_reduce(struct mei_cl *cl); /* * MEI input output function prototype */ diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 3edafc8d3ad4..46a4302e5524 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -156,8 +156,7 @@ int mei_reset(struct mei_device *dev) mei_cl_all_wakeup(dev); /* remove entry if already in list */ - dev_dbg(dev->dev, "remove iamthif and wd from the file list.\n"); - mei_cl_unlink(&dev->wd_cl); + dev_dbg(dev->dev, "remove iamthif from the file list.\n"); mei_cl_unlink(&dev->iamthif_cl); mei_amthif_reset_params(dev); } @@ -165,7 +164,6 @@ int mei_reset(struct mei_device *dev) mei_hbm_reset(dev); dev->rd_msg_hdr = 0; - dev->wd_pending = false; if (ret) { dev_err(dev->dev, "hw_reset failed ret = %d\n", ret); @@ -335,16 +333,12 @@ void mei_stop(struct mei_device *dev) mutex_lock(&dev->device_lock); - mei_wd_stop(dev); - dev->dev_state = MEI_DEV_POWER_DOWN; mei_reset(dev); /* move device to disabled state unconditionally */ dev->dev_state = MEI_DEV_DISABLED; mutex_unlock(&dev->device_lock); - - mei_watchdog_unregister(dev); } EXPORT_SYMBOL_GPL(mei_stop); @@ -394,7 +388,6 @@ void mei_device_init(struct mei_device *dev, init_waitqueue_head(&dev->wait_hw_ready); init_waitqueue_head(&dev->wait_pg); init_waitqueue_head(&dev->wait_hbm_start); - init_waitqueue_head(&dev->wait_stop_wd); dev->dev_state = MEI_DEV_INITIALIZING; dev->reset_count = 0; @@ -407,7 +400,6 @@ void mei_device_init(struct mei_device *dev, INIT_WORK(&dev->init_work, mei_host_client_init); INIT_WORK(&dev->reset_work, mei_reset_work); - INIT_LIST_HEAD(&dev->wd_cl.link); INIT_LIST_HEAD(&dev->iamthif_cl.link); mei_io_list_init(&dev->amthif_cmd_list); mei_io_list_init(&dev->amthif_rd_complete_list); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 64b568a0268d..6340dee33052 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -360,21 +360,6 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) list_move_tail(&cb->list, &cmpl_list->list); } - if (dev->wd_state == MEI_WD_STOPPING) { - dev->wd_state = MEI_WD_IDLE; - wake_up(&dev->wait_stop_wd); - } - - if (mei_cl_is_connected(&dev->wd_cl)) { - if (dev->wd_pending && - mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) { - ret = mei_wd_send(dev); - if (ret) - return ret; - dev->wd_pending = false; - } - } - /* complete control write list CB */ dev_dbg(dev->dev, "complete control write list cb.\n"); list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index b54d9d9cacea..da613268480c 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -18,7 +18,7 @@ #define _MEI_DEV_H_ #include -#include +#include #include #include #include @@ -26,33 +26,13 @@ #include "hw.h" #include "hbm.h" -/* - * watch dog definition - */ -#define MEI_WD_HDR_SIZE 4 -#define MEI_WD_STOP_MSG_SIZE MEI_WD_HDR_SIZE -#define MEI_WD_START_MSG_SIZE (MEI_WD_HDR_SIZE + 16) - -#define MEI_WD_DEFAULT_TIMEOUT 120 /* seconds */ -#define MEI_WD_MIN_TIMEOUT 120 /* seconds */ -#define MEI_WD_MAX_TIMEOUT 65535 /* seconds */ - -#define MEI_WD_STOP_TIMEOUT 10 /* msecs */ - -#define MEI_WD_STATE_INDEPENDENCE_MSG_SENT (1 << 0) - -#define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32)) - /* * AMTHI Client UUID */ extern const uuid_le mei_amthif_guid; -/* - * Watchdog Client UUID - */ -extern const uuid_le mei_wd_guid; +#define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32)) /* * Number of Maximum MEI Clients @@ -78,7 +58,6 @@ extern const uuid_le mei_wd_guid; */ #define MEI_HOST_CLIENT_ID_ANY (-1) #define MEI_HBM_HOST_CLIENT_ID 0 /* not used, just for documentation */ -#define MEI_WD_HOST_CLIENT_ID 1 #define MEI_IAMTHIF_HOST_CLIENT_ID 2 @@ -123,12 +102,6 @@ enum mei_file_transaction_states { MEI_READ_COMPLETE }; -enum mei_wd_states { - MEI_WD_IDLE, - MEI_WD_RUNNING, - MEI_WD_STOPPING, -}; - /** * enum mei_cb_file_ops - file operation associated with the callback * @MEI_FOP_READ: read @@ -404,7 +377,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @wait_hw_ready : wait queue for receive HW ready message form FW * @wait_pg : wait queue for receive PG message from FW * @wait_hbm_start : wait queue for receive HBM start message from FW - * @wait_stop_wd : wait queue for receive WD stop message from FW * * @reset_count : number of consecutive resets * @dev_state : device state @@ -435,12 +407,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * * @allow_fixed_address: allow user space to connect a fixed client * - * @wd_cl : watchdog client - * @wd_state : watchdog client state - * @wd_pending : watchdog command is pending - * @wd_timeout : watchdog expiration timeout - * @wd_data : watchdog message buffer - * * @amthif_cmd_list : amthif list for cmd waiting * @amthif_rd_complete_list : amthif list for reading completed cmd data * @iamthif_file_object : file for current amthif operation @@ -486,7 +452,6 @@ struct mei_device { wait_queue_head_t wait_hw_ready; wait_queue_head_t wait_pg; wait_queue_head_t wait_hbm_start; - wait_queue_head_t wait_stop_wd; /* * mei device states @@ -531,13 +496,6 @@ struct mei_device { bool allow_fixed_address; - struct mei_cl wd_cl; - enum mei_wd_states wd_state; - bool wd_pending; - u16 wd_timeout; - unsigned char wd_data[MEI_WD_START_MSG_SIZE]; - - /* amthif list for cmd waiting */ struct mei_cl_cb amthif_cmd_list; /* driver managed amthif list for reading completed amthif cmd data */ @@ -649,21 +607,6 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_cl_cb *complete_list); int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); -int mei_wd_send(struct mei_device *dev); -int mei_wd_stop(struct mei_device *dev); -int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl); -/* - * mei_watchdog_register - Registering watchdog interface - * once we got connection to the WD Client - * @dev: mei device - */ -int mei_watchdog_register(struct mei_device *dev); -/* - * mei_watchdog_unregister - Unregistering watchdog interface - * @dev: mei device - */ -void mei_watchdog_unregister(struct mei_device *dev); - /* * Register Access Function */ diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c deleted file mode 100644 index b346638833b0..000000000000 --- a/drivers/misc/mei/wd.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * - * Intel Management Engine Interface (Intel MEI) Linux driver - * Copyright (c) 2003-2012, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - */ -#include -#include -#include -#include -#include -#include - -#include - -#include "mei_dev.h" -#include "hbm.h" -#include "client.h" - -static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 }; -static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 }; - -/* - * AMT Watchdog Device - */ -#define INTEL_AMT_WATCHDOG_ID "INTCAMT" - -/* UUIDs for AMT F/W clients */ -const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, - 0x9D, 0xA9, 0x15, 0x14, 0xCB, - 0x32, 0xAB); - -static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) -{ - dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout); - memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE); - memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16)); -} - -/** - * mei_wd_host_init - connect to the watchdog client - * - * @dev: the device structure - * @me_cl: me client - * - * Return: -ENOTTY if wd client cannot be found - * -EIO if write has failed - * 0 on success - */ -int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl) -{ - struct mei_cl *cl = &dev->wd_cl; - int ret; - - mei_cl_init(cl, dev); - - dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; - dev->wd_state = MEI_WD_IDLE; - - ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); - if (ret < 0) { - dev_info(dev->dev, "wd: failed link client\n"); - return ret; - } - - ret = mei_cl_connect(cl, me_cl, NULL); - if (ret) { - dev_err(dev->dev, "wd: failed to connect = %d\n", ret); - mei_cl_unlink(cl); - return ret; - } - - ret = mei_watchdog_register(dev); - if (ret) { - mei_cl_disconnect(cl); - mei_cl_unlink(cl); - } - return ret; -} - -/** - * mei_wd_send - sends watch dog message to fw. - * - * @dev: the device structure - * - * Return: 0 if success, - * -EIO when message send fails - * -EINVAL when invalid message is to be sent - * -ENODEV on flow control failure - */ -int mei_wd_send(struct mei_device *dev) -{ - struct mei_cl *cl = &dev->wd_cl; - struct mei_msg_hdr hdr; - int ret; - - hdr.host_addr = cl->host_client_id; - hdr.me_addr = mei_cl_me_id(cl); - hdr.msg_complete = 1; - hdr.reserved = 0; - hdr.internal = 0; - - if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE)) - hdr.length = MEI_WD_START_MSG_SIZE; - else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE)) - hdr.length = MEI_WD_STOP_MSG_SIZE; - else { - dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n"); - return -EINVAL; - } - - ret = mei_write_message(dev, &hdr, dev->wd_data); - if (ret) { - dev_err(dev->dev, "wd: write message failed\n"); - return ret; - } - - ret = mei_cl_flow_ctrl_reduce(cl); - if (ret) { - dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n"); - return ret; - } - - return 0; -} - -/** - * mei_wd_stop - sends watchdog stop message to fw. - * - * @dev: the device structure - * - * Return: 0 if success - * on error: - * -EIO when message send fails - * -EINVAL when invalid message is to be sent - * -ETIME on message timeout - */ -int mei_wd_stop(struct mei_device *dev) -{ - struct mei_cl *cl = &dev->wd_cl; - int ret; - - if (!mei_cl_is_connected(cl) || - dev->wd_state != MEI_WD_RUNNING) - return 0; - - memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE); - - dev->wd_state = MEI_WD_STOPPING; - - ret = mei_cl_flow_ctrl_creds(cl); - if (ret < 0) - goto err; - - if (ret && mei_hbuf_acquire(dev)) { - ret = mei_wd_send(dev); - if (ret) - goto err; - dev->wd_pending = false; - } else { - dev->wd_pending = true; - } - - mutex_unlock(&dev->device_lock); - - ret = wait_event_timeout(dev->wait_stop_wd, - dev->wd_state == MEI_WD_IDLE, - msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); - mutex_lock(&dev->device_lock); - if (dev->wd_state != MEI_WD_IDLE) { - /* timeout */ - ret = -ETIME; - dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret); - goto err; - } - dev_dbg(dev->dev, "wd: stop completed after %u msec\n", - MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret)); - return 0; -err: - return ret; -} - -/** - * mei_wd_ops_start - wd start command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_start(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - struct mei_cl *cl; - int err = -ENODEV; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - cl = &dev->wd_cl; - - mutex_lock(&dev->device_lock); - - if (dev->dev_state != MEI_DEV_ENABLED) { - dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", - mei_dev_state_str(dev->dev_state)); - goto end_unlock; - } - - if (!mei_cl_is_connected(cl)) { - cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n"); - goto end_unlock; - } - - mei_wd_set_start_timeout(dev, dev->wd_timeout); - - err = 0; -end_unlock: - mutex_unlock(&dev->device_lock); - return err; -} - -/** - * mei_wd_ops_stop - wd stop command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_stop(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - mutex_lock(&dev->device_lock); - mei_wd_stop(dev); - mutex_unlock(&dev->device_lock); - - return 0; -} - -/** - * mei_wd_ops_ping - wd ping command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_ping(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - struct mei_cl *cl; - int ret; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - cl = &dev->wd_cl; - - mutex_lock(&dev->device_lock); - - if (!mei_cl_is_connected(cl)) { - cl_err(dev, cl, "wd: not connected.\n"); - ret = -ENODEV; - goto end; - } - - dev->wd_state = MEI_WD_RUNNING; - - ret = mei_cl_flow_ctrl_creds(cl); - if (ret < 0) - goto end; - - /* Check if we can send the ping to HW*/ - if (ret && mei_hbuf_acquire(dev)) { - dev_dbg(dev->dev, "wd: sending ping\n"); - - ret = mei_wd_send(dev); - if (ret) - goto end; - dev->wd_pending = false; - } else { - dev->wd_pending = true; - } - -end: - mutex_unlock(&dev->device_lock); - return ret; -} - -/** - * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core. - * - * @wd_dev: watchdog device struct - * @timeout: timeout value to set - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, - unsigned int timeout) -{ - struct mei_device *dev; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - /* Check Timeout value */ - if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT) - return -EINVAL; - - mutex_lock(&dev->device_lock); - - dev->wd_timeout = timeout; - wd_dev->timeout = timeout; - mei_wd_set_start_timeout(dev, dev->wd_timeout); - - mutex_unlock(&dev->device_lock); - - return 0; -} - -/* - * Watchdog Device structs - */ -static const struct watchdog_ops wd_ops = { - .owner = THIS_MODULE, - .start = mei_wd_ops_start, - .stop = mei_wd_ops_stop, - .ping = mei_wd_ops_ping, - .set_timeout = mei_wd_ops_set_timeout, -}; -static const struct watchdog_info wd_info = { - .identity = INTEL_AMT_WATCHDOG_ID, - .options = WDIOF_KEEPALIVEPING | - WDIOF_SETTIMEOUT | - WDIOF_ALARMONLY, -}; - -static struct watchdog_device amt_wd_dev = { - .info = &wd_info, - .ops = &wd_ops, - .timeout = MEI_WD_DEFAULT_TIMEOUT, - .min_timeout = MEI_WD_MIN_TIMEOUT, - .max_timeout = MEI_WD_MAX_TIMEOUT, -}; - - -int mei_watchdog_register(struct mei_device *dev) -{ - - int ret; - - amt_wd_dev.parent = dev->dev; - /* unlock to perserve correct locking order */ - mutex_unlock(&dev->device_lock); - ret = watchdog_register_device(&amt_wd_dev); - mutex_lock(&dev->device_lock); - if (ret) { - dev_err(dev->dev, "wd: unable to register watchdog device = %d.\n", - ret); - return ret; - } - - dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n"); - watchdog_set_drvdata(&amt_wd_dev, dev); - return 0; -} - -void mei_watchdog_unregister(struct mei_device *dev) -{ - if (watchdog_get_drvdata(&amt_wd_dev) == NULL) - return; - - watchdog_set_drvdata(&amt_wd_dev, NULL); - watchdog_unregister_device(&amt_wd_dev); -} - -- cgit v1.2.3 From e97cdb303c04bfe60283094bdced68a0d363bd5d Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 8 Jan 2016 00:49:24 +0200 Subject: mei: bus: whitelist the watchdog client The iAMT WD client has to be whitelisted sice it has two connections and is filtered out by number_of_connections fixup. Also the API has changed for BDW and SKL but firmware haven't updated the protocol version. Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus-fixup.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index b2d2a6ea576c..b87323f4bb14 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -35,6 +35,9 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; #define MEI_UUID_NFC_HCI UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \ 0x94, 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c) +#define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \ + 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB) + #define MEI_UUID_ANY NULL_UUID_LE /** @@ -66,6 +69,31 @@ static void blacklist(struct mei_cl_device *cldev) cldev->do_match = 0; } +/** + * mei_wd - wd client on the bus, change protocol version + * as the API has changed. + * + * @cldev: me clients device + */ +#if IS_ENABLED(CONFIG_INTEL_MEI_ME) +#include +#include "hw-me-regs.h" +static void mei_wd(struct mei_cl_device *cldev) +{ + struct pci_dev *pdev = to_pci_dev(cldev->dev.parent); + + dev_dbg(&cldev->dev, "running hook %s\n", __func__); + if (pdev->device == MEI_DEV_ID_WPT_LP || + pdev->device == MEI_DEV_ID_SPT || + pdev->device == MEI_DEV_ID_SPT_H) + cldev->me_cl->props.protocol_version = 0x2; + + cldev->do_match = 1; +} +#else +static inline void mei_wd(struct mei_cl_device *cldev) {} +#endif /* CONFIG_INTEL_MEI_ME */ + struct mei_nfc_cmd { u8 command; u8 status; @@ -280,6 +308,7 @@ static struct mei_fixup { MEI_FIXUP(MEI_UUID_ANY, number_of_connections), MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist), MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc), + MEI_FIXUP(MEI_UUID_WD, mei_wd), }; /** -- cgit v1.2.3 From 7326fffb712f09a315bc73cc1ee63843f59b8bd4 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 17 Jan 2016 12:25:01 +0200 Subject: mei: validate request value in client notify request ioctl This patch address a possible security issue: The request field in client notify request ioctl comes from user space as u32 and is downcasted to u8 with out validation. Check request field to have approved values MEI_HBM_NOTIFICATION_STAR/STOP Cc: #4.3+ Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 677d0362f334..80f9afcb1382 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -458,7 +458,11 @@ static int mei_ioctl_client_notify_request(struct file *file, u32 request) { struct mei_cl *cl = file->private_data; - return mei_cl_notify_request(cl, file, request); + if (request != MEI_HBM_NOTIFICATION_START && + request != MEI_HBM_NOTIFICATION_STOP) + return -EINVAL; + + return mei_cl_notify_request(cl, file, (u8)request); } /** -- cgit v1.2.3 From a96c548291719ae40da1b3c52493f40a63d3dd84 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 22:46:51 +0200 Subject: mei: trace pci configuration space io Use tracing events also for reading and writing pci configuration space /tracing/events/mei/mei_pci_reg_{read,write} Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 10 ++++++++-- drivers/misc/mei/hw-txe.c | 10 ++++++++-- drivers/misc/mei/mei-trace.c | 2 ++ drivers/misc/mei/mei-trace.h | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 4 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 25b1997a62cb..e2fb44cc5c37 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -189,8 +189,11 @@ static int mei_me_fw_status(struct mei_device *dev, fw_status->count = fw_src->count; for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { - ret = pci_read_config_dword(pdev, - fw_src->status[i], &fw_status->status[i]); + ret = pci_read_config_dword(pdev, fw_src->status[i], + &fw_status->status[i]); + trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HSF_X", + fw_src->status[i], + fw_status->status[i]); if (ret) return ret; } @@ -215,6 +218,7 @@ static void mei_me_hw_config(struct mei_device *dev) reg = 0; pci_read_config_dword(pdev, PCI_CFG_HFS_1, ®); + trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); hw->d0i3_supported = ((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK); @@ -1248,6 +1252,7 @@ static bool mei_me_fw_type_nm(struct pci_dev *pdev) u32 reg; pci_read_config_dword(pdev, PCI_CFG_HFS_2, ®); + trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg); /* make sure that bit 9 (NM) is up and bit 10 (DM) is down */ return (reg & 0x600) == 0x200; } @@ -1260,6 +1265,7 @@ static bool mei_me_fw_type_sps(struct pci_dev *pdev) u32 reg; /* Read ME FW Status check for SPS Firmware */ pci_read_config_dword(pdev, PCI_CFG_HFS_1, ®); + trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); /* if bits [19:16] = 15, running SPS Firmware */ return (reg & 0xf0000) == 0xf0000; } diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index bae680c648ff..4a6c1b85f11e 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -28,6 +28,9 @@ #include "client.h" #include "hbm.h" +#include "mei-trace.h" + + /** * mei_txe_reg_read - Reads 32bit data from the txe device * @@ -640,8 +643,11 @@ static int mei_txe_fw_status(struct mei_device *dev, fw_status->count = fw_src->count; for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { - ret = pci_read_config_dword(pdev, - fw_src->status[i], &fw_status->status[i]); + ret = pci_read_config_dword(pdev, fw_src->status[i], + &fw_status->status[i]); + trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HSF_X", + fw_src->status[i], + fw_status->status[i]); if (ret) return ret; } diff --git a/drivers/misc/mei/mei-trace.c b/drivers/misc/mei/mei-trace.c index 388efb519138..e19e6acb191b 100644 --- a/drivers/misc/mei/mei-trace.c +++ b/drivers/misc/mei/mei-trace.c @@ -22,4 +22,6 @@ EXPORT_TRACEPOINT_SYMBOL(mei_reg_read); EXPORT_TRACEPOINT_SYMBOL(mei_reg_write); +EXPORT_TRACEPOINT_SYMBOL(mei_pci_cfg_read); +EXPORT_TRACEPOINT_SYMBOL(mei_pci_cfg_write); #endif /* __CHECKER__ */ diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h index 47e1bc6551d4..86e5068837c1 100644 --- a/drivers/misc/mei/mei-trace.h +++ b/drivers/misc/mei/mei-trace.h @@ -64,6 +64,44 @@ TRACE_EVENT(mei_reg_write, __get_str(dev), __entry->reg, __entry->offs, __entry->val) ); +TRACE_EVENT(mei_pci_cfg_read, + TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val), + TP_ARGS(dev, reg, offs, val), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(const char *, reg) + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)) + __entry->reg = reg; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] pci cfg read %s:[%#x] = %#x", + __get_str(dev), __entry->reg, __entry->offs, __entry->val) +); + +TRACE_EVENT(mei_pci_cfg_write, + TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val), + TP_ARGS(dev, reg, offs, val), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(const char *, reg) + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)) + __entry->reg = reg; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] pci cfg write %s[%#x] = %#x)", + __get_str(dev), __entry->reg, __entry->offs, __entry->val) +); + #endif /* _MEI_TRACE_H_ */ /* This part must be outside protection */ -- cgit v1.2.3 From 26900254827878c4f98c0d169f8ba290099b1ef3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:17 +0200 Subject: mei: debugfs: adjust active clients print buffer In case of many active host clients clients (41 and more) 1K buffer is not enough for full information print. Calculate buffer size according to real clients number. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/debugfs.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index a138d8a27ab5..9f5410b98688 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -50,6 +50,7 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, } pos += scnprintf(buf + pos, bufsz - pos, HDR); +#undef HDR /* if the driver is not enabled the list won't be consistent */ if (dev->dev_state != MEI_DEV_ENABLED) @@ -90,23 +91,37 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, { struct mei_device *dev = fp->private_data; struct mei_cl *cl; - const size_t bufsz = 1024; + size_t bufsz = 1; char *buf; int i = 0; int pos = 0; int ret; +#define HDR " |me|host|state|rd|wr|\n" + if (!dev) return -ENODEV; + mutex_lock(&dev->device_lock); + + /* + * if the driver is not enabled the list won't be consistent, + * we output empty table + */ + if (dev->dev_state == MEI_DEV_ENABLED) + list_for_each_entry(cl, &dev->file_list, link) + bufsz++; + + bufsz *= sizeof(HDR) + 1; + buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) + if (!buf) { + mutex_unlock(&dev->device_lock); return -ENOMEM; + } - pos += scnprintf(buf + pos, bufsz - pos, - " |me|host|state|rd|wr|\n"); - - mutex_lock(&dev->device_lock); + pos += scnprintf(buf + pos, bufsz - pos, HDR); +#undef HDR /* if the driver is not enabled the list won't be consistent */ if (dev->dev_state != MEI_DEV_ENABLED) @@ -115,7 +130,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, list_for_each_entry(cl, &dev->file_list, link) { pos += scnprintf(buf + pos, bufsz - pos, - "%2d|%2d|%4d|%5d|%2d|%2d|\n", + "%3d|%2d|%4d|%5d|%2d|%2d|\n", i, mei_cl_me_id(cl), cl->host_client_id, cl->state, !list_empty(&cl->rd_completed), cl->writing_state); i++; -- cgit v1.2.3 From 439a74b3373dab4214b4191d50dfb1d78be5b18e Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:18 +0200 Subject: mei: debugfs: allow hbm features list dump in earlier stages HBM features list is ready while sending enumerate request and enumerating clients, output it to debugfs in these states too. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/debugfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index 9f5410b98688..6bdd75424fe8 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -165,7 +165,8 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n", mei_hbm_state_str(dev->hbm_state)); - if (dev->hbm_state == MEI_HBM_STARTED) { + if (dev->hbm_state >= MEI_HBM_ENUM_CLIENTS && + dev->hbm_state <= MEI_HBM_STARTED) { pos += scnprintf(buf + pos, bufsz - pos, "hbm features:\n"); pos += scnprintf(buf + pos, bufsz - pos, "\tPG: %01d\n", dev->hbm_f_pg_supported); -- cgit v1.2.3 From f862b6b24f0ffd954633a55f39251a6873b664ca Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:19 +0200 Subject: mei: fix possible integer overflow issue There is a possible integer overflow following by a buffer overflow when accumulating messages coming from the FW to compose a full payload. Occurrence of wrap around has to be prevented for next message size calculation. For unsigned integer the addition overflow has occurred when the result is smaller than one of the arguments. To simplify the fix, the types of buf.size and buf_idx are set to the same width, namely size_t also to be aligned with the type of length parameter in file read/write ops. Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 5 ++--- drivers/misc/mei/client.c | 2 +- drivers/misc/mei/interrupt.c | 21 ++++++++++++++++----- drivers/misc/mei/main.c | 5 +++-- drivers/misc/mei/mei_dev.h | 4 ++-- 5 files changed, 24 insertions(+), 13 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index cd0403f09267..b753df98b476 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -195,9 +195,8 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, * remove message from deletion list */ - dev_dbg(dev->dev, "amthif cb->buf size - %d\n", - cb->buf.size); - dev_dbg(dev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx); + dev_dbg(dev->dev, "amthif cb->buf.size - %zd cb->buf_idx - %zd\n", + cb->buf.size, cb->buf_idx); /* length is being truncated to PAGE_SIZE, however, * the buf_idx may point beyond */ diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e069fcaed7aa..738f3d703323 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1569,7 +1569,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, return 0; } - cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", + cl_dbg(dev, cl, "buf: size = %zd idx = %zd\n", cb->buf.size, cb->buf_idx); rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 6340dee33052..b8aa047ec258 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -104,6 +104,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; unsigned char *buffer = NULL; + size_t buf_sz; cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list); if (!cb) { @@ -124,11 +125,21 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, goto out; } - if (cb->buf.size < mei_hdr->length + cb->buf_idx) { - cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n", + buf_sz = mei_hdr->length + cb->buf_idx; + /* catch for integer overflow */ + if (buf_sz < cb->buf_idx) { + cl_err(dev, cl, "message is too big len %d idx %ld\n", + mei_hdr->length, cb->buf_idx); + + list_move_tail(&cb->list, &complete_list->list); + cb->status = -EMSGSIZE; + goto out; + } + + if (cb->buf.size < buf_sz) { + cl_dbg(dev, cl, "message overflow. size %zd len %d idx %zd\n", cb->buf.size, mei_hdr->length, cb->buf_idx); - buffer = krealloc(cb->buf.data, mei_hdr->length + cb->buf_idx, - GFP_KERNEL); + buffer = krealloc(cb->buf.data, buf_sz, GFP_KERNEL); if (!buffer) { cb->status = -ENOMEM; @@ -136,7 +147,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, goto out; } cb->buf.data = buffer; - cb->buf.size = mei_hdr->length + cb->buf_idx; + cb->buf.size = buf_sz; } buffer = cb->buf.data + cb->buf_idx; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 36ca15234344..47dc6d9ae655 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -226,7 +226,7 @@ copy_buffer: goto free; } - cl_dbg(dev, cl, "buf.size = %d buf.idx = %ld offset = %lld\n", + cl_dbg(dev, cl, "buf.size = %zd buf.idx = %zd offset = %lld\n", cb->buf.size, cb->buf_idx, *offset); if (*offset >= cb->buf_idx) { rets = 0; @@ -245,7 +245,8 @@ copy_buffer: rets = length; *offset += length; - if ((unsigned long)*offset < cb->buf_idx) + /* not all data was read, keep the cb */ + if (*offset < cb->buf_idx) goto out; free: diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index da613268480c..9b793f87b7d4 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -126,7 +126,7 @@ enum mei_cb_file_ops { * Intel MEI message data struct */ struct mei_msg_data { - u32 size; + size_t size; unsigned char *data; }; @@ -190,7 +190,7 @@ struct mei_cl_cb { struct mei_cl *cl; enum mei_cb_file_ops fop_type; struct mei_msg_data buf; - unsigned long buf_idx; + size_t buf_idx; unsigned long read_time; struct file *file_object; int status; -- cgit v1.2.3 From 1f7e489a285c8b1aca9a7b66a55bbc95eb031721 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:20 +0200 Subject: mei: call stop on failed char device register If registering of character device failed stop the device properly. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/pci-me.c | 4 +++- drivers/misc/mei/pci-txe.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 75fc9c688df8..996344f6c32d 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -210,7 +210,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = mei_register(dev, &pdev->dev); if (err) - goto release_irq; + goto stop; pci_set_drvdata(pdev, dev); @@ -231,6 +231,8 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; +stop: + mei_stop(dev); release_irq: mei_cancel_work(dev); mei_disable_interrupts(dev); diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index 71f8a7475717..30cc30683c07 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -154,7 +154,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = mei_register(dev, &pdev->dev); if (err) - goto release_irq; + goto stop; pci_set_drvdata(pdev, dev); @@ -170,6 +170,8 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; +stop: + mei_stop(dev); release_irq: mei_cancel_work(dev); -- cgit v1.2.3 From 5ba0bf476c792b91543da77e0bdec311b0eaa8f7 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:21 +0200 Subject: mei: amthif: don't copy from an empty buffer If empty message come from FW (buf_idx == 0) then the current code will still try to copy data from not filled buffer to the user-space, instead the code should behave the same as when end of a message has been reached, clean resources and return 0 Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index b753df98b476..c3f514027c80 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -185,7 +185,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, /* if the whole message will fit remove it from the list */ if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset)) list_del_init(&cb->list); - else if (cb->buf_idx > 0 && cb->buf_idx <= *offset) { + else if (cb->buf_idx <= *offset) { /* end of the message has been reached */ list_del_init(&cb->list); rets = 0; -- cgit v1.2.3 From d0df8dfb8582f50053bde6a771262571daedf5d0 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:22 +0200 Subject: mei: amthif: don't drop read packets on timeout Since the driver now uses a list for storing read packets instead of single variable a pending read is no longer blocking other connections. A pending read will be discarded up the file closure. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 27 --------------------------- drivers/misc/mei/hw.h | 1 - drivers/misc/mei/interrupt.c | 32 -------------------------------- drivers/misc/mei/main.c | 16 ---------------- drivers/misc/mei/mei_dev.h | 4 ---- 5 files changed, 80 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index c3f514027c80..5da1654bc209 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -50,7 +50,6 @@ void mei_amthif_reset_params(struct mei_device *dev) dev->iamthif_current_cb = NULL; dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_timer = 0; dev->iamthif_stall_timer = 0; dev->iamthif_open_count = 0; } @@ -124,18 +123,10 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, int mei_amthif_read(struct mei_device *dev, struct file *file, char __user *ubuf, size_t length, loff_t *offset) { - struct mei_cl *cl = file->private_data; struct mei_cl_cb *cb; - unsigned long timeout; int rets; int wait_ret; - /* Only possible if we are in timeout */ - if (!cl) { - dev_err(dev->dev, "bad file ext.\n"); - return -ETIME; - } - dev_dbg(dev->dev, "checking amthif data\n"); cb = mei_amthif_find_read_list_entry(dev, file); @@ -168,20 +159,6 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, } dev_dbg(dev->dev, "Got amthif data\n"); - dev->iamthif_timer = 0; - - timeout = cb->read_time + - mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); - dev_dbg(dev->dev, "amthif timeout = %lud\n", - timeout); - - if (time_after(jiffies, timeout)) { - dev_dbg(dev->dev, "amthif Time out\n"); - /* 15 sec for the message has expired */ - list_del_init(&cb->list); - rets = -ETIME; - goto free; - } /* if the whole message will fit remove it from the list */ if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset)) list_del_init(&cb->list); @@ -303,7 +280,6 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_timer = 0; dev->iamthif_file_object = NULL; dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); @@ -462,9 +438,6 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) dev->iamthif_stall_timer = 0; list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); dev_dbg(dev->dev, "amthif read completed\n"); - dev->iamthif_timer = jiffies; - dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n", - dev->iamthif_timer); } else { mei_amthif_run_next_cmd(dev); } diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 4cebde85924f..4c5d6cfd79b4 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -29,7 +29,6 @@ #define MEI_CLIENTS_INIT_TIMEOUT 15 /* HPS: Clients Enumeration Timeout */ #define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */ -#define MEI_IAMTHIF_READ_TIMER 10 /* HPS */ #define MEI_PGI_TIMEOUT 1 /* PG Isolation time response 1 sec */ #define MEI_D0I3_TIMEOUT 5 /* D0i3 set/unset max response time */ diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index b8aa047ec258..78978193d19b 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -156,7 +156,6 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, cb->buf_idx += mei_hdr->length; if (mei_hdr->msg_complete) { - cb->read_time = jiffies; cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx); list_move_tail(&cb->list, &complete_list->list); } else { @@ -458,7 +457,6 @@ static void mei_connect_timeout(struct mei_cl *cl) */ void mei_timer(struct work_struct *work) { - unsigned long timeout; struct mei_cl *cl; struct mei_device *dev = container_of(work, @@ -504,7 +502,6 @@ void mei_timer(struct work_struct *work) mei_reset(dev); dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_timer = 0; mei_io_cb_free(dev->iamthif_current_cb); dev->iamthif_current_cb = NULL; @@ -514,35 +511,6 @@ void mei_timer(struct work_struct *work) } } - if (dev->iamthif_timer) { - - timeout = dev->iamthif_timer + - mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); - - dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n", - dev->iamthif_timer); - dev_dbg(dev->dev, "timeout = %ld\n", timeout); - dev_dbg(dev->dev, "jiffies = %ld\n", jiffies); - if (time_after(jiffies, timeout)) { - /* - * User didn't read the AMTHI data on time (15sec) - * freeing AMTHI for other requests - */ - - dev_dbg(dev->dev, "freeing AMTHI for other requests\n"); - - mei_io_list_flush(&dev->amthif_rd_complete_list, - &dev->iamthif_cl); - mei_io_cb_free(dev->iamthif_current_cb); - dev->iamthif_current_cb = NULL; - - dev->iamthif_file_object->private_data = NULL; - dev->iamthif_file_object = NULL; - dev->iamthif_timer = 0; - mei_amthif_run_next_cmd(dev); - - } - } out: if (dev->dev_state != MEI_DEV_DISABLED) schedule_delayed_work(&dev->timer_work, 2 * HZ); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 47dc6d9ae655..5f2b63460d88 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -274,7 +274,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, struct mei_cl *cl = file->private_data; struct mei_cl_cb *write_cb = NULL; struct mei_device *dev; - unsigned long timeout = 0; int rets; if (WARN_ON(!cl || !cl->dev)) @@ -310,21 +309,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; } - if (cl == &dev->iamthif_cl) { - write_cb = mei_amthif_find_read_list_entry(dev, file); - - if (write_cb) { - timeout = write_cb->read_time + - mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); - - if (time_after(jiffies, timeout)) { - *offset = 0; - mei_io_cb_free(write_cb); - write_cb = NULL; - } - } - } - *offset = 0; write_cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file); if (!write_cb) { diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 9b793f87b7d4..4d0e066703ef 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -179,7 +179,6 @@ struct mei_cl; * @fop_type: file operation type * @buf: buffer for data associated with the callback * @buf_idx: last read index - * @read_time: last read operation time stamp (iamthif) * @file_object: pointer to file structure * @status: io status of the cb * @internal: communication between driver and FW flag @@ -191,7 +190,6 @@ struct mei_cl_cb { enum mei_cb_file_ops fop_type; struct mei_msg_data buf; size_t buf_idx; - unsigned long read_time; struct file *file_object; int status; u32 internal:1; @@ -413,7 +411,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @iamthif_cl : amthif host client * @iamthif_current_cb : amthif current operation callback * @iamthif_open_count : number of opened amthif connections - * @iamthif_timer : time stamp of current amthif command completion * @iamthif_stall_timer : timer to detect amthif hang * @iamthif_state : amthif processor state * @iamthif_canceled : current amthif command is canceled @@ -504,7 +501,6 @@ struct mei_device { struct mei_cl iamthif_cl; struct mei_cl_cb *iamthif_current_cb; long iamthif_open_count; - unsigned long iamthif_timer; u32 iamthif_stall_timer; enum iamthif_states iamthif_state; bool iamthif_canceled; -- cgit v1.2.3 From f23e2cc4bb1d8bbbe02dcd3539ed531a3f40277c Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:23 +0200 Subject: mei: constify struct file pointer The struct file file pointer is used as an opaque handle to for a connected client, for this part the pointer should be immutable and should be set to count. Reviewed-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 6 +++--- drivers/misc/mei/client.c | 12 +++++++----- drivers/misc/mei/client.h | 12 +++++++----- drivers/misc/mei/main.c | 4 ++-- drivers/misc/mei/mei_dev.h | 6 +++--- 5 files changed, 22 insertions(+), 18 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 5da1654bc209..16cceedeed97 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -93,7 +93,7 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) * Return: returned a list entry on success, NULL on failure. */ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, - struct file *file) + const struct file *file) { struct mei_cl_cb *cb; @@ -205,7 +205,7 @@ out: * * Return: 0 on success, <0 on failure. */ -static int mei_amthif_read_start(struct mei_cl *cl, struct file *file) +static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file) { struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; @@ -495,7 +495,7 @@ static bool mei_clear_list(struct mei_device *dev, * * Return: true if callback removed from the list, false otherwise */ -static bool mei_clear_lists(struct mei_device *dev, struct file *file) +static bool mei_clear_lists(struct mei_device *dev, const struct file *file) { bool removed = false; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 738f3d703323..4c23caee04ab 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -359,7 +359,7 @@ void mei_io_cb_free(struct mei_cl_cb *cb) * Return: mei_cl_cb pointer or NULL; */ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, - struct file *fp) + const struct file *fp) { struct mei_cl_cb *cb; @@ -455,7 +455,8 @@ int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length) * Return: cb on success and NULL on failure */ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, - enum mei_cb_file_ops type, struct file *fp) + enum mei_cb_file_ops type, + const struct file *fp) { struct mei_cl_cb *cb; @@ -1028,7 +1029,7 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, * Return: 0 on success, <0 on failure. */ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, - struct file *file) + const struct file *file) { struct mei_device *dev; struct mei_cl_cb *cb; @@ -1277,7 +1278,8 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, * * Return: 0 on such and error otherwise. */ -int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request) +int mei_cl_notify_request(struct mei_cl *cl, + const struct file *file, u8 request) { struct mei_device *dev; struct mei_cl_cb *cb; @@ -1443,7 +1445,7 @@ static bool mei_cl_is_read_fc_cb(struct mei_cl *cl) * * Return: 0 on success, <0 on failure. */ -int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp) +int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) { struct mei_device *dev; struct mei_cl_cb *cb; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 2e90a25f896e..fdc43279368f 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -83,7 +83,7 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl) * MEI IO Functions */ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, - struct file *fp); + const struct file *fp); void mei_io_cb_free(struct mei_cl_cb *priv_cb); int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length); @@ -116,7 +116,8 @@ struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp); void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp); struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, - enum mei_cb_file_ops type, struct file *fp); + enum mei_cb_file_ops type, + const struct file *fp); int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp); /* @@ -213,10 +214,10 @@ void mei_cl_set_disconnected(struct mei_cl *cl); int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, - struct file *file); + const struct file *file); int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); -int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp); +int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp); int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr, struct mei_cl_cb *cmpl_list); int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking); @@ -229,7 +230,8 @@ void mei_host_client_init(struct work_struct *work); u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop); enum mei_cb_file_ops mei_cl_notify_req2fop(u8 request); -int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request); +int mei_cl_notify_request(struct mei_cl *cl, + const struct file *file, u8 request); int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 5f2b63460d88..03741c456ee4 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -435,7 +435,7 @@ end: * * Return: 0 on success , <0 on error */ -static int mei_ioctl_client_notify_request(struct file *file, u32 request) +static int mei_ioctl_client_notify_request(const struct file *file, u32 request) { struct mei_cl *cl = file->private_data; @@ -450,7 +450,7 @@ static int mei_ioctl_client_notify_request(struct file *file, u32 request) * * Return: 0 on success , <0 on error */ -static int mei_ioctl_client_notify_get(struct file *file, u32 *notify_get) +static int mei_ioctl_client_notify_get(const struct file *file, u32 *notify_get) { struct mei_cl *cl = file->private_data; bool notify_ev; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 4d0e066703ef..947e0a70577a 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -190,7 +190,7 @@ struct mei_cl_cb { enum mei_cb_file_ops fop_type; struct mei_msg_data buf; size_t buf_idx; - struct file *file_object; + const struct file *file_object; int status; u32 internal:1; u32 completed:1; @@ -497,7 +497,7 @@ struct mei_device { struct mei_cl_cb amthif_cmd_list; /* driver managed amthif list for reading completed amthif cmd data */ struct mei_cl_cb amthif_rd_complete_list; - struct file *iamthif_file_object; + const struct file *iamthif_file_object; struct mei_cl iamthif_cl; struct mei_cl_cb *iamthif_current_cb; long iamthif_open_count; @@ -590,7 +590,7 @@ unsigned int mei_amthif_poll(struct mei_device *dev, int mei_amthif_release(struct mei_device *dev, struct file *file); struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, - struct file *file); + const struct file *file); int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_amthif_run_next_cmd(struct mei_device *dev); -- cgit v1.2.3 From 62e8e6ad60975df491d886efaa7fe0aa832947fb Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:24 +0200 Subject: mei: rename variable names 'file_object' to fp The driver uses three names file, fp, and file_object for struct file type. To improve code clarity and adjust to my taste rename file_object to more common and shorter fp. Reviewed-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 20 ++++++++++---------- drivers/misc/mei/client.c | 8 ++++---- drivers/misc/mei/interrupt.c | 2 +- drivers/misc/mei/mei_dev.h | 8 ++++---- 4 files changed, 19 insertions(+), 19 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 16cceedeed97..647edc68884f 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -98,7 +98,7 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, struct mei_cl_cb *cb; list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) - if (cb->file_object == file) + if (cb->fp == file) return cb; return NULL; } @@ -224,7 +224,7 @@ static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file) list_add_tail(&cb->list, &dev->ctrl_wr_list.list); dev->iamthif_state = MEI_IAMTHIF_READING; - dev->iamthif_file_object = cb->file_object; + dev->iamthif_fp = cb->fp; dev->iamthif_current_cb = cb; return 0; @@ -253,7 +253,7 @@ static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb) dev->iamthif_state = MEI_IAMTHIF_WRITING; dev->iamthif_current_cb = cb; - dev->iamthif_file_object = cb->file_object; + dev->iamthif_fp = cb->fp; dev->iamthif_canceled = false; ret = mei_cl_write(cl, cb, false); @@ -261,7 +261,7 @@ static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb) return ret; if (cb->completed) - cb->status = mei_amthif_read_start(cl, cb->file_object); + cb->status = mei_amthif_read_start(cl, cb->fp); return 0; } @@ -280,7 +280,7 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_file_object = NULL; + dev->iamthif_fp = NULL; dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); @@ -338,7 +338,7 @@ unsigned int mei_amthif_poll(struct mei_device *dev, poll_wait(file, &dev->iamthif_cl.wait, wait); if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE && - dev->iamthif_file_object == file) { + dev->iamthif_fp == file) { mask |= POLLIN | POLLRDNORM; mei_amthif_run_next_cmd(dev); @@ -368,7 +368,7 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, return ret; if (cb->completed) - cb->status = mei_amthif_read_start(cl, cb->file_object); + cb->status = mei_amthif_read_start(cl, cb->fp); return 0; } @@ -469,7 +469,7 @@ static bool mei_clear_list(struct mei_device *dev, /* list all list member */ list_for_each_entry_safe(cb, next, mei_cb_list, list) { /* check if list member associated with a file */ - if (file == cb->file_object) { + if (file == cb->fp) { /* check if cb equal to current iamthif cb */ if (dev->iamthif_current_cb == cb) { dev->iamthif_current_cb = NULL; @@ -518,7 +518,7 @@ static bool mei_clear_lists(struct mei_device *dev, const struct file *file) /* check if iamthif_current_cb not NULL */ if (dev->iamthif_current_cb && !removed) { /* check file and iamthif current cb association */ - if (dev->iamthif_current_cb->file_object == file) { + if (dev->iamthif_current_cb->fp == file) { /* remove cb */ mei_io_cb_free(dev->iamthif_current_cb); dev->iamthif_current_cb = NULL; @@ -541,7 +541,7 @@ int mei_amthif_release(struct mei_device *dev, struct file *file) if (dev->iamthif_open_count > 0) dev->iamthif_open_count--; - if (dev->iamthif_file_object == file && + if (dev->iamthif_fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) { dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 4c23caee04ab..c57ac25ce8db 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -368,7 +368,7 @@ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, return NULL; INIT_LIST_HEAD(&cb->list); - cb->file_object = fp; + cb->fp = fp; cb->cl = cl; cb->buf_idx = 0; cb->fop_type = type; @@ -486,7 +486,7 @@ struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp) struct mei_cl_cb *cb; list_for_each_entry(cb, &cl->rd_completed, list) - if (!fp || fp == cb->file_object) + if (!fp || fp == cb->fp) return cb; return NULL; @@ -504,12 +504,12 @@ void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp) struct mei_cl_cb *cb, *next; list_for_each_entry_safe(cb, next, &cl->rd_completed, list) - if (!fp || fp == cb->file_object) + if (!fp || fp == cb->fp) mei_io_cb_free(cb); list_for_each_entry_safe(cb, next, &cl->rd_pending, list) - if (!fp || fp == cb->file_object) + if (!fp || fp == cb->fp) mei_io_cb_free(cb); } diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 78978193d19b..37c8f0e14387 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -506,7 +506,7 @@ void mei_timer(struct work_struct *work) mei_io_cb_free(dev->iamthif_current_cb); dev->iamthif_current_cb = NULL; - dev->iamthif_file_object = NULL; + dev->iamthif_fp = NULL; mei_amthif_run_next_cmd(dev); } } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 947e0a70577a..3acbbb49d08c 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -179,7 +179,7 @@ struct mei_cl; * @fop_type: file operation type * @buf: buffer for data associated with the callback * @buf_idx: last read index - * @file_object: pointer to file structure + * @fp: pointer to file structure * @status: io status of the cb * @internal: communication between driver and FW flag * @completed: the transfer or reception has completed @@ -190,7 +190,7 @@ struct mei_cl_cb { enum mei_cb_file_ops fop_type; struct mei_msg_data buf; size_t buf_idx; - const struct file *file_object; + const struct file *fp; int status; u32 internal:1; u32 completed:1; @@ -407,7 +407,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * * @amthif_cmd_list : amthif list for cmd waiting * @amthif_rd_complete_list : amthif list for reading completed cmd data - * @iamthif_file_object : file for current amthif operation + * @iamthif_fp : file for current amthif operation * @iamthif_cl : amthif host client * @iamthif_current_cb : amthif current operation callback * @iamthif_open_count : number of opened amthif connections @@ -497,7 +497,7 @@ struct mei_device { struct mei_cl_cb amthif_cmd_list; /* driver managed amthif list for reading completed amthif cmd data */ struct mei_cl_cb amthif_rd_complete_list; - const struct file *iamthif_file_object; + const struct file *iamthif_fp; struct mei_cl iamthif_cl; struct mei_cl_cb *iamthif_current_cb; long iamthif_open_count; -- cgit v1.2.3 From 5cf8b2a9be0abec35f675bc719ca6eba9e559b1d Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:25 +0200 Subject: mei: amthif: allow only one request at a time A next amthif write can be executed only after the previous one has completed. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 647edc68884f..4383a9ad9208 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -315,6 +315,14 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) dev = cl->dev; list_add_tail(&cb->list, &dev->amthif_cmd_list.list); + + /* + * The previous request is still in processing, queue this one. + */ + if (dev->iamthif_state > MEI_IAMTHIF_IDLE && + dev->iamthif_state < MEI_IAMTHIF_READ_COMPLETE) + return 0; + return mei_amthif_run_next_cmd(dev); } -- cgit v1.2.3 From 9abd8b31292497530085156b41746e2a1cd9c934 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:26 +0200 Subject: mei: amthif: replace amthif_rd_complete_list with rd_completed Now when we have per client rd_completed list we can remove the amthif specific amthif_rd_complete_list. In addition in the function mei_amthif_read do not loop over the rd_completed list like the original code as the code path is unlocked. Reviewed-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 39 +++++++++++---------------------------- drivers/misc/mei/client.c | 1 - drivers/misc/mei/init.c | 1 - drivers/misc/mei/interrupt.c | 2 +- drivers/misc/mei/mei_dev.h | 7 +------ 5 files changed, 13 insertions(+), 37 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 4383a9ad9208..058dd6917805 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -84,26 +84,6 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) return ret; } -/** - * mei_amthif_find_read_list_entry - finds a amthilist entry for current file - * - * @dev: the device structure - * @file: pointer to file object - * - * Return: returned a list entry on success, NULL on failure. - */ -struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, - const struct file *file) -{ - struct mei_cl_cb *cb; - - list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) - if (cb->fp == file) - return cb; - return NULL; -} - - /** * mei_amthif_read - read data from AMTHIF client * @@ -123,12 +103,13 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, int mei_amthif_read(struct mei_device *dev, struct file *file, char __user *ubuf, size_t length, loff_t *offset) { + struct mei_cl *cl = file->private_data; struct mei_cl_cb *cb; int rets; int wait_ret; dev_dbg(dev->dev, "checking amthif data\n"); - cb = mei_amthif_find_read_list_entry(dev, file); + cb = mei_cl_read_cb(cl, file); /* Check for if we can block or not*/ if (cb == NULL && file->f_flags & O_NONBLOCK) @@ -141,7 +122,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, mutex_unlock(&dev->device_lock); wait_ret = wait_event_interruptible(dev->iamthif_cl.wait, - (cb = mei_amthif_find_read_list_entry(dev, file))); + !list_empty(&cl->rd_completed)); /* Locking again the Mutex */ mutex_lock(&dev->device_lock); @@ -149,7 +130,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, if (wait_ret) return -ERESTARTSYS; - dev_dbg(dev->dev, "woke up from sleep\n"); + cb = mei_cl_read_cb(cl, file); } if (cb->status) { @@ -420,11 +401,12 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, /** * mei_amthif_complete - complete amthif callback. * - * @dev: the device structure. + * @cl: host client * @cb: callback block. */ -void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) +void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) { + struct mei_device *dev = cl->dev; if (cb->fop_type == MEI_FOP_WRITE) { if (!cb->status) { @@ -436,7 +418,7 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) * in case of error enqueue the write cb to complete read list * so it can be propagated to the reader */ - list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); + list_add_tail(&cb->list, &cl->rd_completed); wake_up_interruptible(&dev->iamthif_cl.wait); return; } @@ -444,7 +426,7 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) if (!dev->iamthif_canceled) { dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE; dev->iamthif_stall_timer = 0; - list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); + list_add_tail(&cb->list, &cl->rd_completed); dev_dbg(dev->dev, "amthif read completed\n"); } else { mei_amthif_run_next_cmd(dev); @@ -506,10 +488,11 @@ static bool mei_clear_list(struct mei_device *dev, static bool mei_clear_lists(struct mei_device *dev, const struct file *file) { bool removed = false; + struct mei_cl *cl = &dev->iamthif_cl; /* remove callbacks associated with a file */ mei_clear_list(dev, file, &dev->amthif_cmd_list.list); - if (mei_clear_list(dev, file, &dev->amthif_rd_complete_list.list)) + if (mei_clear_list(dev, file, &cl->rd_completed)) removed = true; mei_clear_list(dev, file, &dev->ctrl_rd_list.list); diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index c57ac25ce8db..32991684563b 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -536,7 +536,6 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); - mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl); mei_cl_read_cb_flush(cl, fp); diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 46a4302e5524..a1d978a82806 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -402,7 +402,6 @@ void mei_device_init(struct mei_device *dev, INIT_LIST_HEAD(&dev->iamthif_cl.link); mei_io_list_init(&dev->amthif_cmd_list); - mei_io_list_init(&dev->amthif_rd_complete_list); bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); dev->open_handle_count = 0; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 37c8f0e14387..72806ef6efdd 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -48,7 +48,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) dev_dbg(dev->dev, "completing call back.\n"); if (cl == &dev->iamthif_cl) - mei_amthif_complete(dev, cb); + mei_amthif_complete(cl, cb); else mei_cl_complete(cl, cb); } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 3acbbb49d08c..81901967911d 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -406,7 +406,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @allow_fixed_address: allow user space to connect a fixed client * * @amthif_cmd_list : amthif list for cmd waiting - * @amthif_rd_complete_list : amthif list for reading completed cmd data * @iamthif_fp : file for current amthif operation * @iamthif_cl : amthif host client * @iamthif_current_cb : amthif current operation callback @@ -496,7 +495,6 @@ struct mei_device { /* amthif list for cmd waiting */ struct mei_cl_cb amthif_cmd_list; /* driver managed amthif list for reading completed amthif cmd data */ - struct mei_cl_cb amthif_rd_complete_list; const struct file *iamthif_fp; struct mei_cl iamthif_cl; struct mei_cl_cb *iamthif_current_cb; @@ -589,15 +587,12 @@ unsigned int mei_amthif_poll(struct mei_device *dev, int mei_amthif_release(struct mei_device *dev, struct file *file); -struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, - const struct file *file); - int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_amthif_run_next_cmd(struct mei_device *dev); int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); -void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb); +void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, struct mei_cl_cb *complete_list); -- cgit v1.2.3 From 77b007b1b50ef728be8252cbdf3f7e7e45b29bc5 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:27 +0200 Subject: mei: amthif: drop parameter validation from mei_amthif_write Remove duplicated parameter validation from mei_amthif_write functions, The parameter check is already performed by the caller function mei_write Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 058dd6917805..911b663a8fdb 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -285,15 +285,7 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) { - struct mei_device *dev; - - if (WARN_ON(!cl || !cl->dev)) - return -ENODEV; - - if (WARN_ON(!cb)) - return -EINVAL; - - dev = cl->dev; + struct mei_device *dev = cl->dev; list_add_tail(&cb->list, &dev->amthif_cmd_list.list); -- cgit v1.2.3 From 4bddf56fc93c6ad7a451f15a69e3d89d49db0747 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:28 +0200 Subject: mei: amthif: use rx_wait queue also for amthif client Switch using cl->rx_wait wait queue also for amthif, there is nothing special about amthif in that matter in Rx flow. The cl->wait is reserved for hbm flows and asynchronous events Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 911b663a8fdb..b98eac389782 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -121,8 +121,8 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, /* unlock the Mutex */ mutex_unlock(&dev->device_lock); - wait_ret = wait_event_interruptible(dev->iamthif_cl.wait, - !list_empty(&cl->rd_completed)); + wait_ret = wait_event_interruptible(cl->rx_wait, + !list_empty(&cl->rd_completed)); /* Locking again the Mutex */ mutex_lock(&dev->device_lock); @@ -316,7 +316,7 @@ unsigned int mei_amthif_poll(struct mei_device *dev, { unsigned int mask = 0; - poll_wait(file, &dev->iamthif_cl.wait, wait); + poll_wait(file, &dev->iamthif_cl.rx_wait, wait); if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE && dev->iamthif_fp == file) { @@ -411,7 +411,7 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) * so it can be propagated to the reader */ list_add_tail(&cb->list, &cl->rd_completed); - wake_up_interruptible(&dev->iamthif_cl.wait); + wake_up_interruptible(&cl->rx_wait); return; } @@ -425,7 +425,7 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) } dev_dbg(dev->dev, "completing amthif call back.\n"); - wake_up_interruptible(&dev->iamthif_cl.wait); + wake_up_interruptible(&cl->rx_wait); } /** -- cgit v1.2.3 From b74d883138911b110cf8f5b7b94790e16bb56795 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:29 +0200 Subject: mei: amthif: interrupt reader on link reset In case of link reset all waiting readers should be interrupted. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index b98eac389782..ca2efc3602df 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -122,7 +122,8 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, mutex_unlock(&dev->device_lock); wait_ret = wait_event_interruptible(cl->rx_wait, - !list_empty(&cl->rd_completed)); + !list_empty(&cl->rd_completed) || + !mei_cl_is_connected(cl)); /* Locking again the Mutex */ mutex_lock(&dev->device_lock); @@ -130,6 +131,11 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, if (wait_ret) return -ERESTARTSYS; + if (!mei_cl_is_connected(cl)) { + rets = -EBUSY; + goto out; + } + cb = mei_cl_read_cb(cl, file); } -- cgit v1.2.3 From a1f9ae2bd264e3aed95aacd0102bd22a0422b8d1 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:30 +0200 Subject: mei: bus: fix RX event scheduling In this particular case this more correct and safer to check if the RX event is set in the event mask rather than query waitqueue_active Since the check is already performed in the mei_cl_bus_rx_event function, it is just required to check for its return value. Second, since we don't have exclusive waiter wake_up_interruptible_all is not used correctly here. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 13 +++++++++---- drivers/misc/mei/client.c | 6 ++---- drivers/misc/mei/mei_dev.h | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 0b05aa938799..a40d8e24ecda 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -252,23 +252,28 @@ void mei_cl_bus_notify_event(struct mei_cl *cl) } /** - * mei_cl_bus_rx_event - schedule rx evenet + * mei_cl_bus_rx_event - schedule rx event * * @cl: host client + * + * Return: true if event was scheduled + * false if the client is not waiting for event */ -void mei_cl_bus_rx_event(struct mei_cl *cl) +bool mei_cl_bus_rx_event(struct mei_cl *cl) { struct mei_cl_device *cldev = cl->cldev; if (!cldev || !cldev->event_cb) - return; + return false; if (!(cldev->events_mask & BIT(MEI_CL_EVENT_RX))) - return; + return false; set_bit(MEI_CL_EVENT_RX, &cldev->events); schedule_work(&cldev->event_work); + + return true; } /** diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 32991684563b..574f7394fb2b 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1735,10 +1735,8 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) case MEI_FOP_READ: list_add_tail(&cb->list, &cl->rd_completed); - if (waitqueue_active(&cl->rx_wait)) - wake_up_interruptible_all(&cl->rx_wait); - else - mei_cl_bus_rx_event(cl); + if (!mei_cl_bus_rx_event(cl)) + wake_up_interruptible(&cl->rx_wait); break; case MEI_FOP_CONNECT: diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 81901967911d..be97b8f2a883 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -316,7 +316,7 @@ void mei_cl_bus_dev_fixup(struct mei_cl_device *dev); ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking); ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length); -void mei_cl_bus_rx_event(struct mei_cl *cl); +bool mei_cl_bus_rx_event(struct mei_cl *cl); void mei_cl_bus_notify_event(struct mei_cl *cl); void mei_cl_bus_remove_devices(struct mei_device *bus); int mei_cl_bus_init(void); -- cgit v1.2.3 From 850f8940a6a38563ff401073fd845414ab49c5ed Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Feb 2016 23:35:31 +0200 Subject: mei: bus: fix notification event delivery Call wake_up cl->ev_wait only in case there is no bus client registered to the event notification. Second, since we don't have exclusive waiter wake_up_interruptible_all is not used correctly here. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 13 +++++++++---- drivers/misc/mei/client.c | 4 ++-- drivers/misc/mei/mei_dev.h | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index a40d8e24ecda..990f4f65c35e 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -230,25 +230,30 @@ static void mei_cl_bus_event_work(struct work_struct *work) * mei_cl_bus_notify_event - schedule notify cb on bus client * * @cl: host client + * + * Return: true if event was scheduled + * false if the client is not waiting for event */ -void mei_cl_bus_notify_event(struct mei_cl *cl) +bool mei_cl_bus_notify_event(struct mei_cl *cl) { struct mei_cl_device *cldev = cl->cldev; if (!cldev || !cldev->event_cb) - return; + return false; if (!(cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF))) - return; + return false; if (!cl->notify_ev) - return; + return false; set_bit(MEI_CL_EVENT_NOTIF, &cldev->events); schedule_work(&cldev->event_work); cl->notify_ev = false; + + return true; } /** diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 574f7394fb2b..3fd070a698ce 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1363,12 +1363,12 @@ void mei_cl_notify(struct mei_cl *cl) cl_dbg(dev, cl, "notify event"); cl->notify_ev = true; - wake_up_interruptible_all(&cl->ev_wait); + if (!mei_cl_bus_notify_event(cl)) + wake_up_interruptible(&cl->ev_wait); if (cl->ev_async) kill_fasync(&cl->ev_async, SIGIO, POLL_PRI); - mei_cl_bus_notify_event(cl); } /** diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index be97b8f2a883..70c4da015401 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -317,7 +317,7 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking); ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length); bool mei_cl_bus_rx_event(struct mei_cl *cl); -void mei_cl_bus_notify_event(struct mei_cl *cl); +bool mei_cl_bus_notify_event(struct mei_cl *cl); void mei_cl_bus_remove_devices(struct mei_device *bus); int mei_cl_bus_init(void); void mei_cl_bus_exit(void); -- cgit v1.2.3 From 15c13dfcad883a1e76b714480fb27be96247fd82 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:32 +0200 Subject: mei: bus: check if the device is enabled before data transfer The bus data transfer interface was missing the check if the device is in enabled state, this may lead to stack corruption during link reset. Cc: #4.0 Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 990f4f65c35e..83b084558ee5 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -53,6 +53,11 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bus = cl->dev; mutex_lock(&bus->device_lock); + if (bus->dev_state != MEI_DEV_ENABLED) { + rets = -ENODEV; + goto out; + } + if (!mei_cl_is_connected(cl)) { rets = -ENODEV; goto out; @@ -109,6 +114,10 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) bus = cl->dev; mutex_lock(&bus->device_lock); + if (bus->dev_state != MEI_DEV_ENABLED) { + rets = -ENODEV; + goto out; + } cb = mei_cl_read_cb(cl, NULL); if (cb) -- cgit v1.2.3 From e8466b336adc20bacb597670e0a89e695626e000 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:33 +0200 Subject: mei: drop superfluous closing bracket from write traces Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/mei-trace.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h index 86e5068837c1..7d2d5d4a1624 100644 --- a/drivers/misc/mei/mei-trace.h +++ b/drivers/misc/mei/mei-trace.h @@ -60,7 +60,7 @@ TRACE_EVENT(mei_reg_write, __entry->offs = offs; __entry->val = val; ), - TP_printk("[%s] write %s[%#x] = %#x)", + TP_printk("[%s] write %s[%#x] = %#x", __get_str(dev), __entry->reg, __entry->offs, __entry->val) ); @@ -98,7 +98,7 @@ TRACE_EVENT(mei_pci_cfg_write, __entry->offs = offs; __entry->val = val; ), - TP_printk("[%s] pci cfg write %s[%#x] = %#x)", + TP_printk("[%s] pci cfg write %s[%#x] = %#x", __get_str(dev), __entry->reg, __entry->offs, __entry->val) ); -- cgit v1.2.3 From 0faf6a3bbae74ca08c2ecc09e5f6d350ad54c1ab Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:34 +0200 Subject: mei: wake blocked write on link reset In case of link reset all blocked writes should be interrupted. Note, that currently blocking write is used only through bus layer. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 3fd070a698ce..5ddc690752c2 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1691,7 +1691,8 @@ out: mutex_unlock(&dev->device_lock); rets = wait_event_interruptible(cl->tx_wait, - cl->writing_state == MEI_WRITE_COMPLETE); + cl->writing_state == MEI_WRITE_COMPLETE || + (!mei_cl_is_connected(cl))); mutex_lock(&dev->device_lock); /* wait_event_interruptible returns -ERESTARTSYS */ if (rets) { @@ -1699,6 +1700,10 @@ out: rets = -EINTR; goto err; } + if (cl->writing_state != MEI_WRITE_COMPLETE) { + rets = -EFAULT; + goto err; + } } rets = size; -- cgit v1.2.3 From a4307fe45aa9be03d5d7194b317a40b0d0558bee Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:35 +0200 Subject: mei: clean write queues and wake waiters on disconnect Clean write and write_waiting queues in disconnect. Requests in those queues are stale and processing will lead to fat warnings. In multi thread operations on disconnect and in FW disconnect case - write/read/event waiters should end wait and return error. Wake all waiters for disconnecting client to achieve that. Drop wake all and write queue clean on reset, as now we waking all waiters and cleaning write queues on disconnect. No need to do it twice. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 71 ++++++++++++++++++++--------------------------- drivers/misc/mei/client.h | 2 -- drivers/misc/mei/init.c | 5 ---- 3 files changed, 30 insertions(+), 48 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 5ddc690752c2..3a9c656f1425 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -719,6 +719,33 @@ bool mei_hbuf_acquire(struct mei_device *dev) return true; } +/** + * mei_cl_wake_all - wake up readers, writers and event waiters so + * they can be interrupted + * + * @cl: host client + */ +static void mei_cl_wake_all(struct mei_cl *cl) +{ + struct mei_device *dev = cl->dev; + + /* synchronized under device mutex */ + if (waitqueue_active(&cl->rx_wait)) { + cl_dbg(dev, cl, "Waking up reading client!\n"); + wake_up_interruptible(&cl->rx_wait); + } + /* synchronized under device mutex */ + if (waitqueue_active(&cl->tx_wait)) { + cl_dbg(dev, cl, "Waking up writing client!\n"); + wake_up_interruptible(&cl->tx_wait); + } + /* synchronized under device mutex */ + if (waitqueue_active(&cl->ev_wait)) { + cl_dbg(dev, cl, "Waking up waiting for event clients!\n"); + wake_up_interruptible(&cl->ev_wait); + } +} + /** * mei_cl_set_disconnected - set disconnected state and clear * associated states and resources @@ -734,8 +761,11 @@ void mei_cl_set_disconnected(struct mei_cl *cl) return; cl->state = MEI_FILE_DISCONNECTED; + mei_io_list_free(&dev->write_list, cl); + mei_io_list_free(&dev->write_waiting_list, cl); mei_io_list_flush(&dev->ctrl_rd_list, cl); mei_io_list_flush(&dev->ctrl_wr_list, cl); + mei_cl_wake_all(cl); cl->mei_flow_ctrl_creds = 0; cl->timer_count = 0; @@ -1770,44 +1800,3 @@ void mei_cl_all_disconnect(struct mei_device *dev) list_for_each_entry(cl, &dev->file_list, link) mei_cl_set_disconnected(cl); } - - -/** - * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted - * - * @dev: mei device - */ -void mei_cl_all_wakeup(struct mei_device *dev) -{ - struct mei_cl *cl; - - list_for_each_entry(cl, &dev->file_list, link) { - if (waitqueue_active(&cl->rx_wait)) { - cl_dbg(dev, cl, "Waking up reading client!\n"); - wake_up_interruptible(&cl->rx_wait); - } - if (waitqueue_active(&cl->tx_wait)) { - cl_dbg(dev, cl, "Waking up writing client!\n"); - wake_up_interruptible(&cl->tx_wait); - } - - /* synchronized under device mutex */ - if (waitqueue_active(&cl->ev_wait)) { - cl_dbg(dev, cl, "Waking up waiting for event clients!\n"); - wake_up_interruptible(&cl->ev_wait); - } - } -} - -/** - * mei_cl_all_write_clear - clear all pending writes - * - * @dev: mei device - */ -void mei_cl_all_write_clear(struct mei_device *dev) -{ - mei_io_list_free(&dev->write_list, NULL); - mei_io_list_free(&dev->write_waiting_list, NULL); -} - - diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index fdc43279368f..9925cf1a91ef 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -238,8 +238,6 @@ int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev); void mei_cl_notify(struct mei_cl *cl); void mei_cl_all_disconnect(struct mei_device *dev); -void mei_cl_all_wakeup(struct mei_device *dev); -void mei_cl_all_write_clear(struct mei_device *dev); #define MEI_CL_FMT "cl:host=%02d me=%02d " #define MEI_CL_PRM(cl) (cl)->host_client_id, mei_cl_me_id(cl) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index a1d978a82806..cfcb46da13f1 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -148,13 +148,8 @@ int mei_reset(struct mei_device *dev) state != MEI_DEV_POWER_UP) { /* remove all waiting requests */ - mei_cl_all_write_clear(dev); - mei_cl_all_disconnect(dev); - /* wake up all readers and writers so they can be interrupted */ - mei_cl_all_wakeup(dev); - /* remove entry if already in list */ dev_dbg(dev->dev, "remove iamthif from the file list.\n"); mei_cl_unlink(&dev->iamthif_cl); -- cgit v1.2.3 From 603c53e42adf5f2d29ffdd1ff1edda0c27e400df Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:36 +0200 Subject: mei: discard replies from unconnected fixed address clients A fixed address client in the FW doesn't have a notion of connection and can send message after the file associated with it was already closed. Silently discard such messages. Add inline helpers to detect whether a message is hbm or intended for a fixed address client Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/interrupt.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 72806ef6efdd..06b744a503a3 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -239,6 +239,16 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, return 0; } +static inline bool hdr_is_hbm(struct mei_msg_hdr *mei_hdr) +{ + return mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0; +} + +static inline bool hdr_is_fixed(struct mei_msg_hdr *mei_hdr) +{ + return mei_hdr->host_addr == 0 && mei_hdr->me_addr != 0; +} + /** * mei_irq_read_handler - bottom half read routine after ISR to * handle the read processing. @@ -280,7 +290,7 @@ int mei_irq_read_handler(struct mei_device *dev, } /* HBM message */ - if (mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0) { + if (hdr_is_hbm(mei_hdr)) { ret = mei_hbm_dispatch(dev, mei_hdr); if (ret) { dev_dbg(dev->dev, "mei_hbm_dispatch failed ret = %d\n", @@ -300,6 +310,14 @@ int mei_irq_read_handler(struct mei_device *dev, /* if no recipient cl was found we assume corrupted header */ if (&cl->link == &dev->file_list) { + /* A message for not connected fixed address clients + * should be silently discarded + */ + if (hdr_is_fixed(mei_hdr)) { + mei_irq_discard_msg(dev, mei_hdr); + ret = 0; + goto reset_slots; + } dev_err(dev->dev, "no destination client found 0x%08X\n", dev->rd_msg_hdr); ret = -EBADMSG; -- cgit v1.2.3 From 06ee536bcb15ca12868289467762130fa0a426f3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:37 +0200 Subject: mei: fill file pointer in read cb for fixed address client The read callback created from a flow control request for a fixed address client have NULL in the file pointer. Fill the file pointer using a data from a write callback. This allows us to drop workaround introduced in: commit eeabfcf5a92a ("mei: connection to fixed address clients from user-space") Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 11 +- drivers/misc/mei/main.c | 5 - drivers/misc/mei/wd.c | 391 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+), 10 deletions(-) create mode 100644 drivers/misc/mei/wd.c (limited to 'drivers/misc') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 3a9c656f1425..a27ae2deecb4 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1173,11 +1173,12 @@ err: /** * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. * - * @cl: private data of the file object + * @cl: host client + * @fp: the file pointer associated with the pointer * * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. */ -static int mei_cl_flow_ctrl_creds(struct mei_cl *cl) +static int mei_cl_flow_ctrl_creds(struct mei_cl *cl, const struct file *fp) { int rets; @@ -1188,7 +1189,7 @@ static int mei_cl_flow_ctrl_creds(struct mei_cl *cl) return 1; if (mei_cl_is_fixed_address(cl)) { - rets = mei_cl_read_start(cl, mei_cl_mtu(cl), NULL); + rets = mei_cl_read_start(cl, mei_cl_mtu(cl), fp); if (rets && rets != -EBUSY) return rets; return 1; @@ -1568,7 +1569,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, first_chunk = cb->buf_idx == 0; - rets = first_chunk ? mei_cl_flow_ctrl_creds(cl) : 1; + rets = first_chunk ? mei_cl_flow_ctrl_creds(cl, cb->fp) : 1; if (rets < 0) return rets; @@ -1674,7 +1675,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) mei_hdr.msg_complete = 0; mei_hdr.internal = cb->internal; - rets = mei_cl_flow_ctrl_creds(cl); + rets = mei_cl_flow_ctrl_creds(cl, cb->fp); if (rets < 0) goto err; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 03741c456ee4..f1fdd65e9678 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -209,11 +209,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, cb = mei_cl_read_cb(cl, file); if (!cb) { - if (mei_cl_is_fixed_address(cl) && dev->allow_fixed_address) { - cb = mei_cl_read_cb(cl, NULL); - if (cb) - goto copy_buffer; - } rets = 0; goto out; } diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c new file mode 100644 index 000000000000..7d9b4ee42f65 --- /dev/null +++ b/drivers/misc/mei/wd.c @@ -0,0 +1,391 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2012, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#include +#include +#include +#include +#include +#include + +#include + +#include "mei_dev.h" +#include "hbm.h" +#include "client.h" + +static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 }; +static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 }; + +/* + * AMT Watchdog Device + */ +#define INTEL_AMT_WATCHDOG_ID "INTCAMT" + +/* UUIDs for AMT F/W clients */ +const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, + 0x9D, 0xA9, 0x15, 0x14, 0xCB, + 0x32, 0xAB); + +static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) +{ + dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout); + memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE); + memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16)); +} + +/** + * mei_wd_host_init - connect to the watchdog client + * + * @dev: the device structure + * @me_cl: me client + * + * Return: -ENOTTY if wd client cannot be found + * -EIO if write has failed + * 0 on success + */ +int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl) +{ + struct mei_cl *cl = &dev->wd_cl; + int ret; + + mei_cl_init(cl, dev); + + dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; + dev->wd_state = MEI_WD_IDLE; + + ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); + if (ret < 0) { + dev_info(dev->dev, "wd: failed link client\n"); + return ret; + } + + ret = mei_cl_connect(cl, me_cl, NULL); + if (ret) { + dev_err(dev->dev, "wd: failed to connect = %d\n", ret); + mei_cl_unlink(cl); + return ret; + } + + ret = mei_watchdog_register(dev); + if (ret) { + mei_cl_disconnect(cl); + mei_cl_unlink(cl); + } + return ret; +} + +/** + * mei_wd_send - sends watch dog message to fw. + * + * @dev: the device structure + * + * Return: 0 if success, + * -EIO when message send fails + * -EINVAL when invalid message is to be sent + * -ENODEV on flow control failure + */ +int mei_wd_send(struct mei_device *dev) +{ + struct mei_cl *cl = &dev->wd_cl; + struct mei_msg_hdr hdr; + int ret; + + hdr.host_addr = cl->host_client_id; + hdr.me_addr = mei_cl_me_id(cl); + hdr.msg_complete = 1; + hdr.reserved = 0; + hdr.internal = 0; + + if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE)) + hdr.length = MEI_WD_START_MSG_SIZE; + else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE)) + hdr.length = MEI_WD_STOP_MSG_SIZE; + else { + dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n"); + return -EINVAL; + } + + ret = mei_write_message(dev, &hdr, dev->wd_data); + if (ret) { + dev_err(dev->dev, "wd: write message failed\n"); + return ret; + } + + ret = mei_cl_flow_ctrl_reduce(cl); + if (ret) { + dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n"); + return ret; + } + + return 0; +} + +/** + * mei_wd_stop - sends watchdog stop message to fw. + * + * @dev: the device structure + * + * Return: 0 if success + * on error: + * -EIO when message send fails + * -EINVAL when invalid message is to be sent + * -ETIME on message timeout + */ +int mei_wd_stop(struct mei_device *dev) +{ + struct mei_cl *cl = &dev->wd_cl; + int ret; + + if (!mei_cl_is_connected(cl) || + dev->wd_state != MEI_WD_RUNNING) + return 0; + + memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE); + + dev->wd_state = MEI_WD_STOPPING; + + ret = mei_cl_flow_ctrl_creds(cl, NULL); + if (ret < 0) + goto err; + + if (ret && mei_hbuf_acquire(dev)) { + ret = mei_wd_send(dev); + if (ret) + goto err; + dev->wd_pending = false; + } else { + dev->wd_pending = true; + } + + mutex_unlock(&dev->device_lock); + + ret = wait_event_timeout(dev->wait_stop_wd, + dev->wd_state == MEI_WD_IDLE, + msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); + mutex_lock(&dev->device_lock); + if (dev->wd_state != MEI_WD_IDLE) { + /* timeout */ + ret = -ETIME; + dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret); + goto err; + } + dev_dbg(dev->dev, "wd: stop completed after %u msec\n", + MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret)); + return 0; +err: + return ret; +} + +/** + * mei_wd_ops_start - wd start command from the watchdog core. + * + * @wd_dev: watchdog device struct + * + * Return: 0 if success, negative errno code for failure + */ +static int mei_wd_ops_start(struct watchdog_device *wd_dev) +{ + struct mei_device *dev; + struct mei_cl *cl; + int err = -ENODEV; + + dev = watchdog_get_drvdata(wd_dev); + if (!dev) + return -ENODEV; + + cl = &dev->wd_cl; + + mutex_lock(&dev->device_lock); + + if (dev->dev_state != MEI_DEV_ENABLED) { + dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", + mei_dev_state_str(dev->dev_state)); + goto end_unlock; + } + + if (!mei_cl_is_connected(cl)) { + cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n"); + goto end_unlock; + } + + mei_wd_set_start_timeout(dev, dev->wd_timeout); + + err = 0; +end_unlock: + mutex_unlock(&dev->device_lock); + return err; +} + +/** + * mei_wd_ops_stop - wd stop command from the watchdog core. + * + * @wd_dev: watchdog device struct + * + * Return: 0 if success, negative errno code for failure + */ +static int mei_wd_ops_stop(struct watchdog_device *wd_dev) +{ + struct mei_device *dev; + + dev = watchdog_get_drvdata(wd_dev); + if (!dev) + return -ENODEV; + + mutex_lock(&dev->device_lock); + mei_wd_stop(dev); + mutex_unlock(&dev->device_lock); + + return 0; +} + +/** + * mei_wd_ops_ping - wd ping command from the watchdog core. + * + * @wd_dev: watchdog device struct + * + * Return: 0 if success, negative errno code for failure + */ +static int mei_wd_ops_ping(struct watchdog_device *wd_dev) +{ + struct mei_device *dev; + struct mei_cl *cl; + int ret; + + dev = watchdog_get_drvdata(wd_dev); + if (!dev) + return -ENODEV; + + cl = &dev->wd_cl; + + mutex_lock(&dev->device_lock); + + if (!mei_cl_is_connected(cl)) { + cl_err(dev, cl, "wd: not connected.\n"); + ret = -ENODEV; + goto end; + } + + dev->wd_state = MEI_WD_RUNNING; + + ret = mei_cl_flow_ctrl_creds(cl, NULL); + if (ret < 0) + goto end; + + /* Check if we can send the ping to HW*/ + if (ret && mei_hbuf_acquire(dev)) { + dev_dbg(dev->dev, "wd: sending ping\n"); + + ret = mei_wd_send(dev); + if (ret) + goto end; + dev->wd_pending = false; + } else { + dev->wd_pending = true; + } + +end: + mutex_unlock(&dev->device_lock); + return ret; +} + +/** + * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core. + * + * @wd_dev: watchdog device struct + * @timeout: timeout value to set + * + * Return: 0 if success, negative errno code for failure + */ +static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, + unsigned int timeout) +{ + struct mei_device *dev; + + dev = watchdog_get_drvdata(wd_dev); + if (!dev) + return -ENODEV; + + /* Check Timeout value */ + if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT) + return -EINVAL; + + mutex_lock(&dev->device_lock); + + dev->wd_timeout = timeout; + wd_dev->timeout = timeout; + mei_wd_set_start_timeout(dev, dev->wd_timeout); + + mutex_unlock(&dev->device_lock); + + return 0; +} + +/* + * Watchdog Device structs + */ +static const struct watchdog_ops wd_ops = { + .owner = THIS_MODULE, + .start = mei_wd_ops_start, + .stop = mei_wd_ops_stop, + .ping = mei_wd_ops_ping, + .set_timeout = mei_wd_ops_set_timeout, +}; +static const struct watchdog_info wd_info = { + .identity = INTEL_AMT_WATCHDOG_ID, + .options = WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | + WDIOF_ALARMONLY, +}; + +static struct watchdog_device amt_wd_dev = { + .info = &wd_info, + .ops = &wd_ops, + .timeout = MEI_WD_DEFAULT_TIMEOUT, + .min_timeout = MEI_WD_MIN_TIMEOUT, + .max_timeout = MEI_WD_MAX_TIMEOUT, +}; + + +int mei_watchdog_register(struct mei_device *dev) +{ + + int ret; + + amt_wd_dev.parent = dev->dev; + /* unlock to perserve correct locking order */ + mutex_unlock(&dev->device_lock); + ret = watchdog_register_device(&amt_wd_dev); + mutex_lock(&dev->device_lock); + if (ret) { + dev_err(dev->dev, "wd: unable to register watchdog device = %d.\n", + ret); + return ret; + } + + dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n"); + watchdog_set_drvdata(&amt_wd_dev, dev); + return 0; +} + +void mei_watchdog_unregister(struct mei_device *dev) +{ + if (watchdog_get_drvdata(&amt_wd_dev) == NULL) + return; + + watchdog_set_drvdata(&amt_wd_dev, NULL); + watchdog_unregister_device(&amt_wd_dev); +} + -- cgit v1.2.3 From f4e06246183f187d1327fdba18a797eb091a7d03 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:38 +0200 Subject: mei: fixed address clients for the new platforms Enable by default connection to fixed address clients from user-space for skylake and newer platform. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/debugfs.c | 31 +++++++++++++++++++++++++++++-- drivers/misc/mei/hbm.c | 4 ++++ drivers/misc/mei/hw.h | 6 ++++++ drivers/misc/mei/main.c | 18 ++++++++++++++---- drivers/misc/mei/mei_dev.h | 4 ++++ 5 files changed, 57 insertions(+), 6 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index 6bdd75424fe8..8ad406315741 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -176,6 +176,8 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, dev->hbm_f_dot_supported); pos += scnprintf(buf + pos, bufsz - pos, "\tEV: %01d\n", dev->hbm_f_ev_supported); + pos += scnprintf(buf + pos, bufsz - pos, "\tFA: %01d\n", + dev->hbm_f_fa_supported); } pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", @@ -191,6 +193,30 @@ static const struct file_operations mei_dbgfs_fops_devstate = { .llseek = generic_file_llseek, }; +static ssize_t mei_dbgfs_write_allow_fa(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mei_device *dev; + int ret; + + dev = container_of(file->private_data, + struct mei_device, allow_fixed_address); + + ret = debugfs_write_file_bool(file, user_buf, count, ppos); + if (ret < 0) + return ret; + dev->override_fixed_address = true; + return ret; +} + +static const struct file_operations mei_dbgfs_fops_allow_fa = { + .open = simple_open, + .read = debugfs_read_file_bool, + .write = mei_dbgfs_write_allow_fa, + .llseek = generic_file_llseek, +}; + /** * mei_dbgfs_deregister - Remove the debugfs files and directories * @@ -240,8 +266,9 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name) dev_err(dev->dev, "devstate: registration failed\n"); goto err; } - f = debugfs_create_bool("allow_fixed_address", S_IRUSR | S_IWUSR, dir, - &dev->allow_fixed_address); + f = debugfs_create_file("allow_fixed_address", S_IRUSR | S_IWUSR, dir, + &dev->allow_fixed_address, + &mei_dbgfs_fops_allow_fa); if (!f) { dev_err(dev->dev, "allow_fixed_address: registration failed\n"); goto err; diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index e7b7aad0999b..3915b8e8d0f1 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -979,6 +979,10 @@ static void mei_hbm_config_features(struct mei_device *dev) /* Notification Event Support */ if (dev->version.major_version >= HBM_MAJOR_VERSION_EV) dev->hbm_f_ev_supported = 1; + + /* Fixed Address Client Support */ + if (dev->version.major_version >= HBM_MAJOR_VERSION_FA) + dev->hbm_f_fa_supported = 1; } /** diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 4c5d6cfd79b4..459ad596df66 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -64,6 +64,12 @@ #define HBM_MINOR_VERSION_EV 0 #define HBM_MAJOR_VERSION_EV 2 +/* + * MEI version with fixed address client support + */ +#define HBM_MINOR_VERSION_FA 0 +#define HBM_MAJOR_VERSION_FA 2 + /* Host bus message command opcode */ #define MEI_HBM_CMD_OP_MSK 0x7f /* Host bus message command RESPONSE */ diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index f1fdd65e9678..58fa2c83ee39 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -369,12 +369,22 @@ static int mei_ioctl_connect_client(struct file *file, /* find ME client we're trying to connect to */ me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid); - if (!me_cl || - (me_cl->props.fixed_address && !dev->allow_fixed_address)) { + if (!me_cl) { dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n", &data->in_client_uuid); - mei_me_cl_put(me_cl); - return -ENOTTY; + rets = -ENOTTY; + goto end; + } + + if (me_cl->props.fixed_address) { + bool forbidden = dev->override_fixed_address ? + !dev->allow_fixed_address : !dev->hbm_f_fa_supported; + if (forbidden) { + dev_dbg(dev->dev, "Connection forbidden to FW Client UUID = %pUl\n", + &data->in_client_uuid); + rets = -ENOTTY; + goto end; + } } dev_dbg(dev->dev, "Connect to FW Client ID = %d\n", diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 70c4da015401..6d97f3335e22 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -396,6 +396,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @hbm_f_dc_supported : hbm feature dynamic clients * @hbm_f_dot_supported : hbm feature disconnect on timeout * @hbm_f_ev_supported : hbm feature event notification + * @hbm_f_fa_supported : hbm feature fixed address client * * @me_clients_rwsem: rw lock over me_clients list * @me_clients : list of FW clients @@ -404,6 +405,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @me_client_index : last FW client index in enumeration * * @allow_fixed_address: allow user space to connect a fixed client + * @override_fixed_address: force allow fixed address behavior * * @amthif_cmd_list : amthif list for cmd waiting * @iamthif_fp : file for current amthif operation @@ -483,6 +485,7 @@ struct mei_device { unsigned int hbm_f_dc_supported:1; unsigned int hbm_f_dot_supported:1; unsigned int hbm_f_ev_supported:1; + unsigned int hbm_f_fa_supported:1; struct rw_semaphore me_clients_rwsem; struct list_head me_clients; @@ -491,6 +494,7 @@ struct mei_device { unsigned long me_client_index; bool allow_fixed_address; + bool override_fixed_address; /* amthif list for cmd waiting */ struct mei_cl_cb amthif_cmd_list; -- cgit v1.2.3 From 6938c1923f8ac6eb6d1c99b924d3e377201143ed Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:39 +0200 Subject: mei: hbm: warn about fw-initiated disconnect The FW can initiate client disconnection only because an error condition, hence it make sense to bump the debug message to the warning level to have an entery in the log. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.h | 3 +++ drivers/misc/mei/hbm.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 9925cf1a91ef..be6929620d61 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -245,6 +245,9 @@ void mei_cl_all_disconnect(struct mei_device *dev); #define cl_dbg(dev, cl, format, arg...) \ dev_dbg((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) +#define cl_warn(dev, cl, format, arg...) \ + dev_warn((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) + #define cl_err(dev, cl, format, arg...) \ dev_err((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 3915b8e8d0f1..a2f32b0eb649 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -866,7 +866,7 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev, cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req); if (cl) { - cl_dbg(dev, cl, "fw disconnect request received\n"); + cl_warn(dev, cl, "fw disconnect request received\n"); cl->state = MEI_FILE_DISCONNECTING; cl->timer_count = 0; -- cgit v1.2.3 From 7851e008703e2f7073802e560293213e93dcdde6 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:40 +0200 Subject: mei: drop reserved host client ids The reserved host clients can be obsoleted now, a portion of the platforms is shipped without iAMT enabled, where the reservation is not relevant and for platforms with iAMT dynamic allocation is sufficient. Dropping reserved ids makes enumeration more flexible and generic Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 2 +- drivers/misc/mei/bus-fixup.c | 2 +- drivers/misc/mei/bus.c | 2 +- drivers/misc/mei/client.c | 15 +++++---------- drivers/misc/mei/client.h | 4 ++-- drivers/misc/mei/main.c | 2 +- drivers/misc/mei/mei_dev.h | 8 -------- 7 files changed, 11 insertions(+), 24 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index ca2efc3602df..de194ef573ee 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -71,7 +71,7 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) mei_cl_init(cl, dev); - ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID); + ret = mei_cl_link(cl); if (ret < 0) { dev_err(dev->dev, "amthif: failed cl_link %d\n", ret); return ret; diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index b87323f4bb14..e9e6ea3ab73c 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -239,7 +239,7 @@ static void mei_nfc(struct mei_cl_device *cldev) mutex_lock(&bus->device_lock); /* we need to connect to INFO GUID */ - cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY); + cl = mei_cl_alloc_linked(bus); if (IS_ERR(cl)) { ret = PTR_ERR(cl); cl = NULL; diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 83b084558ee5..bc18e5519da6 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -417,7 +417,7 @@ int mei_cldev_enable(struct mei_cl_device *cldev) if (!cl) { mutex_lock(&bus->device_lock); - cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY); + cl = mei_cl_alloc_linked(bus); mutex_unlock(&bus->device_lock); if (IS_ERR(cl)) return PTR_ERR(cl); diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index a27ae2deecb4..2890669b81f9 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -587,27 +587,23 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev) * mei_cl_link - allocate host id in the host map * * @cl: host client - * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one * * Return: 0 on success * -EINVAL on incorrect values * -EMFILE if open count exceeded. */ -int mei_cl_link(struct mei_cl *cl, int id) +int mei_cl_link(struct mei_cl *cl) { struct mei_device *dev; long open_handle_count; + int id; if (WARN_ON(!cl || !cl->dev)) return -EINVAL; dev = cl->dev; - /* If Id is not assigned get one*/ - if (id == MEI_HOST_CLIENT_ID_ANY) - id = find_first_zero_bit(dev->host_clients_map, - MEI_CLIENTS_MAX); - + id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX); if (id >= MEI_CLIENTS_MAX) { dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX); return -EMFILE; @@ -1143,11 +1139,10 @@ nortpm: * mei_cl_alloc_linked - allocate and link host client * * @dev: the device structure - * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one * * Return: cl on success ERR_PTR on failure */ -struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id) +struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev) { struct mei_cl *cl; int ret; @@ -1158,7 +1153,7 @@ struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id) goto err; } - ret = mei_cl_link(cl, id); + ret = mei_cl_link(cl); if (ret) goto err; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index be6929620d61..a912ea686d97 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -107,10 +107,10 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev); void mei_cl_init(struct mei_cl *cl, struct mei_device *dev); -int mei_cl_link(struct mei_cl *cl, int id); +int mei_cl_link(struct mei_cl *cl); int mei_cl_unlink(struct mei_cl *cl); -struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id); +struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev); struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 58fa2c83ee39..17970163eacc 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -65,7 +65,7 @@ static int mei_open(struct inode *inode, struct file *file) goto err_unlock; } - cl = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY); + cl = mei_cl_alloc_linked(dev); if (IS_ERR(cl)) { err = PTR_ERR(cl); goto err_unlock; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 6d97f3335e22..320ddae5ee1e 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -53,14 +53,6 @@ extern const uuid_le mei_amthif_guid; */ #define MEI_MAX_OPEN_HANDLE_COUNT (MEI_CLIENTS_MAX - 1) -/* - * Internal Clients Number - */ -#define MEI_HOST_CLIENT_ID_ANY (-1) -#define MEI_HBM_HOST_CLIENT_ID 0 /* not used, just for documentation */ -#define MEI_IAMTHIF_HOST_CLIENT_ID 2 - - /* File state */ enum file_state { MEI_FILE_INITIALIZING = 0, -- cgit v1.2.3 From a816a00ece63d16ade7e9c0ca8b5a7e4c5ea2453 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:41 +0200 Subject: mei: bus: run rescan on me_clients list change Since clients can be now added and removed during runtime we need to run bus rescan whenever me_clients list is modified. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 8 ++++++++ drivers/misc/mei/hbm.c | 8 +++++++- drivers/misc/mei/init.c | 2 ++ drivers/misc/mei/mei_dev.h | 3 +++ 4 files changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index bc18e5519da6..951d32265040 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -977,6 +977,14 @@ void mei_cl_bus_rescan(struct mei_device *bus) dev_dbg(bus->dev, "rescan end"); } +void mei_cl_bus_rescan_work(struct work_struct *work) +{ + struct mei_device *bus = + container_of(work, struct mei_device, bus_rescan_work); + + mei_cl_bus_rescan(bus); +} + int __mei_cldev_driver_register(struct mei_cl_driver *cldrv, struct module *owner) { diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index a2f32b0eb649..0c9310ad6136 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -401,6 +401,9 @@ static int mei_hbm_fw_add_cl_req(struct mei_device *dev, if (ret) status = !MEI_HBMS_SUCCESS; + if (dev->dev_state == MEI_DEV_ENABLED) + schedule_work(&dev->bus_rescan_work); + return mei_hbm_add_cl_resp(dev, req->me_addr, status); } @@ -789,8 +792,11 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, struct mei_cl *cl, cl->state = MEI_FILE_CONNECTED; else { cl->state = MEI_FILE_DISCONNECT_REPLY; - if (rs->status == MEI_CL_CONN_NOT_FOUND) + if (rs->status == MEI_CL_CONN_NOT_FOUND) { mei_me_cl_del(dev, cl->me_cl); + if (dev->dev_state == MEI_DEV_ENABLED) + schedule_work(&dev->bus_rescan_work); + } } cl->status = mei_cl_conn_status_to_errno(rs->status); } diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index cfcb46da13f1..52fde2b498ef 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -93,6 +93,7 @@ void mei_cancel_work(struct mei_device *dev) { cancel_work_sync(&dev->init_work); cancel_work_sync(&dev->reset_work); + cancel_work_sync(&dev->bus_rescan_work); cancel_delayed_work(&dev->timer_work); } @@ -394,6 +395,7 @@ void mei_device_init(struct mei_device *dev, INIT_DELAYED_WORK(&dev->timer_work, mei_timer); INIT_WORK(&dev->init_work, mei_host_client_init); INIT_WORK(&dev->reset_work, mei_reset_work); + INIT_WORK(&dev->bus_rescan_work, mei_cl_bus_rescan_work); INIT_LIST_HEAD(&dev->iamthif_cl.link); mei_io_list_init(&dev->amthif_cmd_list); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 320ddae5ee1e..c31adb8a0cff 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -304,6 +304,7 @@ struct mei_hw_ops { /* MEI bus API*/ void mei_cl_bus_rescan(struct mei_device *bus); +void mei_cl_bus_rescan_work(struct work_struct *work); void mei_cl_bus_dev_fixup(struct mei_cl_device *dev); ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking); @@ -410,6 +411,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * * @init_work : work item for the device init * @reset_work : work item for the device reset + * @bus_rescan_work : work item for the bus rescan * * @device_list : mei client bus list * @cl_bus_lock : client bus list lock @@ -501,6 +503,7 @@ struct mei_device { struct work_struct init_work; struct work_struct reset_work; + struct work_struct bus_rescan_work; /* List of bus devices */ struct list_head device_list; -- cgit v1.2.3 From 27f476ea98ed495839662db4e3a76357bbeb1bb3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:42 +0200 Subject: mei: hbm: send immediate reply flag in enum request Signal the FW that it can send an HBM enumeration answer immediately, without waiting for FW initialization completion, meaning before all the FW clients are ready and registered. Organize enumeration response options to enum as a byproduct. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/debugfs.c | 2 ++ drivers/misc/mei/hbm.c | 8 +++++++- drivers/misc/mei/hw.h | 25 +++++++++++++++++++++---- drivers/misc/mei/mei_dev.h | 2 ++ 4 files changed, 32 insertions(+), 5 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index 8ad406315741..c6c051b52f55 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -172,6 +172,8 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, dev->hbm_f_pg_supported); pos += scnprintf(buf + pos, bufsz - pos, "\tDC: %01d\n", dev->hbm_f_dc_supported); + pos += scnprintf(buf + pos, bufsz - pos, "\tIE: %01d\n", + dev->hbm_f_ie_supported); pos += scnprintf(buf + pos, bufsz - pos, "\tDOT: %01d\n", dev->hbm_f_dot_supported); pos += scnprintf(buf + pos, bufsz - pos, "\tEV: %01d\n", diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 0c9310ad6136..d2798d5b0a9d 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -301,7 +301,10 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev) enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data; memset(enum_req, 0, len); enum_req->hbm_cmd = HOST_ENUM_REQ_CMD; - enum_req->allow_add = dev->hbm_f_dc_supported; + enum_req->flags |= dev->hbm_f_dc_supported ? + MEI_HBM_ENUM_F_ALLOW_ADD : 0; + enum_req->flags |= dev->hbm_f_ie_supported ? + MEI_HBM_ENUM_F_IMMEDIATE_ENUM : 0; ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); if (ret) { @@ -978,6 +981,9 @@ static void mei_hbm_config_features(struct mei_device *dev) if (dev->version.major_version >= HBM_MAJOR_VERSION_DC) dev->hbm_f_dc_supported = 1; + if (dev->version.major_version >= HBM_MAJOR_VERSION_IE) + dev->hbm_f_ie_supported = 1; + /* disconnect on connect timeout instead of link reset */ if (dev->version.major_version >= HBM_MAJOR_VERSION_DOT) dev->hbm_f_dot_supported = 1; diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 459ad596df66..9daf3f9aed25 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -52,6 +52,12 @@ #define HBM_MINOR_VERSION_DC 0 #define HBM_MAJOR_VERSION_DC 2 +/* + * MEI version with immediate reply to enum request support + */ +#define HBM_MINOR_VERSION_IE 0 +#define HBM_MAJOR_VERSION_IE 2 + /* * MEI version with disconnect on connection timeout support */ @@ -246,15 +252,26 @@ struct hbm_me_stop_request { } __packed; /** - * struct hbm_host_enum_request - enumeration request from host to fw + * enum hbm_host_enum_flags - enumeration request flags (HBM version >= 2.0) * - * @hbm_cmd: bus message command header - * @allow_add: allow dynamic clients add HBM version >= 2.0 + * @MEI_HBM_ENUM_F_ALLOW_ADD: allow dynamic clients add + * @MEI_HBM_ENUM_F_IMMEDIATE_ENUM: allow FW to send answer immediately + */ +enum hbm_host_enum_flags { + MEI_HBM_ENUM_F_ALLOW_ADD = BIT(0), + MEI_HBM_ENUM_F_IMMEDIATE_ENUM = BIT(1), +}; + +/** + * struct hbm_host_enum_request - enumeration request from host to fw + * + * @hbm_cmd : bus message command header + * @flags : request flags * @reserved: reserved */ struct hbm_host_enum_request { u8 hbm_cmd; - u8 allow_add; + u8 flags; u8 reserved[2]; } __packed; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index c31adb8a0cff..2b9160e506d7 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -390,6 +390,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @hbm_f_dot_supported : hbm feature disconnect on timeout * @hbm_f_ev_supported : hbm feature event notification * @hbm_f_fa_supported : hbm feature fixed address client + * @hbm_f_ie_supported : hbm feature immediate reply to enum request * * @me_clients_rwsem: rw lock over me_clients list * @me_clients : list of FW clients @@ -480,6 +481,7 @@ struct mei_device { unsigned int hbm_f_dot_supported:1; unsigned int hbm_f_ev_supported:1; unsigned int hbm_f_fa_supported:1; + unsigned int hbm_f_ie_supported:1; struct rw_semaphore me_clients_rwsem; struct list_head me_clients; -- cgit v1.2.3 From 025fb792bac33632c19fe12265ba1f6108921300 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 7 Feb 2016 23:35:43 +0200 Subject: mei: split amthif client init from end of clients enumeration The amthif FW client can appear after the end of client enumeration. Amthif host client initialization is done now at FW client discovery time. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 5 +++-- drivers/misc/mei/bus.c | 8 ++++++++ drivers/misc/mei/client.c | 17 ++--------------- drivers/misc/mei/client.h | 2 +- drivers/misc/mei/hbm.c | 2 +- drivers/misc/mei/init.c | 2 -- drivers/misc/mei/mei_dev.h | 2 -- 7 files changed, 15 insertions(+), 23 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index de194ef573ee..04525ada9eda 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -67,6 +67,9 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) struct mei_cl *cl = &dev->iamthif_cl; int ret; + if (mei_cl_is_connected(cl)) + return 0; + dev->iamthif_state = MEI_IAMTHIF_IDLE; mei_cl_init(cl, dev); @@ -79,8 +82,6 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) ret = mei_cl_connect(cl, me_cl, NULL); - dev->iamthif_state = MEI_IAMTHIF_IDLE; - return ret; } diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 951d32265040..f4cf43b47c7a 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -981,6 +981,14 @@ void mei_cl_bus_rescan_work(struct work_struct *work) { struct mei_device *bus = container_of(work, struct mei_device, bus_rescan_work); + struct mei_me_client *me_cl; + + mutex_lock(&bus->device_lock); + me_cl = mei_me_cl_by_uuid(bus, &mei_amthif_guid); + if (me_cl) + mei_amthif_host_init(bus, me_cl); + mei_me_cl_put(me_cl); + mutex_unlock(&bus->device_lock); mei_cl_bus_rescan(bus); } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 2890669b81f9..af6816bc268f 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -666,25 +666,12 @@ int mei_cl_unlink(struct mei_cl *cl) return 0; } - -void mei_host_client_init(struct work_struct *work) +void mei_host_client_init(struct mei_device *dev) { - struct mei_device *dev = - container_of(work, struct mei_device, init_work); - struct mei_me_client *me_cl; - - mutex_lock(&dev->device_lock); - - me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); - if (me_cl) - mei_amthif_host_init(dev, me_cl); - mei_me_cl_put(me_cl); - dev->dev_state = MEI_DEV_ENABLED; dev->reset_count = 0; - mutex_unlock(&dev->device_lock); - mei_cl_bus_rescan(dev); + schedule_work(&dev->bus_rescan_work); pm_runtime_mark_last_busy(dev->dev); dev_dbg(dev->dev, "rpm: autosuspend\n"); diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index a912ea686d97..0d7a3a1fef78 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -226,7 +226,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb); -void mei_host_client_init(struct work_struct *work); +void mei_host_client_init(struct mei_device *dev); u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop); enum mei_cb_file_ops mei_cl_notify_req2fop(u8 request); diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index d2798d5b0a9d..5e305d2605f3 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -549,7 +549,7 @@ static int mei_hbm_prop_req(struct mei_device *dev) /* We got all client properties */ if (next_client_index == MEI_CLIENTS_MAX) { dev->hbm_state = MEI_HBM_STARTED; - schedule_work(&dev->init_work); + mei_host_client_init(dev); return 0; } diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 52fde2b498ef..f7c8dfdb6a12 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -91,7 +91,6 @@ EXPORT_SYMBOL_GPL(mei_fw_status2str); */ void mei_cancel_work(struct mei_device *dev) { - cancel_work_sync(&dev->init_work); cancel_work_sync(&dev->reset_work); cancel_work_sync(&dev->bus_rescan_work); @@ -393,7 +392,6 @@ void mei_device_init(struct mei_device *dev, mei_io_list_init(&dev->ctrl_rd_list); INIT_DELAYED_WORK(&dev->timer_work, mei_timer); - INIT_WORK(&dev->init_work, mei_host_client_init); INIT_WORK(&dev->reset_work, mei_reset_work); INIT_WORK(&dev->bus_rescan_work, mei_cl_bus_rescan_work); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 2b9160e506d7..db78e6d99456 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -410,7 +410,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @iamthif_state : amthif processor state * @iamthif_canceled : current amthif command is canceled * - * @init_work : work item for the device init * @reset_work : work item for the device reset * @bus_rescan_work : work item for the bus rescan * @@ -503,7 +502,6 @@ struct mei_device { enum iamthif_states iamthif_state; bool iamthif_canceled; - struct work_struct init_work; struct work_struct reset_work; struct work_struct bus_rescan_work; -- cgit v1.2.3 From 7b64dbf849abdd7e769820e25120758f956a7f13 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 19 Oct 2015 14:19:01 +0300 Subject: misc: mic/scif: fix wrap around tests Signed integer overflow is undefined. Also I added a check for "(offset < 0)" in scif_unregister() because that makes it match the other conditions and because I didn't want to subtract a negative. Fixes: ba612aa8b487 ('misc: mic: SCIF memory registration and unregistration') Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_rma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c index 8310b4dbff06..6a451bd65bf3 100644 --- a/drivers/misc/mic/scif/scif_rma.c +++ b/drivers/misc/mic/scif/scif_rma.c @@ -1511,7 +1511,7 @@ off_t scif_register_pinned_pages(scif_epd_t epd, if ((map_flags & SCIF_MAP_FIXED) && ((ALIGN(offset, PAGE_SIZE) != offset) || (offset < 0) || - (offset + (off_t)len < offset))) + (len > LONG_MAX - offset))) return -EINVAL; might_sleep(); @@ -1614,7 +1614,7 @@ off_t scif_register(scif_epd_t epd, void *addr, size_t len, off_t offset, if ((map_flags & SCIF_MAP_FIXED) && ((ALIGN(offset, PAGE_SIZE) != offset) || (offset < 0) || - (offset + (off_t)len < offset))) + (len > LONG_MAX - offset))) return -EINVAL; /* Unsupported protection requested */ @@ -1732,7 +1732,8 @@ scif_unregister(scif_epd_t epd, off_t offset, size_t len) /* Offset is not page aligned or offset+len wraps around */ if ((ALIGN(offset, PAGE_SIZE) != offset) || - (offset + (off_t)len < offset)) + (offset < 0) || + (len > LONG_MAX - offset)) return -EINVAL; err = scif_verify_epd(ep); -- cgit v1.2.3 From 0d0ce9c00b866090a69a2d63118b3e09cc71c96d Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 16 Nov 2015 21:46:31 +0800 Subject: misc: mic/scif: use list_next_entry instead of list_entry_next list_next_entry has been defined in list.h, so I replace list_entry_next with it. Signed-off-by: Geliang Tang Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_dma.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c index 95a13c629a8e..8804bccf04b6 100644 --- a/drivers/misc/mic/scif/scif_dma.c +++ b/drivers/misc/mic/scif/scif_dma.c @@ -74,11 +74,6 @@ struct scif_copy_work { bool ordered; }; -#ifndef list_entry_next -#define list_entry_next(pos, member) \ - list_entry(pos->member.next, typeof(*pos), member) -#endif - /** * scif_reserve_dma_chan: * @ep: Endpoint Descriptor. @@ -851,7 +846,7 @@ static void scif_rma_local_cpu_copy(s64 offset, struct scif_window *window, (window->nr_pages << PAGE_SHIFT); while (rem_len) { if (offset == end_offset) { - window = list_entry_next(window, list); + window = list_next_entry(window, list); end_offset = window->offset + (window->nr_pages << PAGE_SHIFT); } @@ -957,7 +952,7 @@ scif_rma_list_dma_copy_unaligned(struct scif_copy_work *work, remaining_len -= tail_len; while (remaining_len) { if (offset == end_offset) { - window = list_entry_next(window, list); + window = list_next_entry(window, list); end_offset = window->offset + (window->nr_pages << PAGE_SHIFT); } @@ -1064,7 +1059,7 @@ scif_rma_list_dma_copy_unaligned(struct scif_copy_work *work, } if (tail_len) { if (offset == end_offset) { - window = list_entry_next(window, list); + window = list_next_entry(window, list); end_offset = window->offset + (window->nr_pages << PAGE_SHIFT); } @@ -1147,13 +1142,13 @@ static int _scif_rma_list_dma_copy_aligned(struct scif_copy_work *work, (dst_window->nr_pages << PAGE_SHIFT); while (remaining_len) { if (src_offset == end_src_offset) { - src_window = list_entry_next(src_window, list); + src_window = list_next_entry(src_window, list); end_src_offset = src_window->offset + (src_window->nr_pages << PAGE_SHIFT); scif_init_window_iter(src_window, &src_win_iter); } if (dst_offset == end_dst_offset) { - dst_window = list_entry_next(dst_window, list); + dst_window = list_next_entry(dst_window, list); end_dst_offset = dst_window->offset + (dst_window->nr_pages << PAGE_SHIFT); scif_init_window_iter(dst_window, &dst_win_iter); @@ -1314,13 +1309,13 @@ static int scif_rma_list_dma_copy_aligned(struct scif_copy_work *work, remaining_len -= tail_len; while (remaining_len) { if (src_offset == end_src_offset) { - src_window = list_entry_next(src_window, list); + src_window = list_next_entry(src_window, list); end_src_offset = src_window->offset + (src_window->nr_pages << PAGE_SHIFT); scif_init_window_iter(src_window, &src_win_iter); } if (dst_offset == end_dst_offset) { - dst_window = list_entry_next(dst_window, list); + dst_window = list_next_entry(dst_window, list); end_dst_offset = dst_window->offset + (dst_window->nr_pages << PAGE_SHIFT); scif_init_window_iter(dst_window, &dst_win_iter); @@ -1405,9 +1400,9 @@ static int scif_rma_list_dma_copy_aligned(struct scif_copy_work *work, if (remaining_len) { loop_len = remaining_len; if (src_offset == end_src_offset) - src_window = list_entry_next(src_window, list); + src_window = list_next_entry(src_window, list); if (dst_offset == end_dst_offset) - dst_window = list_entry_next(dst_window, list); + dst_window = list_next_entry(dst_window, list); src_dma_addr = __scif_off_to_dma_addr(src_window, src_offset); dst_dma_addr = __scif_off_to_dma_addr(dst_window, dst_offset); @@ -1550,12 +1545,12 @@ static int scif_rma_list_cpu_copy(struct scif_copy_work *work) end_dst_offset = dst_window->offset + (dst_window->nr_pages << PAGE_SHIFT); if (src_offset == end_src_offset) { - src_window = list_entry_next(src_window, list); + src_window = list_next_entry(src_window, list); scif_init_window_iter(src_window, &src_win_iter); } if (dst_offset == end_dst_offset) { - dst_window = list_entry_next(dst_window, list); + dst_window = list_next_entry(dst_window, list); scif_init_window_iter(dst_window, &dst_win_iter); } -- cgit v1.2.3 From d40a09464401265e2894061cca388fb86e05b4be Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 11 Dec 2015 20:09:16 -0600 Subject: misc: mic: fix incorrect use of error codes in SCIF DMA driver The error code passed to ERR_PTR() always should be negated. Also, the return value of scif_add_mmu_notifier() was never checked. Signed-off-by: Eric Biggers Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_dma.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c index 8804bccf04b6..cd01a0efda6b 100644 --- a/drivers/misc/mic/scif/scif_dma.c +++ b/drivers/misc/mic/scif/scif_dma.c @@ -271,13 +271,10 @@ static struct scif_mmu_notif * scif_find_mmu_notifier(struct mm_struct *mm, struct scif_endpt_rma_info *rma) { struct scif_mmu_notif *mmn; - struct list_head *item; - list_for_each(item, &rma->mmn_list) { - mmn = list_entry(item, struct scif_mmu_notif, list); + list_for_each_entry(mmn, &rma->mmn_list, list) if (mmn->mm == mm) return mmn; - } return NULL; } @@ -288,13 +285,12 @@ scif_add_mmu_notifier(struct mm_struct *mm, struct scif_endpt *ep) = kzalloc(sizeof(*mmn), GFP_KERNEL); if (!mmn) - return ERR_PTR(ENOMEM); + return ERR_PTR(-ENOMEM); scif_init_mmu_notifier(mmn, current->mm, ep); - if (mmu_notifier_register(&mmn->ep_mmu_notifier, - current->mm)) { + if (mmu_notifier_register(&mmn->ep_mmu_notifier, current->mm)) { kfree(mmn); - return ERR_PTR(EBUSY); + return ERR_PTR(-EBUSY); } list_add(&mmn->list, &ep->rma_info.mmn_list); return mmn; @@ -1725,7 +1721,7 @@ static int scif_rma_copy(scif_epd_t epd, off_t loffset, unsigned long addr, mutex_lock(&ep->rma_info.mmn_lock); mmn = scif_find_mmu_notifier(current->mm, &ep->rma_info); if (!mmn) - scif_add_mmu_notifier(current->mm, ep); + mmn = scif_add_mmu_notifier(current->mm, ep); mutex_unlock(&ep->rma_info.mmn_lock); if (IS_ERR(mmn)) { scif_put_peer_dev(spdev); -- cgit v1.2.3 From f42a0fd13bd281811e7457b28d939c8e8b808868 Mon Sep 17 00:00:00 2001 From: Jorgen Hansen Date: Thu, 12 Nov 2015 01:29:32 -0800 Subject: VMCI: Use 32bit atomics for queue headers on X86_32 This change restricts the reading and setting of the head and tail pointers on 32bit X86 to 32bit for both correctness and performance reasons. On uniprocessor X86_32, the atomic64_read may be implemented as a non-locked cmpxchg8b. This may result in updates to the pointers done by the VMCI device being overwritten. On MP systems, there is no such correctness issue, but using 32bit atomics avoids the overhead of the locked 64bit operation. All this is safe because the queue size on 32bit systems will never exceed a 32bit value. Reviewed-by: Thomas Hellstrom Signed-off-by: Jorgen Hansen Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_driver.c | 2 +- include/linux/vmw_vmci_defs.h | 43 +++++++++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 5 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/vmw_vmci/vmci_driver.c b/drivers/misc/vmw_vmci/vmci_driver.c index b823f9a6e464..896be150e28f 100644 --- a/drivers/misc/vmw_vmci/vmci_driver.c +++ b/drivers/misc/vmw_vmci/vmci_driver.c @@ -113,5 +113,5 @@ module_exit(vmci_drv_exit); MODULE_AUTHOR("VMware, Inc."); MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface."); -MODULE_VERSION("1.1.3.0-k"); +MODULE_VERSION("1.1.4.0-k"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/vmw_vmci_defs.h b/include/linux/vmw_vmci_defs.h index 65ac54c61c18..1bd31a38c51e 100644 --- a/include/linux/vmw_vmci_defs.h +++ b/include/linux/vmw_vmci_defs.h @@ -733,6 +733,41 @@ static inline void *vmci_event_data_payload(struct vmci_event_data *ev_data) return (void *)vmci_event_data_const_payload(ev_data); } +/* + * Helper to read a value from a head or tail pointer. For X86_32, the + * pointer is treated as a 32bit value, since the pointer value + * never exceeds a 32bit value in this case. Also, doing an + * atomic64_read on X86_32 uniprocessor systems may be implemented + * as a non locked cmpxchg8b, that may end up overwriting updates done + * by the VMCI device to the memory location. On 32bit SMP, the lock + * prefix will be used, so correctness isn't an issue, but using a + * 64bit operation still adds unnecessary overhead. + */ +static inline u64 vmci_q_read_pointer(atomic64_t *var) +{ +#if defined(CONFIG_X86_32) + return atomic_read((atomic_t *)var); +#else + return atomic64_read(var); +#endif +} + +/* + * Helper to set the value of a head or tail pointer. For X86_32, the + * pointer is treated as a 32bit value, since the pointer value + * never exceeds a 32bit value in this case. On 32bit SMP, using a + * locked cmpxchg8b adds unnecessary overhead. + */ +static inline void vmci_q_set_pointer(atomic64_t *var, + u64 new_val) +{ +#if defined(CONFIG_X86_32) + return atomic_set((atomic_t *)var, (u32)new_val); +#else + return atomic64_set(var, new_val); +#endif +} + /* * Helper to add a given offset to a head or tail pointer. Wraps the * value of the pointer around the max size of the queue. @@ -741,14 +776,14 @@ static inline void vmci_qp_add_pointer(atomic64_t *var, size_t add, u64 size) { - u64 new_val = atomic64_read(var); + u64 new_val = vmci_q_read_pointer(var); if (new_val >= size - add) new_val -= size; new_val += add; - atomic64_set(var, new_val); + vmci_q_set_pointer(var, new_val); } /* @@ -758,7 +793,7 @@ static inline u64 vmci_q_header_producer_tail(const struct vmci_queue_header *q_header) { struct vmci_queue_header *qh = (struct vmci_queue_header *)q_header; - return atomic64_read(&qh->producer_tail); + return vmci_q_read_pointer(&qh->producer_tail); } /* @@ -768,7 +803,7 @@ static inline u64 vmci_q_header_consumer_head(const struct vmci_queue_header *q_header) { struct vmci_queue_header *qh = (struct vmci_queue_header *)q_header; - return atomic64_read(&qh->consumer_head); + return vmci_q_read_pointer(&qh->consumer_head); } /* -- cgit v1.2.3 From f38e87e8c8d326ec6cddfc3f286b605d996a52a3 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 17 Nov 2015 15:55:37 -0800 Subject: misc: mic: Fix crash when MIC reset is invoked in RESET_FAILED state This patch fixes the following crash seen when MIC reset is invoked in RESET_FAILED state due to device_del being called a second time on an already deleted device: [] device_del+0x45/0x1d0 [] device_unregister+0x1e/0x60 [] scif_unregister_device+0x12/0x20 [scif_bus] [] cosm_stop+0xaa/0xe0 [mic_cosm] [] cosm_reset_trigger_work+0x14/0x20 [mic_cosm] The fix consists in realizing that because cosm_reset changes the state to MIC_RESETTING, cosm_stop needs the previous state, before it changed to MIC_RESETTING, to decide whether a hw_ops->stop had previously been issued. This is now provided in a new cosm_device member cdev->prev_state. Reviewed-by: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/bus/cosm_bus.h | 2 ++ drivers/misc/mic/cosm/cosm_main.c | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/bus/cosm_bus.h b/drivers/misc/mic/bus/cosm_bus.h index f7c57f266916..8b6341855dc3 100644 --- a/drivers/misc/mic/bus/cosm_bus.h +++ b/drivers/misc/mic/bus/cosm_bus.h @@ -30,6 +30,7 @@ * @attr_group: Pointer to list of sysfs attribute groups. * @sdev: Device for sysfs entries. * @state: MIC state. + * @prev_state: MIC state previous to MIC_RESETTING * @shutdown_status: MIC status reported by card for shutdown/crashes. * @shutdown_status_int: Internal shutdown status maintained by the driver * @cosm_mutex: Mutex for synchronizing access to data structures. @@ -55,6 +56,7 @@ struct cosm_device { const struct attribute_group **attr_group; struct device *sdev; u8 state; + u8 prev_state; u8 shutdown_status; u8 shutdown_status_int; struct mutex cosm_mutex; diff --git a/drivers/misc/mic/cosm/cosm_main.c b/drivers/misc/mic/cosm/cosm_main.c index 4b4b356c797d..7005cb1e01d2 100644 --- a/drivers/misc/mic/cosm/cosm_main.c +++ b/drivers/misc/mic/cosm/cosm_main.c @@ -153,8 +153,10 @@ void cosm_stop(struct cosm_device *cdev, bool force) * stop(..) calls device_unregister and will crash the system if * called multiple times. */ - bool call_hw_ops = cdev->state != MIC_RESET_FAILED && - cdev->state != MIC_READY; + u8 state = cdev->state == MIC_RESETTING ? + cdev->prev_state : cdev->state; + bool call_hw_ops = state != MIC_RESET_FAILED && + state != MIC_READY; if (cdev->state != MIC_RESETTING) cosm_set_state(cdev, MIC_RESETTING); @@ -195,8 +197,11 @@ int cosm_reset(struct cosm_device *cdev) mutex_lock(&cdev->cosm_mutex); if (cdev->state != MIC_READY) { - cosm_set_state(cdev, MIC_RESETTING); - schedule_work(&cdev->reset_trigger_work); + if (cdev->state != MIC_RESETTING) { + cdev->prev_state = cdev->state; + cosm_set_state(cdev, MIC_RESETTING); + schedule_work(&cdev->reset_trigger_work); + } } else { dev_err(&cdev->dev, "%s %d MIC is READY\n", __func__, __LINE__); rc = -EINVAL; -- cgit v1.2.3 From e6c0effa0e4dd17b4569bb62eaf6283033d475a4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Dec 2015 13:11:28 +0300 Subject: mic_virtio: fix a timeout loop After the loop we test "if (!retry)" to see if we timedout. The problem is "retry--" is a post-op so retry will be -1 at the end of the loop. I have fixed this by changing it to a pre-op instead. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/card/mic_virtio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c index f6ed57d3125c..17764b2fbee5 100644 --- a/drivers/misc/mic/card/mic_virtio.c +++ b/drivers/misc/mic/card/mic_virtio.c @@ -337,7 +337,7 @@ static int mic_find_vqs(struct virtio_device *vdev, unsigned nvqs, * rings have been re-assigned. */ mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); - for (retry = 100; retry--;) { + for (retry = 100; --retry;) { if (!ioread8(&dc->used_address_updated)) break; msleep(100); -- cgit v1.2.3 From d897d7edee8eaf0a8f7924a9502fd68e72041f8c Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 23 Nov 2015 17:24:29 +0530 Subject: misc: mic: remove unneeded debug message >From the error path we are printing an error message with dev_err(). No need to print almost same message with dev_dbg(). Signed-off-by: Sudip Mukherjee Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_x100.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index 8118ac48c764..cd5208d1895d 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -450,21 +450,21 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) rc = mic_x100_get_boot_addr(mdev); if (rc) - goto error; + goto done; /* load OS */ rc = request_firmware(&fw, mdev->cosm_dev->firmware, &mdev->pdev->dev); if (rc < 0) { dev_err(&mdev->pdev->dev, "ramdisk request_firmware failed: %d %s\n", rc, mdev->cosm_dev->firmware); - goto error; + goto done; } if (mdev->bootaddr > mdev->aper.len - fw->size) { rc = -EINVAL; dev_err(&mdev->pdev->dev, "%s %d rc %d bootaddr 0x%x\n", __func__, __LINE__, rc, mdev->bootaddr); release_firmware(fw); - goto error; + goto done; } memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); @@ -475,14 +475,13 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) if (rc) { dev_err(&mdev->pdev->dev, "%s %d rc %d\n", __func__, __LINE__, rc); - goto error; + goto done; } release_firmware(fw); /* load ramdisk */ if (mdev->cosm_dev->ramdisk) rc = mic_x100_load_ramdisk(mdev); -error: - dev_dbg(&mdev->pdev->dev, "%s %d rc %d\n", __func__, __LINE__, rc); + done: return rc; } -- cgit v1.2.3 From ba1b5c44e2a9fed6f97fb10da3b4da164d4a26e4 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 23 Nov 2015 17:24:30 +0530 Subject: misc: mic: return error properly If request_firmware() succeeds then rc becomes 0. After that if the test for strcmp() fails then we were jumping to label done: and returning rc. But rc being 0 we returned success whereas we have failed here and we were supposed to return an error. Signed-off-by: Sudip Mukherjee Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_x100.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index cd5208d1895d..317e25ff484c 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -468,8 +468,13 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) } memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); - if (!strcmp(mdev->cosm_dev->bootmode, "flash")) + if (!strcmp(mdev->cosm_dev->bootmode, "flash")) { + rc = -EINVAL; + dev_err(&mdev->pdev->dev, "%s %d rc %d\n", + __func__, __LINE__, rc); + release_firmware(fw); goto done; + } /* load command line */ rc = mic_x100_load_command_line(mdev, fw); if (rc) { -- cgit v1.2.3 From fbaa72d56424553f0aa434d8816a0da7a9071415 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 23 Nov 2015 17:24:31 +0530 Subject: misc: mic: return error directly Instead of jumping to a label and then returning from there lets return directly. Signed-off-by: Sudip Mukherjee Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_x100.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index 317e25ff484c..37fa89875e1e 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -450,14 +450,14 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) rc = mic_x100_get_boot_addr(mdev); if (rc) - goto done; + return rc; /* load OS */ rc = request_firmware(&fw, mdev->cosm_dev->firmware, &mdev->pdev->dev); if (rc < 0) { dev_err(&mdev->pdev->dev, "ramdisk request_firmware failed: %d %s\n", rc, mdev->cosm_dev->firmware); - goto done; + return rc; } if (mdev->bootaddr > mdev->aper.len - fw->size) { rc = -EINVAL; -- cgit v1.2.3 From 5fb437984b6c4793626be3936297fecece987a9f Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 23 Nov 2015 17:24:32 +0530 Subject: misc: mic: use common error path Instead of calling release_firmware() on every error and then jumping lets have a common release_firmware() in the error path. This patch also fixes a memory leak where we missed release_firmware() if mic_x100_load_command_line() fails. Signed-off-by: Sudip Mukherjee Reviewed-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_x100.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index 37fa89875e1e..82a973c85b5d 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -463,8 +463,7 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) rc = -EINVAL; dev_err(&mdev->pdev->dev, "%s %d rc %d bootaddr 0x%x\n", __func__, __LINE__, rc, mdev->bootaddr); - release_firmware(fw); - goto done; + goto error; } memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); @@ -472,22 +471,24 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) rc = -EINVAL; dev_err(&mdev->pdev->dev, "%s %d rc %d\n", __func__, __LINE__, rc); - release_firmware(fw); - goto done; + goto error; } /* load command line */ rc = mic_x100_load_command_line(mdev, fw); if (rc) { dev_err(&mdev->pdev->dev, "%s %d rc %d\n", __func__, __LINE__, rc); - goto done; + goto error; } release_firmware(fw); /* load ramdisk */ if (mdev->cosm_dev->ramdisk) rc = mic_x100_load_ramdisk(mdev); -done: + return rc; + +error: + release_firmware(fw); return rc; } -- cgit v1.2.3 From c7f340f844de848e6e15c964ad2ce597dbd31441 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 20 Nov 2015 15:23:50 +0530 Subject: misc: st_core: remove unreachable code The pr_debug() will never be executed. Signed-off-by: Sudip Mukherjee Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti-st/st_core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 6e3af8b42cdd..dcdbd58672cc 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -632,7 +632,6 @@ long st_register(struct st_proto_s *new_proto) spin_unlock_irqrestore(&st_gdata->lock, flags); return err; } - pr_debug("done %s(%d) ", __func__, new_proto->chnl_id); } EXPORT_SYMBOL_GPL(st_register); -- cgit v1.2.3 From 46fd8c3426c2de74966480f83ebc8a1ed59b9840 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Mon, 30 Nov 2015 16:21:35 +0000 Subject: misc: pch_phub: allow build on MIPS platforms Allow the pch_phub driver to be build on MIPS platforms, in preparation for its use on the MIPS Boston board. Signed-off-by: Paul Burton Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 054fc10cb3b6..d8b11d7d569c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -470,7 +470,7 @@ config BMP085_SPI config PCH_PHUB tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB" select GENERIC_NET_UTILS - depends on PCI && (X86_32 || COMPILE_TEST) + depends on PCI && (X86_32 || MIPS || COMPILE_TEST) help This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of Intel Topcliff which is an IOH(Input/Output Hub) for x86 embedded -- cgit v1.2.3 From 47312a4a484f21e04d286367f16dd432cf646163 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Dec 2015 20:07:14 -0500 Subject: drivers/misc: make arm-charlcd.c explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/misc/Kconfig:config ARM_CHARLCD drivers/misc/Kconfig: bool "ARM Ltd. Character LCD Driver" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. We explicitly disallow a driver unbind, since that doesn't have a sensible use case anyway, and this driver did not have a ".remove" function coded for non-modular drivers either. Since module_platform_driver() uses the same init level priority as builtin_platform_driver() the init ordering remains unchanged with this commit. We don't replace module.h with init.h since the file already has that. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Cc: Arnd Bergmann Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Paul Gortmaker Reviewed-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/misc/arm-charlcd.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c index c65b5ea5d5ef..b3176ee92b90 100644 --- a/drivers/misc/arm-charlcd.c +++ b/drivers/misc/arm-charlcd.c @@ -8,7 +8,6 @@ * Author: Linus Walleij */ #include -#include #include #include #include @@ -328,20 +327,6 @@ out_no_resource: return ret; } -static int __exit charlcd_remove(struct platform_device *pdev) -{ - struct charlcd *lcd = platform_get_drvdata(pdev); - - if (lcd) { - free_irq(lcd->irq, lcd); - iounmap(lcd->virtbase); - release_mem_region(lcd->phybase, lcd->physize); - kfree(lcd); - } - - return 0; -} - static int charlcd_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -376,13 +361,8 @@ static struct platform_driver charlcd_driver = { .driver = { .name = DRIVERNAME, .pm = &charlcd_pm_ops, + .suppress_bind_attrs = true, .of_match_table = of_match_ptr(charlcd_match), }, - .remove = __exit_p(charlcd_remove), }; - -module_platform_driver_probe(charlcd_driver, charlcd_probe); - -MODULE_AUTHOR("Linus Walleij "); -MODULE_DESCRIPTION("ARM Character LCD Driver"); -MODULE_LICENSE("GPL v2"); +builtin_platform_driver_probe(charlcd_driver, charlcd_probe); -- cgit v1.2.3 From 50e6315dba721cbc24ccd6d7b299f1782f210a98 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 14 Dec 2015 14:29:23 +0000 Subject: misc/bmp085: Enable building as a module Commit 985087dbcb02 'misc: add support for bmp18x chips to the bmp085 driver' changed the BMP085 config symbol to a boolean. I see no reason why the shared code cannot be built as a module, so change it back to tristate. Fixes: 985087dbcb02 ("misc: add support for bmp18x chips to the bmp085 driver") Cc: Eric Andersson Signed-off-by: Ben Hutchings Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d8b11d7d569c..15579514d120 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -440,7 +440,7 @@ config ARM_CHARLCD still useful. config BMP085 - bool + tristate depends on SYSFS config BMP085_I2C -- cgit v1.2.3 From 8c99d8e6de0474bf5a7004e41c044ac4dcacbae1 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 28 Dec 2015 23:00:16 +0800 Subject: misc: apds990x, bh1770glc, lis3lv02d: use to_i2c_client Use to_i2c_client() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/apds990x.c | 8 ++++---- drivers/misc/bh1770glc.c | 8 ++++---- drivers/misc/lis3lv02d/lis3lv02d_i2c.c | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index a3e789b85cc8..dfb72ecfa604 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -1215,7 +1215,7 @@ static int apds990x_remove(struct i2c_client *client) #ifdef CONFIG_PM_SLEEP static int apds990x_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct apds990x_chip *chip = i2c_get_clientdata(client); apds990x_chip_off(chip); @@ -1224,7 +1224,7 @@ static int apds990x_suspend(struct device *dev) static int apds990x_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct apds990x_chip *chip = i2c_get_clientdata(client); /* @@ -1240,7 +1240,7 @@ static int apds990x_resume(struct device *dev) #ifdef CONFIG_PM static int apds990x_runtime_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct apds990x_chip *chip = i2c_get_clientdata(client); apds990x_chip_off(chip); @@ -1249,7 +1249,7 @@ static int apds990x_runtime_suspend(struct device *dev) static int apds990x_runtime_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct apds990x_chip *chip = i2c_get_clientdata(client); apds990x_chip_on(chip); diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index 753d7ecdadaa..845466e45b95 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -1323,7 +1323,7 @@ static int bh1770_remove(struct i2c_client *client) #ifdef CONFIG_PM_SLEEP static int bh1770_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct bh1770_chip *chip = i2c_get_clientdata(client); bh1770_chip_off(chip); @@ -1333,7 +1333,7 @@ static int bh1770_suspend(struct device *dev) static int bh1770_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct bh1770_chip *chip = i2c_get_clientdata(client); int ret = 0; @@ -1361,7 +1361,7 @@ static int bh1770_resume(struct device *dev) #ifdef CONFIG_PM static int bh1770_runtime_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct bh1770_chip *chip = i2c_get_clientdata(client); bh1770_chip_off(chip); @@ -1371,7 +1371,7 @@ static int bh1770_runtime_suspend(struct device *dev) static int bh1770_runtime_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct bh1770_chip *chip = i2c_get_clientdata(client); bh1770_chip_on(chip); diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c index 0c3bb7e3ee80..14b7d539fed6 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c @@ -209,7 +209,7 @@ static int lis3lv02d_i2c_remove(struct i2c_client *client) #ifdef CONFIG_PM_SLEEP static int lis3lv02d_i2c_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); if (!lis3->pdata || !lis3->pdata->wakeup_flags) @@ -219,7 +219,7 @@ static int lis3lv02d_i2c_suspend(struct device *dev) static int lis3lv02d_i2c_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); /* @@ -238,7 +238,7 @@ static int lis3lv02d_i2c_resume(struct device *dev) #ifdef CONFIG_PM static int lis3_i2c_runtime_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); lis3lv02d_poweroff(lis3); @@ -247,7 +247,7 @@ static int lis3_i2c_runtime_suspend(struct device *dev) static int lis3_i2c_runtime_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct i2c_client *client = to_i2c_client(dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); lis3lv02d_poweron(lis3); -- cgit v1.2.3 From 092462c2b52259edba80a6748acb3305f7f70423 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 13 Jan 2016 23:30:11 +0800 Subject: misc: eeprom: use kobj_to_dev() Use kobj_to_dev() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at24.c | 4 ++-- drivers/misc/eeprom/at25.c | 4 ++-- drivers/misc/eeprom/eeprom.c | 2 +- drivers/misc/eeprom/eeprom_93xx46.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 5d7c0900fa1b..d105c2564400 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -289,7 +289,7 @@ static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj, { struct at24_data *at24; - at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); + at24 = dev_get_drvdata(kobj_to_dev(kobj)); return at24_read(at24, buf, off, count); } @@ -420,7 +420,7 @@ static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj, { struct at24_data *at24; - at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); + at24 = dev_get_drvdata(kobj_to_dev(kobj)); return at24_write(at24, buf, off, count); } diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index f850ef556bcc..3e9e5a28acaa 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -139,7 +139,7 @@ at25_bin_read(struct file *filp, struct kobject *kobj, struct device *dev; struct at25_data *at25; - dev = container_of(kobj, struct device, kobj); + dev = kobj_to_dev(kobj); at25 = dev_get_drvdata(dev); return at25_ee_read(at25, buf, off, count); @@ -273,7 +273,7 @@ at25_bin_write(struct file *filp, struct kobject *kobj, struct device *dev; struct at25_data *at25; - dev = container_of(kobj, struct device, kobj); + dev = kobj_to_dev(kobj); at25 = dev_get_drvdata(dev); return at25_ee_write(at25, buf, off, count); diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index 7342fd637031..3d1d55157e5f 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -84,7 +84,7 @@ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj)); struct eeprom_data *data = i2c_get_clientdata(client); u8 slice; diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index ff63f05edc76..473aa0a2eaf6 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -45,7 +45,7 @@ eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj, int bits, ret; u16 cmd_addr; - dev = container_of(kobj, struct device, kobj); + dev = kobj_to_dev(kobj); edev = dev_get_drvdata(dev); cmd_addr = OP_READ << edev->addrlen; @@ -190,7 +190,7 @@ eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj, struct device *dev; int i, ret, step = 1; - dev = container_of(kobj, struct device, kobj); + dev = kobj_to_dev(kobj); edev = dev_get_drvdata(dev); /* only write even number of bytes on 16-bit devices */ -- cgit v1.2.3 From 85016ff33f359981a8c78a1eb67b04777207775c Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 13 Jan 2016 23:30:09 +0800 Subject: misc: cxl: use kobj_to_dev() Use kobj_to_dev() instead of open-coding it. Signed-off-by: Geliang Tang Reviewed-by: Andrew Donnellan Acked-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cxl/sysfs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index 02006f7109a8..038af5d45145 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c @@ -386,8 +386,7 @@ static ssize_t afu_eb_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct cxl_afu *afu = to_cxl_afu(container_of(kobj, - struct device, kobj)); + struct cxl_afu *afu = to_cxl_afu(kobj_to_dev(kobj)); return cxl_afu_read_err_buffer(afu, buf, off, count); } @@ -467,7 +466,7 @@ static ssize_t afu_read_config(struct file *filp, struct kobject *kobj, loff_t off, size_t count) { struct afu_config_record *cr = to_cr(kobj); - struct cxl_afu *afu = to_cxl_afu(container_of(kobj->parent, struct device, kobj)); + struct cxl_afu *afu = to_cxl_afu(kobj_to_dev(kobj->parent)); u64 i, j, val; -- cgit v1.2.3 From 47679cde604d6977b390d5b0fc83dedf8a82f66d Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 13 Jan 2016 23:30:08 +0800 Subject: misc: c2port: use kobj_to_dev() Use kobj_to_dev() instead of open-coding it. Signed-off-by: Geliang Tang Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/misc/c2port/core.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c index cc8645b5369d..1922cb8f6b88 100644 --- a/drivers/misc/c2port/core.c +++ b/drivers/misc/c2port/core.c @@ -721,9 +721,7 @@ static ssize_t c2port_read_flash_data(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buffer, loff_t offset, size_t count) { - struct c2port_device *c2dev = - dev_get_drvdata(container_of(kobj, - struct device, kobj)); + struct c2port_device *c2dev = dev_get_drvdata(kobj_to_dev(kobj)); ssize_t ret; /* Check the device and flash access status */ @@ -838,9 +836,7 @@ static ssize_t c2port_write_flash_data(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buffer, loff_t offset, size_t count) { - struct c2port_device *c2dev = - dev_get_drvdata(container_of(kobj, - struct device, kobj)); + struct c2port_device *c2dev = dev_get_drvdata(kobj_to_dev(kobj)); int ret; /* Check the device access status */ -- cgit v1.2.3 From 85f4f39c80e9350da8a351e6ea36367fa9fb87a5 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 13 Jan 2016 23:30:10 +0800 Subject: pch_phub: use kobj_to_dev() Use kobj_to_dev() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 9a17a9bab8d6..15bb0c8cdda3 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -503,8 +503,7 @@ static ssize_t pch_phub_bin_read(struct file *filp, struct kobject *kobj, int err; ssize_t rom_size; - struct pch_phub_reg *chip = - dev_get_drvdata(container_of(kobj, struct device, kobj)); + struct pch_phub_reg *chip = dev_get_drvdata(kobj_to_dev(kobj)); ret = mutex_lock_interruptible(&pch_phub_mutex); if (ret) { @@ -567,8 +566,7 @@ static ssize_t pch_phub_bin_write(struct file *filp, struct kobject *kobj, unsigned int addr_offset; int ret; ssize_t rom_size; - struct pch_phub_reg *chip = - dev_get_drvdata(container_of(kobj, struct device, kobj)); + struct pch_phub_reg *chip = dev_get_drvdata(kobj_to_dev(kobj)); ret = mutex_lock_interruptible(&pch_phub_mutex); if (ret) -- cgit v1.2.3 From 6908b45eafc480e9da8196f987b1d0f6c887a5d7 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 13 Jan 2016 23:30:12 +0800 Subject: GenWQE: use kobj_to_dev() Use kobj_to_dev() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/genwqe/card_sysfs.c b/drivers/misc/genwqe/card_sysfs.c index 6ab31eff0536..c24c9b7c1dd3 100644 --- a/drivers/misc/genwqe/card_sysfs.c +++ b/drivers/misc/genwqe/card_sysfs.c @@ -278,7 +278,7 @@ static umode_t genwqe_is_visible(struct kobject *kobj, struct attribute *attr, int n) { unsigned int j; - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct genwqe_dev *cd = dev_get_drvdata(dev); umode_t mode = attr->mode; -- cgit v1.2.3 From cb54ad6cddb606add2481b82901d69670b480d1b Mon Sep 17 00:00:00 2001 From: Cory Tusar Date: Wed, 6 Jan 2016 22:55:00 -0500 Subject: misc: eeprom_93xx46: Fix 16-bit read and write accesses. Compatible at93xx46 devices from both Microchip and Atmel expect a word-based address, regardless of whether the device is strapped for 8- or 16-bit operation. However, the offset parameter passed in when reading or writing at a specific location is always specified in terms of bytes. This commit fixes 16-bit read and write accesses by shifting the offset parameter to account for this difference between a byte offset and a word-based address. Signed-off-by: Cory Tusar Tested-by: Chris Healy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/eeprom_93xx46.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index 473aa0a2eaf6..da3c081c2639 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -54,7 +54,7 @@ eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj, cmd_addr |= off & 0x7f; bits = 10; } else { - cmd_addr |= off & 0x3f; + cmd_addr |= (off >> 1) & 0x3f; bits = 9; } @@ -155,7 +155,7 @@ eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev, bits = 10; data_len = 1; } else { - cmd_addr |= off & 0x3f; + cmd_addr |= (off >> 1) & 0x3f; bits = 9; data_len = 2; } -- cgit v1.2.3 From c074abe02e5e3479b2dfd109fa2620d22d351c34 Mon Sep 17 00:00:00 2001 From: Cory Tusar Date: Wed, 6 Jan 2016 22:55:02 -0500 Subject: misc: eeprom_93xx46: Implement eeprom_93xx46 DT bindings. This commit implements bindings in the eeprom_93xx46 driver allowing device word size and read-only attributes to be specified via devicetree. Signed-off-by: Cory Tusar Tested-by: Chris Healy Reviewed-by: Vladimir Zapolskiy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/eeprom_93xx46.c | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index da3c081c2639..f0c30b366218 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -294,12 +296,58 @@ static ssize_t eeprom_93xx46_store_erase(struct device *dev, } static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase); +static const struct of_device_id eeprom_93xx46_of_table[] = { + { .compatible = "eeprom-93xx46", }, + {} +}; +MODULE_DEVICE_TABLE(of, eeprom_93xx46_of_table); + +static int eeprom_93xx46_probe_dt(struct spi_device *spi) +{ + struct device_node *np = spi->dev.of_node; + struct eeprom_93xx46_platform_data *pd; + u32 tmp; + int ret; + + pd = devm_kzalloc(&spi->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + ret = of_property_read_u32(np, "data-size", &tmp); + if (ret < 0) { + dev_err(&spi->dev, "data-size property not found\n"); + return ret; + } + + if (tmp == 8) { + pd->flags |= EE_ADDR8; + } else if (tmp == 16) { + pd->flags |= EE_ADDR16; + } else { + dev_err(&spi->dev, "invalid data-size (%d)\n", tmp); + return -EINVAL; + } + + if (of_property_read_bool(np, "read-only")) + pd->flags |= EE_READONLY; + + spi->dev.platform_data = pd; + + return 0; +} + static int eeprom_93xx46_probe(struct spi_device *spi) { struct eeprom_93xx46_platform_data *pd; struct eeprom_93xx46_dev *edev; int err; + if (spi->dev.of_node) { + err = eeprom_93xx46_probe_dt(spi); + if (err < 0) + return err; + } + pd = spi->dev.platform_data; if (!pd) { dev_err(&spi->dev, "missing platform data\n"); @@ -370,6 +418,7 @@ static int eeprom_93xx46_remove(struct spi_device *spi) static struct spi_driver eeprom_93xx46_driver = { .driver = { .name = "93xx46", + .of_match_table = of_match_ptr(eeprom_93xx46_of_table), }, .probe = eeprom_93xx46_probe, .remove = eeprom_93xx46_remove, -- cgit v1.2.3 From ececdc021ba8f48ce87f476dd412e2ed50f9cd8e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 8 Feb 2016 15:34:21 +0100 Subject: lkdtm: mark execute_location as noinline The kernel sometimes fails to link when lkdrm is built-in and compiled with clang: relocation truncated to fit: R_ARM_THM_CALL against `.bss' The reason here is that a relocation from .text to .bss fails to generate a trampoline because .bss is not an executable section. Marking the function 'noinline' turns the relative branch to .bss into an absolute branch to the function argument, and that works fine. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 11fdadc68e53..5c1351b19029 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -335,7 +335,7 @@ static noinline void corrupt_stack(void) memset((void *)data, 0, 64); } -static void execute_location(void *dst) +static void noinline execute_location(void *dst) { void (*func)(void) = dst; -- cgit v1.2.3 From 4ddbdbb90d2ec7d7db93c98936faf3dad75f479b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 9 Feb 2016 00:00:17 +0200 Subject: mei: wd: drop AGAIN the watchdog code from the core mei driver The file wd.c was remove from the driver by commit commit fdd9b8655933 ("mei: wd: drop the watchdog code from the core mei driver") Unfortunately it came back by mistake in rebasing in the commit commit 06ee536bcb15 ("mei: fill file pointer in read cb for fixed address client") Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/wd.c | 391 -------------------------------------------------- 1 file changed, 391 deletions(-) delete mode 100644 drivers/misc/mei/wd.c (limited to 'drivers/misc') diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c deleted file mode 100644 index 7d9b4ee42f65..000000000000 --- a/drivers/misc/mei/wd.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * - * Intel Management Engine Interface (Intel MEI) Linux driver - * Copyright (c) 2003-2012, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - */ -#include -#include -#include -#include -#include -#include - -#include - -#include "mei_dev.h" -#include "hbm.h" -#include "client.h" - -static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 }; -static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 }; - -/* - * AMT Watchdog Device - */ -#define INTEL_AMT_WATCHDOG_ID "INTCAMT" - -/* UUIDs for AMT F/W clients */ -const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, - 0x9D, 0xA9, 0x15, 0x14, 0xCB, - 0x32, 0xAB); - -static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) -{ - dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout); - memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE); - memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16)); -} - -/** - * mei_wd_host_init - connect to the watchdog client - * - * @dev: the device structure - * @me_cl: me client - * - * Return: -ENOTTY if wd client cannot be found - * -EIO if write has failed - * 0 on success - */ -int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl) -{ - struct mei_cl *cl = &dev->wd_cl; - int ret; - - mei_cl_init(cl, dev); - - dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; - dev->wd_state = MEI_WD_IDLE; - - ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); - if (ret < 0) { - dev_info(dev->dev, "wd: failed link client\n"); - return ret; - } - - ret = mei_cl_connect(cl, me_cl, NULL); - if (ret) { - dev_err(dev->dev, "wd: failed to connect = %d\n", ret); - mei_cl_unlink(cl); - return ret; - } - - ret = mei_watchdog_register(dev); - if (ret) { - mei_cl_disconnect(cl); - mei_cl_unlink(cl); - } - return ret; -} - -/** - * mei_wd_send - sends watch dog message to fw. - * - * @dev: the device structure - * - * Return: 0 if success, - * -EIO when message send fails - * -EINVAL when invalid message is to be sent - * -ENODEV on flow control failure - */ -int mei_wd_send(struct mei_device *dev) -{ - struct mei_cl *cl = &dev->wd_cl; - struct mei_msg_hdr hdr; - int ret; - - hdr.host_addr = cl->host_client_id; - hdr.me_addr = mei_cl_me_id(cl); - hdr.msg_complete = 1; - hdr.reserved = 0; - hdr.internal = 0; - - if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE)) - hdr.length = MEI_WD_START_MSG_SIZE; - else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE)) - hdr.length = MEI_WD_STOP_MSG_SIZE; - else { - dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n"); - return -EINVAL; - } - - ret = mei_write_message(dev, &hdr, dev->wd_data); - if (ret) { - dev_err(dev->dev, "wd: write message failed\n"); - return ret; - } - - ret = mei_cl_flow_ctrl_reduce(cl); - if (ret) { - dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n"); - return ret; - } - - return 0; -} - -/** - * mei_wd_stop - sends watchdog stop message to fw. - * - * @dev: the device structure - * - * Return: 0 if success - * on error: - * -EIO when message send fails - * -EINVAL when invalid message is to be sent - * -ETIME on message timeout - */ -int mei_wd_stop(struct mei_device *dev) -{ - struct mei_cl *cl = &dev->wd_cl; - int ret; - - if (!mei_cl_is_connected(cl) || - dev->wd_state != MEI_WD_RUNNING) - return 0; - - memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE); - - dev->wd_state = MEI_WD_STOPPING; - - ret = mei_cl_flow_ctrl_creds(cl, NULL); - if (ret < 0) - goto err; - - if (ret && mei_hbuf_acquire(dev)) { - ret = mei_wd_send(dev); - if (ret) - goto err; - dev->wd_pending = false; - } else { - dev->wd_pending = true; - } - - mutex_unlock(&dev->device_lock); - - ret = wait_event_timeout(dev->wait_stop_wd, - dev->wd_state == MEI_WD_IDLE, - msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); - mutex_lock(&dev->device_lock); - if (dev->wd_state != MEI_WD_IDLE) { - /* timeout */ - ret = -ETIME; - dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret); - goto err; - } - dev_dbg(dev->dev, "wd: stop completed after %u msec\n", - MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret)); - return 0; -err: - return ret; -} - -/** - * mei_wd_ops_start - wd start command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_start(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - struct mei_cl *cl; - int err = -ENODEV; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - cl = &dev->wd_cl; - - mutex_lock(&dev->device_lock); - - if (dev->dev_state != MEI_DEV_ENABLED) { - dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", - mei_dev_state_str(dev->dev_state)); - goto end_unlock; - } - - if (!mei_cl_is_connected(cl)) { - cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n"); - goto end_unlock; - } - - mei_wd_set_start_timeout(dev, dev->wd_timeout); - - err = 0; -end_unlock: - mutex_unlock(&dev->device_lock); - return err; -} - -/** - * mei_wd_ops_stop - wd stop command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_stop(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - mutex_lock(&dev->device_lock); - mei_wd_stop(dev); - mutex_unlock(&dev->device_lock); - - return 0; -} - -/** - * mei_wd_ops_ping - wd ping command from the watchdog core. - * - * @wd_dev: watchdog device struct - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_ping(struct watchdog_device *wd_dev) -{ - struct mei_device *dev; - struct mei_cl *cl; - int ret; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - cl = &dev->wd_cl; - - mutex_lock(&dev->device_lock); - - if (!mei_cl_is_connected(cl)) { - cl_err(dev, cl, "wd: not connected.\n"); - ret = -ENODEV; - goto end; - } - - dev->wd_state = MEI_WD_RUNNING; - - ret = mei_cl_flow_ctrl_creds(cl, NULL); - if (ret < 0) - goto end; - - /* Check if we can send the ping to HW*/ - if (ret && mei_hbuf_acquire(dev)) { - dev_dbg(dev->dev, "wd: sending ping\n"); - - ret = mei_wd_send(dev); - if (ret) - goto end; - dev->wd_pending = false; - } else { - dev->wd_pending = true; - } - -end: - mutex_unlock(&dev->device_lock); - return ret; -} - -/** - * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core. - * - * @wd_dev: watchdog device struct - * @timeout: timeout value to set - * - * Return: 0 if success, negative errno code for failure - */ -static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, - unsigned int timeout) -{ - struct mei_device *dev; - - dev = watchdog_get_drvdata(wd_dev); - if (!dev) - return -ENODEV; - - /* Check Timeout value */ - if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT) - return -EINVAL; - - mutex_lock(&dev->device_lock); - - dev->wd_timeout = timeout; - wd_dev->timeout = timeout; - mei_wd_set_start_timeout(dev, dev->wd_timeout); - - mutex_unlock(&dev->device_lock); - - return 0; -} - -/* - * Watchdog Device structs - */ -static const struct watchdog_ops wd_ops = { - .owner = THIS_MODULE, - .start = mei_wd_ops_start, - .stop = mei_wd_ops_stop, - .ping = mei_wd_ops_ping, - .set_timeout = mei_wd_ops_set_timeout, -}; -static const struct watchdog_info wd_info = { - .identity = INTEL_AMT_WATCHDOG_ID, - .options = WDIOF_KEEPALIVEPING | - WDIOF_SETTIMEOUT | - WDIOF_ALARMONLY, -}; - -static struct watchdog_device amt_wd_dev = { - .info = &wd_info, - .ops = &wd_ops, - .timeout = MEI_WD_DEFAULT_TIMEOUT, - .min_timeout = MEI_WD_MIN_TIMEOUT, - .max_timeout = MEI_WD_MAX_TIMEOUT, -}; - - -int mei_watchdog_register(struct mei_device *dev) -{ - - int ret; - - amt_wd_dev.parent = dev->dev; - /* unlock to perserve correct locking order */ - mutex_unlock(&dev->device_lock); - ret = watchdog_register_device(&amt_wd_dev); - mutex_lock(&dev->device_lock); - if (ret) { - dev_err(dev->dev, "wd: unable to register watchdog device = %d.\n", - ret); - return ret; - } - - dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n"); - watchdog_set_drvdata(&amt_wd_dev, dev); - return 0; -} - -void mei_watchdog_unregister(struct mei_device *dev) -{ - if (watchdog_get_drvdata(&amt_wd_dev) == NULL) - return; - - watchdog_set_drvdata(&amt_wd_dev, NULL); - watchdog_unregister_device(&amt_wd_dev); -} - -- cgit v1.2.3 From ef39830c35838696013a2f6c16f6728c4a9dcefd Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:11 -0800 Subject: misc: mic: Remove MIC X100 host virtio functionality This patch deletes the virtio functionality from the MIC X100 host driver. A subsequent patch will re-enable this functionality by consolidating the hardware independent logic in a new Virtio over PCIe (VOP) driver. Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/Makefile | 2 - drivers/misc/mic/host/mic_boot.c | 2 - drivers/misc/mic/host/mic_debugfs.c | 190 --------- drivers/misc/mic/host/mic_device.h | 6 - drivers/misc/mic/host/mic_fops.c | 222 ---------- drivers/misc/mic/host/mic_fops.h | 32 -- drivers/misc/mic/host/mic_main.c | 48 +-- drivers/misc/mic/host/mic_virtio.c | 811 ------------------------------------ drivers/misc/mic/host/mic_virtio.h | 155 ------- 9 files changed, 4 insertions(+), 1464 deletions(-) delete mode 100644 drivers/misc/mic/host/mic_fops.c delete mode 100644 drivers/misc/mic/host/mic_fops.h delete mode 100644 drivers/misc/mic/host/mic_virtio.c delete mode 100644 drivers/misc/mic/host/mic_virtio.h (limited to 'drivers/misc') diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile index 004d3db0f990..f3b502333ded 100644 --- a/drivers/misc/mic/host/Makefile +++ b/drivers/misc/mic/host/Makefile @@ -9,5 +9,3 @@ mic_host-objs += mic_smpt.o mic_host-objs += mic_intr.o mic_host-objs += mic_boot.o mic_host-objs += mic_debugfs.o -mic_host-objs += mic_fops.o -mic_host-objs += mic_virtio.o diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index 7845564dff64..3df305f6282c 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -28,7 +28,6 @@ #include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" -#include "mic_virtio.h" static inline struct mic_device *scdev_to_mdev(struct scif_hw_dev *scdev) { @@ -423,7 +422,6 @@ static void _mic_stop(struct cosm_device *cdev, bool force) * will be the first to be registered and the last to be * unregistered. */ - mic_virtio_reset_devices(mdev); scif_unregister_device(mdev->scdev); mic_free_dma_chans(mdev); mbus_unregister_device(mdev->dma_mbdev); diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c index 10581600777a..0a9daba8bb5d 100644 --- a/drivers/misc/mic/host/mic_debugfs.c +++ b/drivers/misc/mic/host/mic_debugfs.c @@ -26,7 +26,6 @@ #include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" -#include "mic_virtio.h" /* Debugfs parent dir */ static struct dentry *mic_dbg; @@ -100,190 +99,6 @@ static const struct file_operations post_code_ops = { .release = mic_post_code_debug_release }; -static int mic_dp_show(struct seq_file *s, void *pos) -{ - struct mic_device *mdev = s->private; - struct mic_device_desc *d; - struct mic_device_ctrl *dc; - struct mic_vqconfig *vqconfig; - __u32 *features; - __u8 *config; - struct mic_bootparam *bootparam = mdev->dp; - int i, j; - - seq_printf(s, "Bootparam: magic 0x%x\n", - bootparam->magic); - seq_printf(s, "Bootparam: h2c_config_db %d\n", - bootparam->h2c_config_db); - seq_printf(s, "Bootparam: node_id %d\n", - bootparam->node_id); - seq_printf(s, "Bootparam: c2h_scif_db %d\n", - bootparam->c2h_scif_db); - seq_printf(s, "Bootparam: h2c_scif_db %d\n", - bootparam->h2c_scif_db); - seq_printf(s, "Bootparam: scif_host_dma_addr 0x%llx\n", - bootparam->scif_host_dma_addr); - seq_printf(s, "Bootparam: scif_card_dma_addr 0x%llx\n", - bootparam->scif_card_dma_addr); - - - for (i = sizeof(*bootparam); i < MIC_DP_SIZE; - i += mic_total_desc_size(d)) { - d = mdev->dp + i; - dc = (void *)d + mic_aligned_desc_size(d); - - /* end of list */ - if (d->type == 0) - break; - - if (d->type == -1) - continue; - - seq_printf(s, "Type %d ", d->type); - seq_printf(s, "Num VQ %d ", d->num_vq); - seq_printf(s, "Feature Len %d\n", d->feature_len); - seq_printf(s, "Config Len %d ", d->config_len); - seq_printf(s, "Shutdown Status %d\n", d->status); - - for (j = 0; j < d->num_vq; j++) { - vqconfig = mic_vq_config(d) + j; - seq_printf(s, "vqconfig[%d]: ", j); - seq_printf(s, "address 0x%llx ", vqconfig->address); - seq_printf(s, "num %d ", vqconfig->num); - seq_printf(s, "used address 0x%llx\n", - vqconfig->used_address); - } - - features = (__u32 *)mic_vq_features(d); - seq_printf(s, "Features: Host 0x%x ", features[0]); - seq_printf(s, "Guest 0x%x\n", features[1]); - - config = mic_vq_configspace(d); - for (j = 0; j < d->config_len; j++) - seq_printf(s, "config[%d]=%d\n", j, config[j]); - - seq_puts(s, "Device control:\n"); - seq_printf(s, "Config Change %d ", dc->config_change); - seq_printf(s, "Vdev reset %d\n", dc->vdev_reset); - seq_printf(s, "Guest Ack %d ", dc->guest_ack); - seq_printf(s, "Host ack %d\n", dc->host_ack); - seq_printf(s, "Used address updated %d ", - dc->used_address_updated); - seq_printf(s, "Vdev 0x%llx\n", dc->vdev); - seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); - seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); - } - - return 0; -} - -static int mic_dp_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, mic_dp_show, inode->i_private); -} - -static int mic_dp_debug_release(struct inode *inode, struct file *file) -{ - return single_release(inode, file); -} - -static const struct file_operations dp_ops = { - .owner = THIS_MODULE, - .open = mic_dp_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = mic_dp_debug_release -}; - -static int mic_vdev_info_show(struct seq_file *s, void *unused) -{ - struct mic_device *mdev = s->private; - struct list_head *pos, *tmp; - struct mic_vdev *mvdev; - int i, j; - - mutex_lock(&mdev->mic_mutex); - list_for_each_safe(pos, tmp, &mdev->vdev_list) { - mvdev = list_entry(pos, struct mic_vdev, list); - seq_printf(s, "VDEV type %d state %s in %ld out %ld\n", - mvdev->virtio_id, - mic_vdevup(mvdev) ? "UP" : "DOWN", - mvdev->in_bytes, - mvdev->out_bytes); - for (i = 0; i < MIC_MAX_VRINGS; i++) { - struct vring_desc *desc; - struct vring_avail *avail; - struct vring_used *used; - struct mic_vringh *mvr = &mvdev->mvr[i]; - struct vringh *vrh = &mvr->vrh; - int num = vrh->vring.num; - if (!num) - continue; - desc = vrh->vring.desc; - seq_printf(s, "vring i %d avail_idx %d", - i, mvr->vring.info->avail_idx & (num - 1)); - seq_printf(s, " vring i %d avail_idx %d\n", - i, mvr->vring.info->avail_idx); - seq_printf(s, "vrh i %d weak_barriers %d", - i, vrh->weak_barriers); - seq_printf(s, " last_avail_idx %d last_used_idx %d", - vrh->last_avail_idx, vrh->last_used_idx); - seq_printf(s, " completed %d\n", vrh->completed); - for (j = 0; j < num; j++) { - seq_printf(s, "desc[%d] addr 0x%llx len %d", - j, desc->addr, desc->len); - seq_printf(s, " flags 0x%x next %d\n", - desc->flags, desc->next); - desc++; - } - avail = vrh->vring.avail; - seq_printf(s, "avail flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, avail->flags), - vringh16_to_cpu(vrh, avail->idx) & (num - 1)); - seq_printf(s, "avail flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, avail->flags), - vringh16_to_cpu(vrh, avail->idx)); - for (j = 0; j < num; j++) - seq_printf(s, "avail ring[%d] %d\n", - j, avail->ring[j]); - used = vrh->vring.used; - seq_printf(s, "used flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, used->flags), - vringh16_to_cpu(vrh, used->idx) & (num - 1)); - seq_printf(s, "used flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, used->flags), - vringh16_to_cpu(vrh, used->idx)); - for (j = 0; j < num; j++) - seq_printf(s, "used ring[%d] id %d len %d\n", - j, vringh32_to_cpu(vrh, - used->ring[j].id), - vringh32_to_cpu(vrh, - used->ring[j].len)); - } - } - mutex_unlock(&mdev->mic_mutex); - - return 0; -} - -static int mic_vdev_info_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, mic_vdev_info_show, inode->i_private); -} - -static int mic_vdev_info_debug_release(struct inode *inode, struct file *file) -{ - return single_release(inode, file); -} - -static const struct file_operations vdev_info_ops = { - .owner = THIS_MODULE, - .open = mic_vdev_info_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = mic_vdev_info_debug_release -}; - static int mic_msi_irq_info_show(struct seq_file *s, void *pos) { struct mic_device *mdev = s->private; @@ -367,11 +182,6 @@ void mic_create_debug_dir(struct mic_device *mdev) debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev, &post_code_ops); - debugfs_create_file("dp", 0444, mdev->dbg_dir, mdev, &dp_ops); - - debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, mdev, - &vdev_info_ops); - debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev, &msi_irq_info_ops); } diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 461184a12fbb..8460de122929 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -64,9 +64,6 @@ extern struct cosm_hw_ops cosm_hw_ops; * @bootaddr: MIC boot address. * @dp: virtio device page * @dp_dma_addr: virtio device page DMA address. - * @name: name for the misc char device - * @miscdev: registered misc char device - * @vdev_list: list of virtio devices. * @dma_mbdev: MIC BUS DMA device. * @dma_ch - Array of DMA channels * @num_dma_ch - Number of DMA channels available @@ -91,9 +88,6 @@ struct mic_device { u32 bootaddr; void *dp; dma_addr_t dp_dma_addr; - char name[16]; - struct miscdevice miscdev; - struct list_head vdev_list; struct mbus_device *dma_mbdev; struct dma_chan *dma_ch[MIC_MAX_DMA_CHAN]; int num_dma_ch; diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c deleted file mode 100644 index 8cc1d90cd949..000000000000 --- a/drivers/misc/mic/host/mic_fops.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Intel MIC Host driver. - * - */ -#include -#include - -#include -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_fops.h" -#include "mic_virtio.h" - -int mic_open(struct inode *inode, struct file *f) -{ - struct mic_vdev *mvdev; - struct mic_device *mdev = container_of(f->private_data, - struct mic_device, miscdev); - - mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL); - if (!mvdev) - return -ENOMEM; - - init_waitqueue_head(&mvdev->waitq); - INIT_LIST_HEAD(&mvdev->list); - mvdev->mdev = mdev; - mvdev->virtio_id = -1; - - f->private_data = mvdev; - return 0; -} - -int mic_release(struct inode *inode, struct file *f) -{ - struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; - - if (-1 != mvdev->virtio_id) - mic_virtio_del_device(mvdev); - f->private_data = NULL; - kfree(mvdev); - return 0; -} - -long mic_ioctl(struct file *f, unsigned int cmd, unsigned long arg) -{ - struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; - void __user *argp = (void __user *)arg; - int ret; - - switch (cmd) { - case MIC_VIRTIO_ADD_DEVICE: - { - ret = mic_virtio_add_device(mvdev, argp); - if (ret < 0) { - dev_err(mic_dev(mvdev), - "%s %d errno ret %d\n", - __func__, __LINE__, ret); - return ret; - } - break; - } - case MIC_VIRTIO_COPY_DESC: - { - struct mic_copy_desc copy; - - ret = mic_vdev_inited(mvdev); - if (ret) - return ret; - - if (copy_from_user(©, argp, sizeof(copy))) - return -EFAULT; - - dev_dbg(mic_dev(mvdev), - "%s %d === iovcnt 0x%x vr_idx 0x%x update_used %d\n", - __func__, __LINE__, copy.iovcnt, copy.vr_idx, - copy.update_used); - - ret = mic_virtio_copy_desc(mvdev, ©); - if (ret < 0) { - dev_err(mic_dev(mvdev), - "%s %d errno ret %d\n", - __func__, __LINE__, ret); - return ret; - } - if (copy_to_user( - &((struct mic_copy_desc __user *)argp)->out_len, - ©.out_len, sizeof(copy.out_len))) { - dev_err(mic_dev(mvdev), "%s %d errno ret %d\n", - __func__, __LINE__, -EFAULT); - return -EFAULT; - } - break; - } - case MIC_VIRTIO_CONFIG_CHANGE: - { - ret = mic_vdev_inited(mvdev); - if (ret) - return ret; - - ret = mic_virtio_config_change(mvdev, argp); - if (ret < 0) { - dev_err(mic_dev(mvdev), - "%s %d errno ret %d\n", - __func__, __LINE__, ret); - return ret; - } - break; - } - default: - return -ENOIOCTLCMD; - }; - return 0; -} - -/* - * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and - * not when previously enqueued buffers may be available. This means that - * in the card->host (TX) path, when userspace is unblocked by poll it - * must drain all available descriptors or it can stall. - */ -unsigned int mic_poll(struct file *f, poll_table *wait) -{ - struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; - int mask = 0; - - poll_wait(f, &mvdev->waitq, wait); - - if (mic_vdev_inited(mvdev)) { - mask = POLLERR; - } else if (mvdev->poll_wake) { - mvdev->poll_wake = 0; - mask = POLLIN | POLLOUT; - } - - return mask; -} - -static inline int -mic_query_offset(struct mic_vdev *mvdev, unsigned long offset, - unsigned long *size, unsigned long *pa) -{ - struct mic_device *mdev = mvdev->mdev; - unsigned long start = MIC_DP_SIZE; - int i; - - /* - * MMAP interface is as follows: - * offset region - * 0x0 virtio device_page - * 0x1000 first vring - * 0x1000 + size of 1st vring second vring - * .... - */ - if (!offset) { - *pa = virt_to_phys(mdev->dp); - *size = MIC_DP_SIZE; - return 0; - } - - for (i = 0; i < mvdev->dd->num_vq; i++) { - struct mic_vringh *mvr = &mvdev->mvr[i]; - if (offset == start) { - *pa = virt_to_phys(mvr->vring.va); - *size = mvr->vring.len; - return 0; - } - start += mvr->vring.len; - } - return -1; -} - -/* - * Maps the device page and virtio rings to user space for readonly access. - */ -int -mic_mmap(struct file *f, struct vm_area_struct *vma) -{ - struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size; - int i, err; - - err = mic_vdev_inited(mvdev); - if (err) - return err; - - if (vma->vm_flags & VM_WRITE) - return -EACCES; - - while (size_rem) { - i = mic_query_offset(mvdev, offset, &size, &pa); - if (i < 0) - return -EINVAL; - err = remap_pfn_range(vma, vma->vm_start + offset, - pa >> PAGE_SHIFT, size, vma->vm_page_prot); - if (err) - return err; - dev_dbg(mic_dev(mvdev), - "%s %d type %d size 0x%lx off 0x%lx pa 0x%lx vma 0x%lx\n", - __func__, __LINE__, mvdev->virtio_id, size, offset, - pa, vma->vm_start + offset); - size_rem -= size; - offset += size; - } - return 0; -} diff --git a/drivers/misc/mic/host/mic_fops.h b/drivers/misc/mic/host/mic_fops.h deleted file mode 100644 index dc3893dff667..000000000000 --- a/drivers/misc/mic/host/mic_fops.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Intel MIC Host driver. - * - */ -#ifndef _MIC_FOPS_H_ -#define _MIC_FOPS_H_ - -int mic_open(struct inode *inode, struct file *filp); -int mic_release(struct inode *inode, struct file *filp); -ssize_t mic_read(struct file *filp, char __user *buf, - size_t count, loff_t *pos); -long mic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); -int mic_mmap(struct file *f, struct vm_area_struct *vma); -unsigned int mic_poll(struct file *f, poll_table *wait); - -#endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index 153894e7ed5b..400def2106e7 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -27,8 +27,6 @@ #include "mic_device.h" #include "mic_x100.h" #include "mic_smpt.h" -#include "mic_fops.h" -#include "mic_virtio.h" static const char mic_driver_name[] = "mic"; @@ -57,17 +55,6 @@ MODULE_DEVICE_TABLE(pci, mic_pci_tbl); /* ID allocator for MIC devices */ static struct ida g_mic_ida; -/* Base device node number for MIC devices */ -static dev_t g_mic_devno; - -static const struct file_operations mic_fops = { - .open = mic_open, - .release = mic_release, - .unlocked_ioctl = mic_ioctl, - .poll = mic_poll, - .mmap = mic_mmap, - .owner = THIS_MODULE, -}; /* Initialize the device page */ static int mic_dp_init(struct mic_device *mdev) @@ -169,7 +156,6 @@ mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) mic_ops_init(mdev); mutex_init(&mdev->mic_mutex); mdev->irq_info.next_avail_src = 0; - INIT_LIST_HEAD(&mdev->vdev_list); } /** @@ -259,30 +245,15 @@ static int mic_probe(struct pci_dev *pdev, goto smpt_uninit; } mic_bootparam_init(mdev); - mic_create_debug_dir(mdev); - mdev->miscdev.minor = MISC_DYNAMIC_MINOR; - snprintf(mdev->name, sizeof(mdev->name), "mic%d", mdev->id); - mdev->miscdev.name = mdev->name; - mdev->miscdev.fops = &mic_fops; - mdev->miscdev.parent = &mdev->pdev->dev; - rc = misc_register(&mdev->miscdev); - if (rc) { - dev_err(&pdev->dev, "misc_register err id %d rc %d\n", - mdev->id, rc); - goto cleanup_debug_dir; - } - mdev->cosm_dev = cosm_register_device(&mdev->pdev->dev, &cosm_hw_ops); if (IS_ERR(mdev->cosm_dev)) { rc = PTR_ERR(mdev->cosm_dev); dev_err(&pdev->dev, "cosm_add_device failed rc %d\n", rc); - goto misc_dereg; + goto cleanup_debug_dir; } return 0; -misc_dereg: - misc_deregister(&mdev->miscdev); cleanup_debug_dir: mic_delete_debug_dir(mdev); mic_dp_uninit(mdev); @@ -323,7 +294,6 @@ static void mic_remove(struct pci_dev *pdev) return; cosm_unregister_device(mdev->cosm_dev); - misc_deregister(&mdev->miscdev); mic_delete_debug_dir(mdev); mic_dp_uninit(mdev); mic_smpt_uninit(mdev); @@ -347,26 +317,17 @@ static int __init mic_init(void) { int ret; - ret = alloc_chrdev_region(&g_mic_devno, 0, - MIC_MAX_NUM_DEVS, mic_driver_name); - if (ret) { - pr_err("alloc_chrdev_region failed ret %d\n", ret); - goto error; - } - mic_init_debugfs(); ida_init(&g_mic_ida); ret = pci_register_driver(&mic_driver); if (ret) { pr_err("pci_register_driver failed ret %d\n", ret); - goto cleanup_chrdev; + goto cleanup_debugfs; } - return ret; -cleanup_chrdev: + return 0; +cleanup_debugfs: ida_destroy(&g_mic_ida); mic_exit_debugfs(); - unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); -error: return ret; } @@ -375,7 +336,6 @@ static void __exit mic_exit(void) pci_unregister_driver(&mic_driver); ida_destroy(&g_mic_ida); mic_exit_debugfs(); - unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); } module_init(mic_init); diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c deleted file mode 100644 index 58b107a24a8b..000000000000 --- a/drivers/misc/mic/host/mic_virtio.c +++ /dev/null @@ -1,811 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Intel MIC Host driver. - * - */ -#include -#include -#include -#include -#include -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_smpt.h" -#include "mic_virtio.h" - -/* - * Size of the internal buffer used during DMA's as an intermediate buffer - * for copy to/from user. - */ -#define MIC_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL) - -static int mic_sync_dma(struct mic_device *mdev, dma_addr_t dst, - dma_addr_t src, size_t len) -{ - int err = 0; - struct dma_async_tx_descriptor *tx; - struct dma_chan *mic_ch = mdev->dma_ch[0]; - - if (!mic_ch) { - err = -EBUSY; - goto error; - } - - tx = mic_ch->device->device_prep_dma_memcpy(mic_ch, dst, src, len, - DMA_PREP_FENCE); - if (!tx) { - err = -ENOMEM; - goto error; - } else { - dma_cookie_t cookie = tx->tx_submit(tx); - - err = dma_submit_error(cookie); - if (err) - goto error; - err = dma_sync_wait(mic_ch, cookie); - } -error: - if (err) - dev_err(&mdev->pdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; -} - -/* - * Initiates the copies across the PCIe bus from card memory to a user - * space buffer. When transfers are done using DMA, source/destination - * addresses and transfer length must follow the alignment requirements of - * the MIC DMA engine. - */ -static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, void __user *ubuf, - size_t len, u64 daddr, size_t dlen, - int vr_idx) -{ - struct mic_device *mdev = mvdev->mdev; - void __iomem *dbuf = mdev->aper.va + daddr; - struct mic_vringh *mvr = &mvdev->mvr[vr_idx]; - size_t dma_alignment = 1 << mdev->dma_ch[0]->device->copy_align; - size_t dma_offset; - size_t partlen; - int err; - - dma_offset = daddr - round_down(daddr, dma_alignment); - daddr -= dma_offset; - len += dma_offset; - - while (len) { - partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE); - - err = mic_sync_dma(mdev, mvr->buf_da, daddr, - ALIGN(partlen, dma_alignment)); - if (err) - goto err; - - if (copy_to_user(ubuf, mvr->buf + dma_offset, - partlen - dma_offset)) { - err = -EFAULT; - goto err; - } - daddr += partlen; - ubuf += partlen; - dbuf += partlen; - mvdev->in_bytes_dma += partlen; - mvdev->in_bytes += partlen; - len -= partlen; - dma_offset = 0; - } - return 0; -err: - dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); - return err; -} - -/* - * Initiates copies across the PCIe bus from a user space buffer to card - * memory. When transfers are done using DMA, source/destination addresses - * and transfer length must follow the alignment requirements of the MIC - * DMA engine. - */ -static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, void __user *ubuf, - size_t len, u64 daddr, size_t dlen, - int vr_idx) -{ - struct mic_device *mdev = mvdev->mdev; - void __iomem *dbuf = mdev->aper.va + daddr; - struct mic_vringh *mvr = &mvdev->mvr[vr_idx]; - size_t dma_alignment = 1 << mdev->dma_ch[0]->device->copy_align; - size_t partlen; - int err; - - if (daddr & (dma_alignment - 1)) { - mvdev->tx_dst_unaligned += len; - goto memcpy; - } else if (ALIGN(len, dma_alignment) > dlen) { - mvdev->tx_len_unaligned += len; - goto memcpy; - } - - while (len) { - partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE); - - if (copy_from_user(mvr->buf, ubuf, partlen)) { - err = -EFAULT; - goto err; - } - err = mic_sync_dma(mdev, daddr, mvr->buf_da, - ALIGN(partlen, dma_alignment)); - if (err) - goto err; - daddr += partlen; - ubuf += partlen; - dbuf += partlen; - mvdev->out_bytes_dma += partlen; - mvdev->out_bytes += partlen; - len -= partlen; - } -memcpy: - /* - * We are copying to IO below and should ideally use something - * like copy_from_user_toio(..) if it existed. - */ - if (copy_from_user((void __force *)dbuf, ubuf, len)) { - err = -EFAULT; - goto err; - } - mvdev->out_bytes += len; - return 0; -err: - dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); - return err; -} - -#define MIC_VRINGH_READ true - -/* The function to call to notify the card about added buffers */ -static void mic_notify(struct vringh *vrh) -{ - struct mic_vringh *mvrh = container_of(vrh, struct mic_vringh, vrh); - struct mic_vdev *mvdev = mvrh->mvdev; - s8 db = mvdev->dc->h2c_vdev_db; - - if (db != -1) - mvdev->mdev->ops->send_intr(mvdev->mdev, db); -} - -/* Determine the total number of bytes consumed in a VRINGH KIOV */ -static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov) -{ - int i; - u32 total = iov->consumed; - - for (i = 0; i < iov->i; i++) - total += iov->iov[i].iov_len; - return total; -} - -/* - * Traverse the VRINGH KIOV and issue the APIs to trigger the copies. - * This API is heavily based on the vringh_iov_xfer(..) implementation - * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..) - * and vringh_iov_push_kern(..) directly is because there is no - * way to override the VRINGH xfer(..) routines as of v3.10. - */ -static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, - void __user *ubuf, size_t len, bool read, int vr_idx, - size_t *out_len) -{ - int ret = 0; - size_t partlen, tot_len = 0; - - while (len && iov->i < iov->used) { - partlen = min(iov->iov[iov->i].iov_len, len); - if (read) - ret = mic_virtio_copy_to_user(mvdev, ubuf, partlen, - (u64)iov->iov[iov->i].iov_base, - iov->iov[iov->i].iov_len, - vr_idx); - else - ret = mic_virtio_copy_from_user(mvdev, ubuf, partlen, - (u64)iov->iov[iov->i].iov_base, - iov->iov[iov->i].iov_len, - vr_idx); - if (ret) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= partlen; - ubuf += partlen; - tot_len += partlen; - iov->consumed += partlen; - iov->iov[iov->i].iov_len -= partlen; - iov->iov[iov->i].iov_base += partlen; - if (!iov->iov[iov->i].iov_len) { - /* Fix up old iov element then increment. */ - iov->iov[iov->i].iov_len = iov->consumed; - iov->iov[iov->i].iov_base -= iov->consumed; - - iov->consumed = 0; - iov->i++; - } - } - *out_len = tot_len; - return ret; -} - -/* - * Use the standard VRINGH infrastructure in the kernel to fetch new - * descriptors, initiate the copies and update the used ring. - */ -static int _mic_virtio_copy(struct mic_vdev *mvdev, - struct mic_copy_desc *copy) -{ - int ret = 0; - u32 iovcnt = copy->iovcnt; - struct iovec iov; - struct iovec __user *u_iov = copy->iov; - void __user *ubuf = NULL; - struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; - struct vringh_kiov *riov = &mvr->riov; - struct vringh_kiov *wiov = &mvr->wiov; - struct vringh *vrh = &mvr->vrh; - u16 *head = &mvr->head; - struct mic_vring *vr = &mvr->vring; - size_t len = 0, out_len; - - copy->out_len = 0; - /* Fetch a new IOVEC if all previous elements have been processed */ - if (riov->i == riov->used && wiov->i == wiov->used) { - ret = vringh_getdesc_kern(vrh, riov, wiov, - head, GFP_KERNEL); - /* Check if there are available descriptors */ - if (ret <= 0) - return ret; - } - while (iovcnt) { - if (!len) { - /* Copy over a new iovec from user space. */ - ret = copy_from_user(&iov, u_iov, sizeof(*u_iov)); - if (ret) { - ret = -EINVAL; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len = iov.iov_len; - ubuf = iov.iov_base; - } - /* Issue all the read descriptors first */ - ret = mic_vringh_copy(mvdev, riov, ubuf, len, MIC_VRINGH_READ, - copy->vr_idx, &out_len); - if (ret) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= out_len; - ubuf += out_len; - copy->out_len += out_len; - /* Issue the write descriptors next */ - ret = mic_vringh_copy(mvdev, wiov, ubuf, len, !MIC_VRINGH_READ, - copy->vr_idx, &out_len); - if (ret) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= out_len; - ubuf += out_len; - copy->out_len += out_len; - if (!len) { - /* One user space iovec is now completed */ - iovcnt--; - u_iov++; - } - /* Exit loop if all elements in KIOVs have been processed. */ - if (riov->i == riov->used && wiov->i == wiov->used) - break; - } - /* - * Update the used ring if a descriptor was available and some data was - * copied in/out and the user asked for a used ring update. - */ - if (*head != USHRT_MAX && copy->out_len && copy->update_used) { - u32 total = 0; - - /* Determine the total data consumed */ - total += mic_vringh_iov_consumed(riov); - total += mic_vringh_iov_consumed(wiov); - vringh_complete_kern(vrh, *head, total); - *head = USHRT_MAX; - if (vringh_need_notify_kern(vrh) > 0) - vringh_notify(vrh); - vringh_kiov_cleanup(riov); - vringh_kiov_cleanup(wiov); - /* Update avail idx for user space */ - vr->info->avail_idx = vrh->last_avail_idx; - } - return ret; -} - -static inline int mic_verify_copy_args(struct mic_vdev *mvdev, - struct mic_copy_desc *copy) -{ - if (copy->vr_idx >= mvdev->dd->num_vq) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -EINVAL); - return -EINVAL; - } - return 0; -} - -/* Copy a specified number of virtio descriptors in a chain */ -int mic_virtio_copy_desc(struct mic_vdev *mvdev, - struct mic_copy_desc *copy) -{ - int err; - struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; - - err = mic_verify_copy_args(mvdev, copy); - if (err) - return err; - - mutex_lock(&mvr->vr_mutex); - if (!mic_vdevup(mvdev)) { - err = -ENODEV; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - err = _mic_virtio_copy(mvdev, copy); - if (err) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, err); - } -err: - mutex_unlock(&mvr->vr_mutex); - return err; -} - -static void mic_virtio_init_post(struct mic_vdev *mvdev) -{ - struct mic_vqconfig *vqconfig = mic_vq_config(mvdev->dd); - int i; - - for (i = 0; i < mvdev->dd->num_vq; i++) { - if (!le64_to_cpu(vqconfig[i].used_address)) { - dev_warn(mic_dev(mvdev), "used_address zero??\n"); - continue; - } - mvdev->mvr[i].vrh.vring.used = - (void __force *)mvdev->mdev->aper.va + - le64_to_cpu(vqconfig[i].used_address); - } - - mvdev->dc->used_address_updated = 0; - - dev_dbg(mic_dev(mvdev), "%s: device type %d LINKUP\n", - __func__, mvdev->virtio_id); -} - -static inline void mic_virtio_device_reset(struct mic_vdev *mvdev) -{ - int i; - - dev_dbg(mic_dev(mvdev), "%s: status %d device type %d RESET\n", - __func__, mvdev->dd->status, mvdev->virtio_id); - - for (i = 0; i < mvdev->dd->num_vq; i++) - /* - * Avoid lockdep false positive. The + 1 is for the mic - * mutex which is held in the reset devices code path. - */ - mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); - - /* 0 status means "reset" */ - mvdev->dd->status = 0; - mvdev->dc->vdev_reset = 0; - mvdev->dc->host_ack = 1; - - for (i = 0; i < mvdev->dd->num_vq; i++) { - struct vringh *vrh = &mvdev->mvr[i].vrh; - mvdev->mvr[i].vring.info->avail_idx = 0; - vrh->completed = 0; - vrh->last_avail_idx = 0; - vrh->last_used_idx = 0; - } - - for (i = 0; i < mvdev->dd->num_vq; i++) - mutex_unlock(&mvdev->mvr[i].vr_mutex); -} - -void mic_virtio_reset_devices(struct mic_device *mdev) -{ - struct list_head *pos, *tmp; - struct mic_vdev *mvdev; - - dev_dbg(&mdev->pdev->dev, "%s\n", __func__); - - list_for_each_safe(pos, tmp, &mdev->vdev_list) { - mvdev = list_entry(pos, struct mic_vdev, list); - mic_virtio_device_reset(mvdev); - mvdev->poll_wake = 1; - wake_up(&mvdev->waitq); - } -} - -void mic_bh_handler(struct work_struct *work) -{ - struct mic_vdev *mvdev = container_of(work, struct mic_vdev, - virtio_bh_work); - - if (mvdev->dc->used_address_updated) - mic_virtio_init_post(mvdev); - - if (mvdev->dc->vdev_reset) - mic_virtio_device_reset(mvdev); - - mvdev->poll_wake = 1; - wake_up(&mvdev->waitq); -} - -static irqreturn_t mic_virtio_intr_handler(int irq, void *data) -{ - struct mic_vdev *mvdev = data; - struct mic_device *mdev = mvdev->mdev; - - mdev->ops->intr_workarounds(mdev); - schedule_work(&mvdev->virtio_bh_work); - return IRQ_HANDLED; -} - -int mic_virtio_config_change(struct mic_vdev *mvdev, - void __user *argp) -{ - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); - int ret = 0, retry, i; - struct mic_bootparam *bootparam = mvdev->mdev->dp; - s8 db = bootparam->h2c_config_db; - - mutex_lock(&mvdev->mdev->mic_mutex); - for (i = 0; i < mvdev->dd->num_vq; i++) - mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); - - if (db == -1 || mvdev->dd->type == -1) { - ret = -EIO; - goto exit; - } - - if (copy_from_user(mic_vq_configspace(mvdev->dd), - argp, mvdev->dd->config_len)) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -EFAULT); - ret = -EFAULT; - goto exit; - } - mvdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED; - mvdev->mdev->ops->send_intr(mvdev->mdev, db); - - for (retry = 100; retry--;) { - ret = wait_event_timeout(wake, - mvdev->dc->guest_ack, msecs_to_jiffies(100)); - if (ret) - break; - } - - dev_dbg(mic_dev(mvdev), - "%s %d retry: %d\n", __func__, __LINE__, retry); - mvdev->dc->config_change = 0; - mvdev->dc->guest_ack = 0; -exit: - for (i = 0; i < mvdev->dd->num_vq; i++) - mutex_unlock(&mvdev->mvr[i].vr_mutex); - mutex_unlock(&mvdev->mdev->mic_mutex); - return ret; -} - -static int mic_copy_dp_entry(struct mic_vdev *mvdev, - void __user *argp, - __u8 *type, - struct mic_device_desc **devpage) -{ - struct mic_device *mdev = mvdev->mdev; - struct mic_device_desc dd, *dd_config, *devp; - struct mic_vqconfig *vqconfig; - int ret = 0, i; - bool slot_found = false; - - if (copy_from_user(&dd, argp, sizeof(dd))) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -EFAULT); - return -EFAULT; - } - - if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE || - dd.num_vq > MIC_MAX_VRINGS) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -EINVAL); - return -EINVAL; - } - - dd_config = kmalloc(mic_desc_size(&dd), GFP_KERNEL); - if (dd_config == NULL) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -ENOMEM); - return -ENOMEM; - } - if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) { - ret = -EFAULT; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto exit; - } - - vqconfig = mic_vq_config(dd_config); - for (i = 0; i < dd.num_vq; i++) { - if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) { - ret = -EINVAL; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto exit; - } - } - - /* Find the first free device page entry */ - for (i = sizeof(struct mic_bootparam); - i < MIC_DP_SIZE - mic_total_desc_size(dd_config); - i += mic_total_desc_size(devp)) { - devp = mdev->dp + i; - if (devp->type == 0 || devp->type == -1) { - slot_found = true; - break; - } - } - if (!slot_found) { - ret = -EINVAL; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto exit; - } - /* - * Save off the type before doing the memcpy. Type will be set in the - * end after completing all initialization for the new device. - */ - *type = dd_config->type; - dd_config->type = 0; - memcpy(devp, dd_config, mic_desc_size(dd_config)); - - *devpage = devp; -exit: - kfree(dd_config); - return ret; -} - -static void mic_init_device_ctrl(struct mic_vdev *mvdev, - struct mic_device_desc *devpage) -{ - struct mic_device_ctrl *dc; - - dc = (void *)devpage + mic_aligned_desc_size(devpage); - - dc->config_change = 0; - dc->guest_ack = 0; - dc->vdev_reset = 0; - dc->host_ack = 0; - dc->used_address_updated = 0; - dc->c2h_vdev_db = -1; - dc->h2c_vdev_db = -1; - mvdev->dc = dc; -} - -int mic_virtio_add_device(struct mic_vdev *mvdev, - void __user *argp) -{ - struct mic_device *mdev = mvdev->mdev; - struct mic_device_desc *dd = NULL; - struct mic_vqconfig *vqconfig; - int vr_size, i, j, ret; - u8 type = 0; - s8 db; - char irqname[10]; - struct mic_bootparam *bootparam = mdev->dp; - u16 num; - dma_addr_t vr_addr; - - mutex_lock(&mdev->mic_mutex); - - ret = mic_copy_dp_entry(mvdev, argp, &type, &dd); - if (ret) { - mutex_unlock(&mdev->mic_mutex); - return ret; - } - - mic_init_device_ctrl(mvdev, dd); - - mvdev->dd = dd; - mvdev->virtio_id = type; - vqconfig = mic_vq_config(dd); - INIT_WORK(&mvdev->virtio_bh_work, mic_bh_handler); - - for (i = 0; i < dd->num_vq; i++) { - struct mic_vringh *mvr = &mvdev->mvr[i]; - struct mic_vring *vr = &mvdev->mvr[i].vring; - num = le16_to_cpu(vqconfig[i].num); - mutex_init(&mvr->vr_mutex); - vr_size = PAGE_ALIGN(vring_size(num, MIC_VIRTIO_RING_ALIGN) + - sizeof(struct _mic_vring_info)); - vr->va = (void *) - __get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(vr_size)); - if (!vr->va) { - ret = -ENOMEM; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vr->len = vr_size; - vr->info = vr->va + vring_size(num, MIC_VIRTIO_RING_ALIGN); - vr->info->magic = cpu_to_le32(MIC_MAGIC + mvdev->virtio_id + i); - vr_addr = mic_map_single(mdev, vr->va, vr_size); - if (mic_map_error(vr_addr)) { - free_pages((unsigned long)vr->va, get_order(vr_size)); - ret = -ENOMEM; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vqconfig[i].address = cpu_to_le64(vr_addr); - - vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN); - ret = vringh_init_kern(&mvr->vrh, - *(u32 *)mic_vq_features(mvdev->dd), num, false, - vr->vr.desc, vr->vr.avail, vr->vr.used); - if (ret) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vringh_kiov_init(&mvr->riov, NULL, 0); - vringh_kiov_init(&mvr->wiov, NULL, 0); - mvr->head = USHRT_MAX; - mvr->mvdev = mvdev; - mvr->vrh.notify = mic_notify; - dev_dbg(&mdev->pdev->dev, - "%s %d index %d va %p info %p vr_size 0x%x\n", - __func__, __LINE__, i, vr->va, vr->info, vr_size); - mvr->buf = (void *)__get_free_pages(GFP_KERNEL, - get_order(MIC_INT_DMA_BUF_SIZE)); - mvr->buf_da = mic_map_single(mvdev->mdev, mvr->buf, - MIC_INT_DMA_BUF_SIZE); - } - - snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id, - mvdev->virtio_id); - mvdev->virtio_db = mic_next_db(mdev); - mvdev->virtio_cookie = mic_request_threaded_irq(mdev, - mic_virtio_intr_handler, - NULL, irqname, mvdev, - mvdev->virtio_db, MIC_INTR_DB); - if (IS_ERR(mvdev->virtio_cookie)) { - ret = PTR_ERR(mvdev->virtio_cookie); - dev_dbg(&mdev->pdev->dev, "request irq failed\n"); - goto err; - } - - mvdev->dc->c2h_vdev_db = mvdev->virtio_db; - - list_add_tail(&mvdev->list, &mdev->vdev_list); - /* - * Order the type update with previous stores. This write barrier - * is paired with the corresponding read barrier before the uncached - * system memory read of the type, on the card while scanning the - * device page. - */ - smp_wmb(); - dd->type = type; - - dev_dbg(&mdev->pdev->dev, "Added virtio device id %d\n", dd->type); - - db = bootparam->h2c_config_db; - if (db != -1) - mdev->ops->send_intr(mdev, db); - mutex_unlock(&mdev->mic_mutex); - return 0; -err: - vqconfig = mic_vq_config(dd); - for (j = 0; j < i; j++) { - struct mic_vringh *mvr = &mvdev->mvr[j]; - mic_unmap_single(mdev, le64_to_cpu(vqconfig[j].address), - mvr->vring.len); - free_pages((unsigned long)mvr->vring.va, - get_order(mvr->vring.len)); - } - mutex_unlock(&mdev->mic_mutex); - return ret; -} - -void mic_virtio_del_device(struct mic_vdev *mvdev) -{ - struct list_head *pos, *tmp; - struct mic_vdev *tmp_mvdev; - struct mic_device *mdev = mvdev->mdev; - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); - int i, ret, retry; - struct mic_vqconfig *vqconfig; - struct mic_bootparam *bootparam = mdev->dp; - s8 db; - - mutex_lock(&mdev->mic_mutex); - db = bootparam->h2c_config_db; - if (db == -1) - goto skip_hot_remove; - dev_dbg(&mdev->pdev->dev, - "Requesting hot remove id %d\n", mvdev->virtio_id); - mvdev->dc->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE; - mdev->ops->send_intr(mdev, db); - for (retry = 100; retry--;) { - ret = wait_event_timeout(wake, - mvdev->dc->guest_ack, msecs_to_jiffies(100)); - if (ret) - break; - } - dev_dbg(&mdev->pdev->dev, - "Device id %d config_change %d guest_ack %d retry %d\n", - mvdev->virtio_id, mvdev->dc->config_change, - mvdev->dc->guest_ack, retry); - mvdev->dc->config_change = 0; - mvdev->dc->guest_ack = 0; -skip_hot_remove: - mic_free_irq(mdev, mvdev->virtio_cookie, mvdev); - flush_work(&mvdev->virtio_bh_work); - vqconfig = mic_vq_config(mvdev->dd); - for (i = 0; i < mvdev->dd->num_vq; i++) { - struct mic_vringh *mvr = &mvdev->mvr[i]; - - mic_unmap_single(mvdev->mdev, mvr->buf_da, - MIC_INT_DMA_BUF_SIZE); - free_pages((unsigned long)mvr->buf, - get_order(MIC_INT_DMA_BUF_SIZE)); - vringh_kiov_cleanup(&mvr->riov); - vringh_kiov_cleanup(&mvr->wiov); - mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), - mvr->vring.len); - free_pages((unsigned long)mvr->vring.va, - get_order(mvr->vring.len)); - } - - list_for_each_safe(pos, tmp, &mdev->vdev_list) { - tmp_mvdev = list_entry(pos, struct mic_vdev, list); - if (tmp_mvdev == mvdev) { - list_del(pos); - dev_dbg(&mdev->pdev->dev, - "Removing virtio device id %d\n", - mvdev->virtio_id); - break; - } - } - /* - * Order the type update with previous stores. This write barrier - * is paired with the corresponding read barrier before the uncached - * system memory read of the type, on the card while scanning the - * device page. - */ - smp_wmb(); - mvdev->dd->type = -1; - mutex_unlock(&mdev->mic_mutex); -} diff --git a/drivers/misc/mic/host/mic_virtio.h b/drivers/misc/mic/host/mic_virtio.h deleted file mode 100644 index a80631f2790d..000000000000 --- a/drivers/misc/mic/host/mic_virtio.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Intel MIC Host driver. - * - */ -#ifndef MIC_VIRTIO_H -#define MIC_VIRTIO_H - -#include -#include - -/* - * Note on endianness. - * 1. Host can be both BE or LE - * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail - * rings and ioreadXX/iowriteXX to access used ring. - * 3. Device page exposed by host to guest contains LE values. Guest - * accesses these using ioreadXX/iowriteXX etc. This way in general we - * obey the virtio spec according to which guest works with native - * endianness and host is aware of guest endianness and does all - * required endianness conversion. - * 4. Data provided from user space to guest (in ADD_DEVICE and - * CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be - * in guest endianness. - */ - -/** - * struct mic_vringh - Virtio ring host information. - * - * @vring: The MIC vring used for setting up user space mappings. - * @vrh: The host VRINGH used for accessing the card vrings. - * @riov: The VRINGH read kernel IOV. - * @wiov: The VRINGH write kernel IOV. - * @vr_mutex: Mutex for synchronizing access to the VRING. - * @buf: Temporary kernel buffer used to copy in/out data - * from/to the card via DMA. - * @buf_da: dma address of buf. - * @mvdev: Back pointer to MIC virtio device for vringh_notify(..). - * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). - */ -struct mic_vringh { - struct mic_vring vring; - struct vringh vrh; - struct vringh_kiov riov; - struct vringh_kiov wiov; - struct mutex vr_mutex; - void *buf; - dma_addr_t buf_da; - struct mic_vdev *mvdev; - u16 head; -}; - -/** - * struct mic_vdev - Host information for a card Virtio device. - * - * @virtio_id - Virtio device id. - * @waitq - Waitqueue to allow ring3 apps to poll. - * @mdev - Back pointer to host MIC device. - * @poll_wake - Used for waking up threads blocked in poll. - * @out_bytes - Debug stats for number of bytes copied from host to card. - * @in_bytes - Debug stats for number of bytes copied from card to host. - * @out_bytes_dma - Debug stats for number of bytes copied from host to card - * using DMA. - * @in_bytes_dma - Debug stats for number of bytes copied from card to host - * using DMA. - * @tx_len_unaligned - Debug stats for number of bytes copied to the card where - * the transfer length did not have the required DMA alignment. - * @tx_dst_unaligned - Debug stats for number of bytes copied where the - * destination address on the card did not have the required DMA alignment. - * @mvr - Store per VRING data structures. - * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. - * @dd - Virtio device descriptor. - * @dc - Virtio device control fields. - * @list - List of Virtio devices. - * @virtio_db - The doorbell used by the card to interrupt the host. - * @virtio_cookie - The cookie returned while requesting interrupts. - */ -struct mic_vdev { - int virtio_id; - wait_queue_head_t waitq; - struct mic_device *mdev; - int poll_wake; - unsigned long out_bytes; - unsigned long in_bytes; - unsigned long out_bytes_dma; - unsigned long in_bytes_dma; - unsigned long tx_len_unaligned; - unsigned long tx_dst_unaligned; - struct mic_vringh mvr[MIC_MAX_VRINGS]; - struct work_struct virtio_bh_work; - struct mic_device_desc *dd; - struct mic_device_ctrl *dc; - struct list_head list; - int virtio_db; - struct mic_irq *virtio_cookie; -}; - -void mic_virtio_uninit(struct mic_device *mdev); -int mic_virtio_add_device(struct mic_vdev *mvdev, - void __user *argp); -void mic_virtio_del_device(struct mic_vdev *mvdev); -int mic_virtio_config_change(struct mic_vdev *mvdev, - void __user *argp); -int mic_virtio_copy_desc(struct mic_vdev *mvdev, - struct mic_copy_desc *request); -void mic_virtio_reset_devices(struct mic_device *mdev); -void mic_bh_handler(struct work_struct *work); - -/* Helper API to obtain the MIC PCIe device */ -static inline struct device *mic_dev(struct mic_vdev *mvdev) -{ - return &mvdev->mdev->pdev->dev; -} - -/* Helper API to check if a virtio device is initialized */ -static inline int mic_vdev_inited(struct mic_vdev *mvdev) -{ - /* Device has not been created yet */ - if (!mvdev->dd || !mvdev->dd->type) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -EINVAL); - return -EINVAL; - } - - /* Device has been removed/deleted */ - if (mvdev->dd->type == -1) { - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, -ENODEV); - return -ENODEV; - } - - return 0; -} - -/* Helper API to check if a virtio device is running */ -static inline bool mic_vdevup(struct mic_vdev *mvdev) -{ - return !!mvdev->dd->status; -} -#endif -- cgit v1.2.3 From b73c295833cc660c1acdf2601920e2abdb6a29c8 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:12 -0800 Subject: misc: mic: Remove MIC X100 card virtio functionality This patch deletes the virtio functionality from the MIC X100 card driver. A subsequent patch will re-enable this functionality by consolidating the hardware independent logic in a new Virtio over PCIe (VOP) driver. Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/card/Makefile | 1 - drivers/misc/mic/card/mic_device.c | 9 +- drivers/misc/mic/card/mic_virtio.c | 634 ------------------------------------- drivers/misc/mic/card/mic_virtio.h | 76 ----- 4 files changed, 1 insertion(+), 719 deletions(-) delete mode 100644 drivers/misc/mic/card/mic_virtio.c delete mode 100644 drivers/misc/mic/card/mic_virtio.h (limited to 'drivers/misc') diff --git a/drivers/misc/mic/card/Makefile b/drivers/misc/mic/card/Makefile index 69d58bef92ce..6e9675e12a09 100644 --- a/drivers/misc/mic/card/Makefile +++ b/drivers/misc/mic/card/Makefile @@ -8,4 +8,3 @@ obj-$(CONFIG_INTEL_MIC_CARD) += mic_card.o mic_card-y += mic_x100.o mic_card-y += mic_device.o mic_card-y += mic_debugfs.o -mic_card-y += mic_virtio.o diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index d0edaf7e0cd5..ff03c633541c 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c @@ -34,7 +34,6 @@ #include #include "../common/mic_dev.h" #include "mic_device.h" -#include "mic_virtio.h" static struct mic_driver *g_drv; @@ -309,9 +308,6 @@ int __init mic_driver_init(struct mic_driver *mdrv) rc = -ENODEV; goto irq_uninit; } - rc = mic_devices_init(mdrv); - if (rc) - goto dma_free; bootparam = mdrv->dp; node_id = ioread8(&bootparam->node_id); mdrv->scdev = scif_register_device(mdrv->dev, MIC_SCIF_DEV, @@ -321,13 +317,11 @@ int __init mic_driver_init(struct mic_driver *mdrv) mdrv->num_dma_ch, true); if (IS_ERR(mdrv->scdev)) { rc = PTR_ERR(mdrv->scdev); - goto device_uninit; + goto dma_free; } mic_create_card_debug_dir(mdrv); done: return rc; -device_uninit: - mic_devices_uninit(mdrv); dma_free: mic_free_dma_chans(mdrv); irq_uninit: @@ -348,7 +342,6 @@ void mic_driver_uninit(struct mic_driver *mdrv) { mic_delete_card_debug_dir(mdrv); scif_unregister_device(mdrv->scdev); - mic_devices_uninit(mdrv); mic_free_dma_chans(mdrv); mic_uninit_irq(); mic_dp_uninit(); diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c deleted file mode 100644 index 17764b2fbee5..000000000000 --- a/drivers/misc/mic/card/mic_virtio.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Adapted from: - * - * virtio for kvm on s390 - * - * Copyright IBM Corp. 2008 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (version 2 only) - * as published by the Free Software Foundation. - * - * Author(s): Christian Borntraeger - * - * Intel MIC Card driver. - * - */ -#include -#include -#include - -#include "../common/mic_dev.h" -#include "mic_virtio.h" - -#define VIRTIO_SUBCODE_64 0x0D00 - -#define MIC_MAX_VRINGS 4 -struct mic_vdev { - struct virtio_device vdev; - struct mic_device_desc __iomem *desc; - struct mic_device_ctrl __iomem *dc; - struct mic_device *mdev; - void __iomem *vr[MIC_MAX_VRINGS]; - int used_size[MIC_MAX_VRINGS]; - struct completion reset_done; - struct mic_irq *virtio_cookie; - int c2h_vdev_db; -}; - -static struct mic_irq *virtio_config_cookie; -#define to_micvdev(vd) container_of(vd, struct mic_vdev, vdev) - -/* Helper API to obtain the parent of the virtio device */ -static inline struct device *mic_dev(struct mic_vdev *mvdev) -{ - return mvdev->vdev.dev.parent; -} - -/* This gets the device's feature bits. */ -static u64 mic_get_features(struct virtio_device *vdev) -{ - unsigned int i, bits; - u32 features = 0; - struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; - u8 __iomem *in_features = mic_vq_features(desc); - int feature_len = ioread8(&desc->feature_len); - - bits = min_t(unsigned, feature_len, sizeof(features)) * 8; - for (i = 0; i < bits; i++) - if (ioread8(&in_features[i / 8]) & (BIT(i % 8))) - features |= BIT(i); - - return features; -} - -static int mic_finalize_features(struct virtio_device *vdev) -{ - unsigned int i, bits; - struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; - u8 feature_len = ioread8(&desc->feature_len); - /* Second half of bitmap is features we accept. */ - u8 __iomem *out_features = - mic_vq_features(desc) + feature_len; - - /* Give virtio_ring a chance to accept features. */ - vring_transport_features(vdev); - - /* Make sure we don't have any features > 32 bits! */ - BUG_ON((u32)vdev->features != vdev->features); - - memset_io(out_features, 0, feature_len); - bits = min_t(unsigned, feature_len, - sizeof(vdev->features)) * 8; - for (i = 0; i < bits; i++) { - if (__virtio_test_bit(vdev, i)) - iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)), - &out_features[i / 8]); - } - - return 0; -} - -/* - * Reading and writing elements in config space - */ -static void mic_get(struct virtio_device *vdev, unsigned int offset, - void *buf, unsigned len) -{ - struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; - - if (offset + len > ioread8(&desc->config_len)) - return; - memcpy_fromio(buf, mic_vq_configspace(desc) + offset, len); -} - -static void mic_set(struct virtio_device *vdev, unsigned int offset, - const void *buf, unsigned len) -{ - struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; - - if (offset + len > ioread8(&desc->config_len)) - return; - memcpy_toio(mic_vq_configspace(desc) + offset, buf, len); -} - -/* - * The operations to get and set the status word just access the status - * field of the device descriptor. set_status also interrupts the host - * to tell about status changes. - */ -static u8 mic_get_status(struct virtio_device *vdev) -{ - return ioread8(&to_micvdev(vdev)->desc->status); -} - -static void mic_set_status(struct virtio_device *vdev, u8 status) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - if (!status) - return; - iowrite8(status, &mvdev->desc->status); - mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); -} - -/* Inform host on a virtio device reset and wait for ack from host */ -static void mic_reset_inform_host(struct virtio_device *vdev) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - struct mic_device_ctrl __iomem *dc = mvdev->dc; - int retry; - - iowrite8(0, &dc->host_ack); - iowrite8(1, &dc->vdev_reset); - mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); - - /* Wait till host completes all card accesses and acks the reset */ - for (retry = 100; retry--;) { - if (ioread8(&dc->host_ack)) - break; - msleep(100); - }; - - dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry); - - /* Reset status to 0 in case we timed out */ - iowrite8(0, &mvdev->desc->status); -} - -static void mic_reset(struct virtio_device *vdev) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - - dev_dbg(mic_dev(mvdev), "%s: virtio id %d\n", - __func__, vdev->id.device); - - mic_reset_inform_host(vdev); - complete_all(&mvdev->reset_done); -} - -/* - * The virtio_ring code calls this API when it wants to notify the Host. - */ -static bool mic_notify(struct virtqueue *vq) -{ - struct mic_vdev *mvdev = vq->priv; - - mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); - return true; -} - -static void mic_del_vq(struct virtqueue *vq, int n) -{ - struct mic_vdev *mvdev = to_micvdev(vq->vdev); - struct vring *vr = (struct vring *)(vq + 1); - - free_pages((unsigned long) vr->used, get_order(mvdev->used_size[n])); - vring_del_virtqueue(vq); - mic_card_unmap(mvdev->mdev, mvdev->vr[n]); - mvdev->vr[n] = NULL; -} - -static void mic_del_vqs(struct virtio_device *vdev) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - struct virtqueue *vq, *n; - int idx = 0; - - dev_dbg(mic_dev(mvdev), "%s\n", __func__); - - list_for_each_entry_safe(vq, n, &vdev->vqs, list) - mic_del_vq(vq, idx++); -} - -/* - * This routine will assign vring's allocated in host/io memory. Code in - * virtio_ring.c however continues to access this io memory as if it were local - * memory without io accessors. - */ -static struct virtqueue *mic_find_vq(struct virtio_device *vdev, - unsigned index, - void (*callback)(struct virtqueue *vq), - const char *name) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - struct mic_vqconfig __iomem *vqconfig; - struct mic_vqconfig config; - struct virtqueue *vq; - void __iomem *va; - struct _mic_vring_info __iomem *info; - void *used; - int vr_size, _vr_size, err, magic; - struct vring *vr; - u8 type = ioread8(&mvdev->desc->type); - - if (index >= ioread8(&mvdev->desc->num_vq)) - return ERR_PTR(-ENOENT); - - if (!name) - return ERR_PTR(-ENOENT); - - /* First assign the vring's allocated in host memory */ - vqconfig = mic_vq_config(mvdev->desc) + index; - memcpy_fromio(&config, vqconfig, sizeof(config)); - _vr_size = vring_size(le16_to_cpu(config.num), MIC_VIRTIO_RING_ALIGN); - vr_size = PAGE_ALIGN(_vr_size + sizeof(struct _mic_vring_info)); - va = mic_card_map(mvdev->mdev, le64_to_cpu(config.address), vr_size); - if (!va) - return ERR_PTR(-ENOMEM); - mvdev->vr[index] = va; - memset_io(va, 0x0, _vr_size); - vq = vring_new_virtqueue(index, le16_to_cpu(config.num), - MIC_VIRTIO_RING_ALIGN, vdev, false, - (void __force *)va, mic_notify, callback, - name); - if (!vq) { - err = -ENOMEM; - goto unmap; - } - info = va + _vr_size; - magic = ioread32(&info->magic); - - if (WARN(magic != MIC_MAGIC + type + index, "magic mismatch")) { - err = -EIO; - goto unmap; - } - - /* Allocate and reassign used ring now */ - mvdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 + - sizeof(struct vring_used_elem) * - le16_to_cpu(config.num)); - used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(mvdev->used_size[index])); - if (!used) { - err = -ENOMEM; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto del_vq; - } - iowrite64(virt_to_phys(used), &vqconfig->used_address); - - /* - * To reassign the used ring here we are directly accessing - * struct vring_virtqueue which is a private data structure - * in virtio_ring.c. At the minimum, a BUILD_BUG_ON() in - * vring_new_virtqueue() would ensure that - * (&vq->vring == (struct vring *) (&vq->vq + 1)); - */ - vr = (struct vring *)(vq + 1); - vr->used = used; - - vq->priv = mvdev; - return vq; -del_vq: - vring_del_virtqueue(vq); -unmap: - mic_card_unmap(mvdev->mdev, mvdev->vr[index]); - return ERR_PTR(err); -} - -static int mic_find_vqs(struct virtio_device *vdev, unsigned nvqs, - struct virtqueue *vqs[], - vq_callback_t *callbacks[], - const char * const names[]) -{ - struct mic_vdev *mvdev = to_micvdev(vdev); - struct mic_device_ctrl __iomem *dc = mvdev->dc; - int i, err, retry; - - /* We must have this many virtqueues. */ - if (nvqs > ioread8(&mvdev->desc->num_vq)) - return -ENOENT; - - for (i = 0; i < nvqs; ++i) { - dev_dbg(mic_dev(mvdev), "%s: %d: %s\n", - __func__, i, names[i]); - vqs[i] = mic_find_vq(vdev, i, callbacks[i], names[i]); - if (IS_ERR(vqs[i])) { - err = PTR_ERR(vqs[i]); - goto error; - } - } - - iowrite8(1, &dc->used_address_updated); - /* - * Send an interrupt to the host to inform it that used - * rings have been re-assigned. - */ - mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); - for (retry = 100; --retry;) { - if (!ioread8(&dc->used_address_updated)) - break; - msleep(100); - }; - - dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry); - if (!retry) { - err = -ENODEV; - goto error; - } - - return 0; -error: - mic_del_vqs(vdev); - return err; -} - -/* - * The config ops structure as defined by virtio config - */ -static struct virtio_config_ops mic_vq_config_ops = { - .get_features = mic_get_features, - .finalize_features = mic_finalize_features, - .get = mic_get, - .set = mic_set, - .get_status = mic_get_status, - .set_status = mic_set_status, - .reset = mic_reset, - .find_vqs = mic_find_vqs, - .del_vqs = mic_del_vqs, -}; - -static irqreturn_t -mic_virtio_intr_handler(int irq, void *data) -{ - struct mic_vdev *mvdev = data; - struct virtqueue *vq; - - mic_ack_interrupt(mvdev->mdev); - list_for_each_entry(vq, &mvdev->vdev.vqs, list) - vring_interrupt(0, vq); - - return IRQ_HANDLED; -} - -static void mic_virtio_release_dev(struct device *_d) -{ - /* - * No need for a release method similar to virtio PCI. - * Provide an empty one to avoid getting a warning from core. - */ -} - -/* - * adds a new device and register it with virtio - * appropriate drivers are loaded by the device model - */ -static int mic_add_device(struct mic_device_desc __iomem *d, - unsigned int offset, struct mic_driver *mdrv) -{ - struct mic_vdev *mvdev; - int ret; - int virtio_db; - u8 type = ioread8(&d->type); - - mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL); - if (!mvdev) { - dev_err(mdrv->dev, "Cannot allocate mic dev %u type %u\n", - offset, type); - return -ENOMEM; - } - - mvdev->mdev = &mdrv->mdev; - mvdev->vdev.dev.parent = mdrv->dev; - mvdev->vdev.dev.release = mic_virtio_release_dev; - mvdev->vdev.id.device = type; - mvdev->vdev.config = &mic_vq_config_ops; - mvdev->desc = d; - mvdev->dc = (void __iomem *)d + mic_aligned_desc_size(d); - init_completion(&mvdev->reset_done); - - virtio_db = mic_next_card_db(); - mvdev->virtio_cookie = mic_request_card_irq(mic_virtio_intr_handler, - NULL, "virtio intr", mvdev, virtio_db); - if (IS_ERR(mvdev->virtio_cookie)) { - ret = PTR_ERR(mvdev->virtio_cookie); - goto kfree; - } - iowrite8((u8)virtio_db, &mvdev->dc->h2c_vdev_db); - mvdev->c2h_vdev_db = ioread8(&mvdev->dc->c2h_vdev_db); - - ret = register_virtio_device(&mvdev->vdev); - if (ret) { - dev_err(mic_dev(mvdev), - "Failed to register mic device %u type %u\n", - offset, type); - goto free_irq; - } - iowrite64((u64)mvdev, &mvdev->dc->vdev); - dev_dbg(mic_dev(mvdev), "%s: registered mic device %u type %u mvdev %p\n", - __func__, offset, type, mvdev); - - return 0; - -free_irq: - mic_free_card_irq(mvdev->virtio_cookie, mvdev); -kfree: - kfree(mvdev); - return ret; -} - -/* - * match for a mic device with a specific desc pointer - */ -static int mic_match_desc(struct device *dev, void *data) -{ - struct virtio_device *vdev = dev_to_virtio(dev); - struct mic_vdev *mvdev = to_micvdev(vdev); - - return mvdev->desc == (void __iomem *)data; -} - -static void mic_handle_config_change(struct mic_device_desc __iomem *d, - unsigned int offset, struct mic_driver *mdrv) -{ - struct mic_device_ctrl __iomem *dc - = (void __iomem *)d + mic_aligned_desc_size(d); - struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev); - - if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED) - return; - - dev_dbg(mdrv->dev, "%s %d\n", __func__, __LINE__); - virtio_config_changed(&mvdev->vdev); - iowrite8(1, &dc->guest_ack); -} - -/* - * removes a virtio device if a hot remove event has been - * requested by the host. - */ -static int mic_remove_device(struct mic_device_desc __iomem *d, - unsigned int offset, struct mic_driver *mdrv) -{ - struct mic_device_ctrl __iomem *dc - = (void __iomem *)d + mic_aligned_desc_size(d); - struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev); - u8 status; - int ret = -1; - - if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) { - dev_dbg(mdrv->dev, - "%s %d config_change %d type %d mvdev %p\n", - __func__, __LINE__, - ioread8(&dc->config_change), ioread8(&d->type), mvdev); - - status = ioread8(&d->status); - reinit_completion(&mvdev->reset_done); - unregister_virtio_device(&mvdev->vdev); - mic_free_card_irq(mvdev->virtio_cookie, mvdev); - if (status & VIRTIO_CONFIG_S_DRIVER_OK) - wait_for_completion(&mvdev->reset_done); - kfree(mvdev); - iowrite8(1, &dc->guest_ack); - dev_dbg(mdrv->dev, "%s %d guest_ack %d\n", - __func__, __LINE__, ioread8(&dc->guest_ack)); - ret = 0; - } - - return ret; -} - -#define REMOVE_DEVICES true - -static void mic_scan_devices(struct mic_driver *mdrv, bool remove) -{ - s8 type; - unsigned int i; - struct mic_device_desc __iomem *d; - struct mic_device_ctrl __iomem *dc; - struct device *dev; - int ret; - - for (i = sizeof(struct mic_bootparam); i < MIC_DP_SIZE; - i += mic_total_desc_size(d)) { - d = mdrv->dp + i; - dc = (void __iomem *)d + mic_aligned_desc_size(d); - /* - * This read barrier is paired with the corresponding write - * barrier on the host which is inserted before adding or - * removing a virtio device descriptor, by updating the type. - */ - rmb(); - type = ioread8(&d->type); - - /* end of list */ - if (type == 0) - break; - - if (type == -1) - continue; - - /* device already exists */ - dev = device_find_child(mdrv->dev, (void __force *)d, - mic_match_desc); - if (dev) { - if (remove) - iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE, - &dc->config_change); - put_device(dev); - mic_handle_config_change(d, i, mdrv); - ret = mic_remove_device(d, i, mdrv); - if (!ret && !remove) - iowrite8(-1, &d->type); - if (remove) { - iowrite8(0, &dc->config_change); - iowrite8(0, &dc->guest_ack); - } - continue; - } - - /* new device */ - dev_dbg(mdrv->dev, "%s %d Adding new virtio device %p\n", - __func__, __LINE__, d); - if (!remove) - mic_add_device(d, i, mdrv); - } -} - -/* - * mic_hotplug_device tries to find changes in the device page. - */ -static void mic_hotplug_devices(struct work_struct *work) -{ - struct mic_driver *mdrv = container_of(work, - struct mic_driver, hotplug_work); - - mic_scan_devices(mdrv, !REMOVE_DEVICES); -} - -/* - * Interrupt handler for hot plug/config changes etc. - */ -static irqreturn_t -mic_extint_handler(int irq, void *data) -{ - struct mic_driver *mdrv = (struct mic_driver *)data; - - dev_dbg(mdrv->dev, "%s %d hotplug work\n", - __func__, __LINE__); - mic_ack_interrupt(&mdrv->mdev); - schedule_work(&mdrv->hotplug_work); - return IRQ_HANDLED; -} - -/* - * Init function for virtio - */ -int mic_devices_init(struct mic_driver *mdrv) -{ - int rc; - struct mic_bootparam __iomem *bootparam; - int config_db; - - INIT_WORK(&mdrv->hotplug_work, mic_hotplug_devices); - mic_scan_devices(mdrv, !REMOVE_DEVICES); - - config_db = mic_next_card_db(); - virtio_config_cookie = mic_request_card_irq(mic_extint_handler, NULL, - "virtio_config_intr", mdrv, - config_db); - if (IS_ERR(virtio_config_cookie)) { - rc = PTR_ERR(virtio_config_cookie); - goto exit; - } - - bootparam = mdrv->dp; - iowrite8(config_db, &bootparam->h2c_config_db); - return 0; -exit: - return rc; -} - -/* - * Uninit function for virtio - */ -void mic_devices_uninit(struct mic_driver *mdrv) -{ - struct mic_bootparam __iomem *bootparam = mdrv->dp; - iowrite8(-1, &bootparam->h2c_config_db); - mic_free_card_irq(virtio_config_cookie, mdrv); - flush_work(&mdrv->hotplug_work); - mic_scan_devices(mdrv, REMOVE_DEVICES); -} diff --git a/drivers/misc/mic/card/mic_virtio.h b/drivers/misc/mic/card/mic_virtio.h deleted file mode 100644 index d0407ba53bb7..000000000000 --- a/drivers/misc/mic/card/mic_virtio.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - * - */ -#ifndef __MIC_CARD_VIRTIO_H -#define __MIC_CARD_VIRTIO_H - -#include -#include "mic_device.h" - -/* - * 64 bit I/O access - */ -#ifndef ioread64 -#define ioread64 readq -#endif -#ifndef iowrite64 -#define iowrite64 writeq -#endif - -static inline unsigned mic_desc_size(struct mic_device_desc __iomem *desc) -{ - return sizeof(*desc) - + ioread8(&desc->num_vq) * sizeof(struct mic_vqconfig) - + ioread8(&desc->feature_len) * 2 - + ioread8(&desc->config_len); -} - -static inline struct mic_vqconfig __iomem * -mic_vq_config(struct mic_device_desc __iomem *desc) -{ - return (struct mic_vqconfig __iomem *)(desc + 1); -} - -static inline __u8 __iomem * -mic_vq_features(struct mic_device_desc __iomem *desc) -{ - return (__u8 __iomem *)(mic_vq_config(desc) + ioread8(&desc->num_vq)); -} - -static inline __u8 __iomem * -mic_vq_configspace(struct mic_device_desc __iomem *desc) -{ - return mic_vq_features(desc) + ioread8(&desc->feature_len) * 2; -} -static inline unsigned mic_total_desc_size(struct mic_device_desc __iomem *desc) -{ - return mic_aligned_desc_size(desc) + sizeof(struct mic_device_ctrl); -} - -int mic_devices_init(struct mic_driver *mdrv); -void mic_devices_uninit(struct mic_driver *mdrv); - -#endif -- cgit v1.2.3 From a19ddd6fd260d6b92de38acb1b87940045697627 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:13 -0800 Subject: misc: mic: MIC VOP Bus The Virtio Over PCIe (VOP) bus abstracts the low level hardware details like interrupts and mapping remote memory so that the same VOP driver can work without changes with different MIC host or card drivers as long as the hardware bus operations are implemented. The VOP driver registers itself on the VOP bus. The base PCIe drivers implement the bus ops and register VOP devices on the bus, resulting in the VOP driver being probed with the VOP devices. This allows the VOP functionality to be shared between multiple generations of Intel MIC products. Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/Kconfig | 17 ++++ drivers/misc/mic/bus/Makefile | 1 + drivers/misc/mic/bus/vop_bus.c | 203 +++++++++++++++++++++++++++++++++++++++++ drivers/misc/mic/bus/vop_bus.h | 140 ++++++++++++++++++++++++++++ 4 files changed, 361 insertions(+) create mode 100644 drivers/misc/mic/bus/vop_bus.c create mode 100644 drivers/misc/mic/bus/vop_bus.h (limited to 'drivers/misc') diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 40677df7f996..840f7ef1a141 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -32,6 +32,23 @@ config SCIF_BUS OS and tools for MIC to use with this driver are available from . +comment "VOP Bus Driver" + +config VOP_BUS + tristate "VOP Bus Driver" + depends on 64BIT && PCI && X86 && X86_DEV_DMA_OPS + help + This option is selected by any driver which registers a + device or driver on the VOP Bus, such as CONFIG_INTEL_MIC_HOST + and CONFIG_INTEL_MIC_CARD. + + If you are building a host/card kernel with an Intel MIC device + then say M (recommended) or Y, else say N. If unsure say N. + + More information about the Intel MIC family as well as the Linux + OS and tools for MIC to use with this driver are available from + . + comment "Intel MIC Host Driver" config INTEL_MIC_HOST diff --git a/drivers/misc/mic/bus/Makefile b/drivers/misc/mic/bus/Makefile index 761842b0d0bb..8758a7daa52c 100644 --- a/drivers/misc/mic/bus/Makefile +++ b/drivers/misc/mic/bus/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_INTEL_MIC_BUS) += mic_bus.o obj-$(CONFIG_SCIF_BUS) += scif_bus.o obj-$(CONFIG_MIC_COSM) += cosm_bus.o +obj-$(CONFIG_VOP_BUS) += vop_bus.o diff --git a/drivers/misc/mic/bus/vop_bus.c b/drivers/misc/mic/bus/vop_bus.c new file mode 100644 index 000000000000..303da222f5b6 --- /dev/null +++ b/drivers/misc/mic/bus/vop_bus.c @@ -0,0 +1,203 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel Virtio Over PCIe (VOP) Bus driver. + */ +#include +#include +#include +#include + +#include "vop_bus.h" + +static ssize_t device_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct vop_device *dev = dev_to_vop(d); + + return sprintf(buf, "0x%04x\n", dev->id.device); +} +static DEVICE_ATTR_RO(device); + +static ssize_t vendor_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct vop_device *dev = dev_to_vop(d); + + return sprintf(buf, "0x%04x\n", dev->id.vendor); +} +static DEVICE_ATTR_RO(vendor); + +static ssize_t modalias_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct vop_device *dev = dev_to_vop(d); + + return sprintf(buf, "vop:d%08Xv%08X\n", + dev->id.device, dev->id.vendor); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *vop_dev_attrs[] = { + &dev_attr_device.attr, + &dev_attr_vendor.attr, + &dev_attr_modalias.attr, + NULL, +}; +ATTRIBUTE_GROUPS(vop_dev); + +static inline int vop_id_match(const struct vop_device *dev, + const struct vop_device_id *id) +{ + if (id->device != dev->id.device && id->device != VOP_DEV_ANY_ID) + return 0; + + return id->vendor == VOP_DEV_ANY_ID || id->vendor == dev->id.vendor; +} + +/* + * This looks through all the IDs a driver claims to support. If any of them + * match, we return 1 and the kernel will call vop_dev_probe(). + */ +static int vop_dev_match(struct device *dv, struct device_driver *dr) +{ + unsigned int i; + struct vop_device *dev = dev_to_vop(dv); + const struct vop_device_id *ids; + + ids = drv_to_vop(dr)->id_table; + for (i = 0; ids[i].device; i++) + if (vop_id_match(dev, &ids[i])) + return 1; + return 0; +} + +static int vop_uevent(struct device *dv, struct kobj_uevent_env *env) +{ + struct vop_device *dev = dev_to_vop(dv); + + return add_uevent_var(env, "MODALIAS=vop:d%08Xv%08X", + dev->id.device, dev->id.vendor); +} + +static int vop_dev_probe(struct device *d) +{ + struct vop_device *dev = dev_to_vop(d); + struct vop_driver *drv = drv_to_vop(dev->dev.driver); + + return drv->probe(dev); +} + +static int vop_dev_remove(struct device *d) +{ + struct vop_device *dev = dev_to_vop(d); + struct vop_driver *drv = drv_to_vop(dev->dev.driver); + + drv->remove(dev); + return 0; +} + +static struct bus_type vop_bus = { + .name = "vop_bus", + .match = vop_dev_match, + .dev_groups = vop_dev_groups, + .uevent = vop_uevent, + .probe = vop_dev_probe, + .remove = vop_dev_remove, +}; + +int vop_register_driver(struct vop_driver *driver) +{ + driver->driver.bus = &vop_bus; + return driver_register(&driver->driver); +} +EXPORT_SYMBOL_GPL(vop_register_driver); + +void vop_unregister_driver(struct vop_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_GPL(vop_unregister_driver); + +static void vop_release_dev(struct device *d) +{ + put_device(d); +} + +struct vop_device * +vop_register_device(struct device *pdev, int id, + const struct dma_map_ops *dma_ops, + struct vop_hw_ops *hw_ops, u8 dnode, struct mic_mw *aper, + struct dma_chan *chan) +{ + int ret; + struct vop_device *vdev; + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return ERR_PTR(-ENOMEM); + + vdev->dev.parent = pdev; + vdev->id.device = id; + vdev->id.vendor = VOP_DEV_ANY_ID; + vdev->dev.archdata.dma_ops = (struct dma_map_ops *)dma_ops; + vdev->dev.dma_mask = &vdev->dev.coherent_dma_mask; + dma_set_mask(&vdev->dev, DMA_BIT_MASK(64)); + vdev->dev.release = vop_release_dev; + vdev->hw_ops = hw_ops; + vdev->dev.bus = &vop_bus; + vdev->dnode = dnode; + vdev->aper = aper; + vdev->dma_ch = chan; + vdev->index = dnode - 1; + dev_set_name(&vdev->dev, "vop-dev%u", vdev->index); + /* + * device_register() causes the bus infrastructure to look for a + * matching driver. + */ + ret = device_register(&vdev->dev); + if (ret) + goto free_vdev; + return vdev; +free_vdev: + kfree(vdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(vop_register_device); + +void vop_unregister_device(struct vop_device *dev) +{ + device_unregister(&dev->dev); +} +EXPORT_SYMBOL_GPL(vop_unregister_device); + +static int __init vop_init(void) +{ + return bus_register(&vop_bus); +} + +static void __exit vop_exit(void) +{ + bus_unregister(&vop_bus); +} + +core_initcall(vop_init); +module_exit(vop_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) VOP Bus driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/bus/vop_bus.h b/drivers/misc/mic/bus/vop_bus.h new file mode 100644 index 000000000000..fff7a865d721 --- /dev/null +++ b/drivers/misc/mic/bus/vop_bus.h @@ -0,0 +1,140 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel Virtio over PCIe Bus driver. + */ +#ifndef _VOP_BUS_H_ +#define _VOP_BUS_H_ +/* + * Everything a vop driver needs to work with any particular vop + * implementation. + */ +#include +#include + +#include "../common/mic_dev.h" + +struct vop_device_id { + u32 device; + u32 vendor; +}; + +#define VOP_DEV_TRNSP 1 +#define VOP_DEV_ANY_ID 0xffffffff +/* + * Size of the internal buffer used during DMA's as an intermediate buffer + * for copy to/from user. Must be an integral number of pages. + */ +#define VOP_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL) + +/** + * vop_device - representation of a device using vop + * @hw_ops: the hardware ops supported by this device. + * @id: the device type identification (used to match it with a driver). + * @dev: underlying device. + * @dnode - The destination node which this device will communicate with. + * @aper: Aperture memory window + * @dma_ch - DMA channel + * @index: unique position on the vop bus + */ +struct vop_device { + struct vop_hw_ops *hw_ops; + struct vop_device_id id; + struct device dev; + u8 dnode; + struct mic_mw *aper; + struct dma_chan *dma_ch; + int index; +}; + +/** + * vop_driver - operations for a vop I/O driver + * @driver: underlying device driver (populate name and owner). + * @id_table: the ids serviced by this driver. + * @probe: the function to call when a device is found. Returns 0 or -errno. + * @remove: the function to call when a device is removed. + */ +struct vop_driver { + struct device_driver driver; + const struct vop_device_id *id_table; + int (*probe)(struct vop_device *dev); + void (*remove)(struct vop_device *dev); +}; + +/** + * vop_hw_ops - Hardware operations for accessing a VOP device on the VOP bus. + * + * @next_db: Obtain the next available doorbell. + * @request_irq: Request an interrupt on a particular doorbell. + * @free_irq: Free an interrupt requested previously. + * @ack_interrupt: acknowledge an interrupt in the ISR. + * @get_remote_dp: Get access to the virtio device page used by the remote + * node to add/remove/configure virtio devices. + * @get_dp: Get access to the virtio device page used by the self + * node to add/remove/configure virtio devices. + * @send_intr: Send an interrupt to the peer node on a specified doorbell. + * @ioremap: Map a buffer with the specified DMA address and length. + * @iounmap: Unmap a buffer previously mapped. + * @dma_filter: The DMA filter function to use for obtaining access to + * a DMA channel on the peer node. + */ +struct vop_hw_ops { + int (*next_db)(struct vop_device *vpdev); + struct mic_irq *(*request_irq)(struct vop_device *vpdev, + irqreturn_t (*func)(int irq, void *data), + const char *name, void *data, + int intr_src); + void (*free_irq)(struct vop_device *vpdev, + struct mic_irq *cookie, void *data); + void (*ack_interrupt)(struct vop_device *vpdev, int num); + void __iomem * (*get_remote_dp)(struct vop_device *vpdev); + void * (*get_dp)(struct vop_device *vpdev); + void (*send_intr)(struct vop_device *vpdev, int db); + void __iomem * (*ioremap)(struct vop_device *vpdev, + dma_addr_t pa, size_t len); + void (*iounmap)(struct vop_device *vpdev, void __iomem *va); +}; + +struct vop_device * +vop_register_device(struct device *pdev, int id, + const struct dma_map_ops *dma_ops, + struct vop_hw_ops *hw_ops, u8 dnode, struct mic_mw *aper, + struct dma_chan *chan); +void vop_unregister_device(struct vop_device *dev); +int vop_register_driver(struct vop_driver *drv); +void vop_unregister_driver(struct vop_driver *drv); + +/* + * module_vop_driver() - Helper macro for drivers that don't do + * anything special in module init/exit. This eliminates a lot of + * boilerplate. Each module may only use this macro once, and + * calling it replaces module_init() and module_exit() + */ +#define module_vop_driver(__vop_driver) \ + module_driver(__vop_driver, vop_register_driver, \ + vop_unregister_driver) + +static inline struct vop_device *dev_to_vop(struct device *dev) +{ + return container_of(dev, struct vop_device, dev); +} + +static inline struct vop_driver *drv_to_vop(struct device_driver *drv) +{ + return container_of(drv, struct vop_driver, driver); +} +#endif /* _VOP_BUS_H */ -- cgit v1.2.3 From 26909e26f4b51a9f7f8838eee775a65563388c0d Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:14 -0800 Subject: misc: mic: Add data structures for the VOP driver This patch adds VOP driver data structures used in subsequent patches. These data structures are refactored from similar data structures used in the virtio parts of previous MIC host and card drivers. Signed-off-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_main.h | 170 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 drivers/misc/mic/vop/vop_main.h (limited to 'drivers/misc') diff --git a/drivers/misc/mic/vop/vop_main.h b/drivers/misc/mic/vop/vop_main.h new file mode 100644 index 000000000000..ba47ec7a6386 --- /dev/null +++ b/drivers/misc/mic/vop/vop_main.h @@ -0,0 +1,170 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel Virtio Over PCIe (VOP) driver. + * + */ +#ifndef _VOP_MAIN_H_ +#define _VOP_MAIN_H_ + +#include +#include +#include +#include + +#include +#include "../common/mic_dev.h" + +#include "../bus/vop_bus.h" + +/* + * Note on endianness. + * 1. Host can be both BE or LE + * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail + * rings and ioreadXX/iowriteXX to access used ring. + * 3. Device page exposed by host to guest contains LE values. Guest + * accesses these using ioreadXX/iowriteXX etc. This way in general we + * obey the virtio spec according to which guest works with native + * endianness and host is aware of guest endianness and does all + * required endianness conversion. + * 4. Data provided from user space to guest (in ADD_DEVICE and + * CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be + * in guest endianness. + */ + +/* + * vop_info - Allocated per invocation of VOP probe + * + * @vpdev: VOP device + * @hotplug_work: Handle virtio device creation, deletion and configuration + * @cookie: Cookie received upon requesting a virtio configuration interrupt + * @h2c_config_db: The doorbell used by the peer to indicate a config change + * @vdev_list: List of "active" virtio devices injected in the peer node + * @vop_mutex: Synchronize access to the device page as well as serialize + * creation/deletion of virtio devices on the peer node + * @dp: Peer device page information + * @dbg: Debugfs entry + * @dma_ch: The DMA channel used by this transport for data transfers. + * @name: Name for this transport used in misc device creation. + * @miscdev: The misc device registered. + */ +struct vop_info { + struct vop_device *vpdev; + struct work_struct hotplug_work; + struct mic_irq *cookie; + int h2c_config_db; + struct list_head vdev_list; + struct mutex vop_mutex; + void __iomem *dp; + struct dentry *dbg; + struct dma_chan *dma_ch; + char name[16]; + struct miscdevice miscdev; +}; + +/** + * struct vop_vringh - Virtio ring host information. + * + * @vring: The VOP vring used for setting up user space mappings. + * @vrh: The host VRINGH used for accessing the card vrings. + * @riov: The VRINGH read kernel IOV. + * @wiov: The VRINGH write kernel IOV. + * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). + * @vr_mutex: Mutex for synchronizing access to the VRING. + * @buf: Temporary kernel buffer used to copy in/out data + * from/to the card via DMA. + * @buf_da: dma address of buf. + * @vdev: Back pointer to VOP virtio device for vringh_notify(..). + */ +struct vop_vringh { + struct mic_vring vring; + struct vringh vrh; + struct vringh_kiov riov; + struct vringh_kiov wiov; + u16 head; + struct mutex vr_mutex; + void *buf; + dma_addr_t buf_da; + struct vop_vdev *vdev; +}; + +/** + * struct vop_vdev - Host information for a card Virtio device. + * + * @virtio_id - Virtio device id. + * @waitq - Waitqueue to allow ring3 apps to poll. + * @vpdev - pointer to VOP bus device. + * @poll_wake - Used for waking up threads blocked in poll. + * @out_bytes - Debug stats for number of bytes copied from host to card. + * @in_bytes - Debug stats for number of bytes copied from card to host. + * @out_bytes_dma - Debug stats for number of bytes copied from host to card + * using DMA. + * @in_bytes_dma - Debug stats for number of bytes copied from card to host + * using DMA. + * @tx_len_unaligned - Debug stats for number of bytes copied to the card where + * the transfer length did not have the required DMA alignment. + * @tx_dst_unaligned - Debug stats for number of bytes copied where the + * destination address on the card did not have the required DMA alignment. + * @vvr - Store per VRING data structures. + * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. + * @dd - Virtio device descriptor. + * @dc - Virtio device control fields. + * @list - List of Virtio devices. + * @virtio_db - The doorbell used by the card to interrupt the host. + * @virtio_cookie - The cookie returned while requesting interrupts. + * @vi: Transport information. + * @vdev_mutex: Mutex synchronizing virtio device injection, + * removal and data transfers. + * @destroy: Track if a virtio device is being destroyed. + * @deleted: The virtio device has been deleted. + */ +struct vop_vdev { + int virtio_id; + wait_queue_head_t waitq; + struct vop_device *vpdev; + int poll_wake; + unsigned long out_bytes; + unsigned long in_bytes; + unsigned long out_bytes_dma; + unsigned long in_bytes_dma; + unsigned long tx_len_unaligned; + unsigned long tx_dst_unaligned; + unsigned long rx_dst_unaligned; + struct vop_vringh vvr[MIC_MAX_VRINGS]; + struct work_struct virtio_bh_work; + struct mic_device_desc *dd; + struct mic_device_ctrl *dc; + struct list_head list; + int virtio_db; + struct mic_irq *virtio_cookie; + struct vop_info *vi; + struct mutex vdev_mutex; + struct completion destroy; + bool deleted; +}; + +/* Helper API to check if a virtio device is running */ +static inline bool vop_vdevup(struct vop_vdev *vdev) +{ + return !!vdev->dd->status; +} + +void vop_init_debugfs(struct vop_info *vi); +void vop_exit_debugfs(struct vop_info *vi); +int vop_host_init(struct vop_info *vi); +void vop_host_uninit(struct vop_info *vi); +#endif -- cgit v1.2.3 From 61e9c905df78c253752971e200f0ac6d8667dda6 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:15 -0800 Subject: misc: mic: Enable VOP host side functionality This patch moves virtio functionality from the MIC host driver into a separate hardware independent Virtio Over PCIe (VOP) driver. This functionality was introduced in commit f69bcbf3b4c4 ("Intel MIC Host Driver Changes for Virtio Devices.") in drivers/misc/mic/host/mic_virtio.c. Apart from being moved into a separate driver the functionality is essentially unchanged. See the above mentioned commit for a description of this functionality. Signed-off-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_vringh.c | 1164 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1164 insertions(+) create mode 100644 drivers/misc/mic/vop/vop_vringh.c (limited to 'drivers/misc') diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c new file mode 100644 index 000000000000..c1dd000f042b --- /dev/null +++ b/drivers/misc/mic/vop/vop_vringh.c @@ -0,0 +1,1164 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel Virtio Over PCIe (VOP) driver. + * + */ +#include +#include +#include + +#include +#include "../common/mic_dev.h" + +#include +#include "vop_main.h" + +/* Helper API to obtain the VOP PCIe device */ +static inline struct device *vop_dev(struct vop_vdev *vdev) +{ + return vdev->vpdev->dev.parent; +} + +/* Helper API to check if a virtio device is initialized */ +static inline int vop_vdev_inited(struct vop_vdev *vdev) +{ + if (!vdev) + return -EINVAL; + /* Device has not been created yet */ + if (!vdev->dd || !vdev->dd->type) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, -EINVAL); + return -EINVAL; + } + /* Device has been removed/deleted */ + if (vdev->dd->type == -1) { + dev_dbg(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, -ENODEV); + return -ENODEV; + } + return 0; +} + +static void _vop_notify(struct vringh *vrh) +{ + struct vop_vringh *vvrh = container_of(vrh, struct vop_vringh, vrh); + struct vop_vdev *vdev = vvrh->vdev; + struct vop_device *vpdev = vdev->vpdev; + s8 db = vdev->dc->h2c_vdev_db; + + if (db != -1) + vpdev->hw_ops->send_intr(vpdev, db); +} + +static void vop_virtio_init_post(struct vop_vdev *vdev) +{ + struct mic_vqconfig *vqconfig = mic_vq_config(vdev->dd); + struct vop_device *vpdev = vdev->vpdev; + int i, used_size; + + for (i = 0; i < vdev->dd->num_vq; i++) { + used_size = PAGE_ALIGN(sizeof(u16) * 3 + + sizeof(struct vring_used_elem) * + le16_to_cpu(vqconfig->num)); + if (!le64_to_cpu(vqconfig[i].used_address)) { + dev_warn(vop_dev(vdev), "used_address zero??\n"); + continue; + } + vdev->vvr[i].vrh.vring.used = + (void __force *)vpdev->hw_ops->ioremap( + vpdev, + le64_to_cpu(vqconfig[i].used_address), + used_size); + } + + vdev->dc->used_address_updated = 0; + + dev_info(vop_dev(vdev), "%s: device type %d LINKUP\n", + __func__, vdev->virtio_id); +} + +static inline void vop_virtio_device_reset(struct vop_vdev *vdev) +{ + int i; + + dev_dbg(vop_dev(vdev), "%s: status %d device type %d RESET\n", + __func__, vdev->dd->status, vdev->virtio_id); + + for (i = 0; i < vdev->dd->num_vq; i++) + /* + * Avoid lockdep false positive. The + 1 is for the vop + * mutex which is held in the reset devices code path. + */ + mutex_lock_nested(&vdev->vvr[i].vr_mutex, i + 1); + + /* 0 status means "reset" */ + vdev->dd->status = 0; + vdev->dc->vdev_reset = 0; + vdev->dc->host_ack = 1; + + for (i = 0; i < vdev->dd->num_vq; i++) { + struct vringh *vrh = &vdev->vvr[i].vrh; + + vdev->vvr[i].vring.info->avail_idx = 0; + vrh->completed = 0; + vrh->last_avail_idx = 0; + vrh->last_used_idx = 0; + } + + for (i = 0; i < vdev->dd->num_vq; i++) + mutex_unlock(&vdev->vvr[i].vr_mutex); +} + +static void vop_virtio_reset_devices(struct vop_info *vi) +{ + struct list_head *pos, *tmp; + struct vop_vdev *vdev; + + list_for_each_safe(pos, tmp, &vi->vdev_list) { + vdev = list_entry(pos, struct vop_vdev, list); + vop_virtio_device_reset(vdev); + vdev->poll_wake = 1; + wake_up(&vdev->waitq); + } +} + +static void vop_bh_handler(struct work_struct *work) +{ + struct vop_vdev *vdev = container_of(work, struct vop_vdev, + virtio_bh_work); + + if (vdev->dc->used_address_updated) + vop_virtio_init_post(vdev); + + if (vdev->dc->vdev_reset) + vop_virtio_device_reset(vdev); + + vdev->poll_wake = 1; + wake_up(&vdev->waitq); +} + +static irqreturn_t _vop_virtio_intr_handler(int irq, void *data) +{ + struct vop_vdev *vdev = data; + struct vop_device *vpdev = vdev->vpdev; + + vpdev->hw_ops->ack_interrupt(vpdev, vdev->virtio_db); + schedule_work(&vdev->virtio_bh_work); + return IRQ_HANDLED; +} + +static int vop_virtio_config_change(struct vop_vdev *vdev, void *argp) +{ + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); + int ret = 0, retry, i; + struct vop_device *vpdev = vdev->vpdev; + struct vop_info *vi = dev_get_drvdata(&vpdev->dev); + struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); + s8 db = bootparam->h2c_config_db; + + mutex_lock(&vi->vop_mutex); + for (i = 0; i < vdev->dd->num_vq; i++) + mutex_lock_nested(&vdev->vvr[i].vr_mutex, i + 1); + + if (db == -1 || vdev->dd->type == -1) { + ret = -EIO; + goto exit; + } + + memcpy(mic_vq_configspace(vdev->dd), argp, vdev->dd->config_len); + vdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED; + vpdev->hw_ops->send_intr(vpdev, db); + + for (retry = 100; retry--;) { + ret = wait_event_timeout(wake, vdev->dc->guest_ack, + msecs_to_jiffies(100)); + if (ret) + break; + } + + dev_dbg(vop_dev(vdev), + "%s %d retry: %d\n", __func__, __LINE__, retry); + vdev->dc->config_change = 0; + vdev->dc->guest_ack = 0; +exit: + for (i = 0; i < vdev->dd->num_vq; i++) + mutex_unlock(&vdev->vvr[i].vr_mutex); + mutex_unlock(&vi->vop_mutex); + return ret; +} + +static int vop_copy_dp_entry(struct vop_vdev *vdev, + struct mic_device_desc *argp, __u8 *type, + struct mic_device_desc **devpage) +{ + struct vop_device *vpdev = vdev->vpdev; + struct mic_device_desc *devp; + struct mic_vqconfig *vqconfig; + int ret = 0, i; + bool slot_found = false; + + vqconfig = mic_vq_config(argp); + for (i = 0; i < argp->num_vq; i++) { + if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) { + ret = -EINVAL; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto exit; + } + } + + /* Find the first free device page entry */ + for (i = sizeof(struct mic_bootparam); + i < MIC_DP_SIZE - mic_total_desc_size(argp); + i += mic_total_desc_size(devp)) { + devp = vpdev->hw_ops->get_dp(vpdev) + i; + if (devp->type == 0 || devp->type == -1) { + slot_found = true; + break; + } + } + if (!slot_found) { + ret = -EINVAL; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto exit; + } + /* + * Save off the type before doing the memcpy. Type will be set in the + * end after completing all initialization for the new device. + */ + *type = argp->type; + argp->type = 0; + memcpy(devp, argp, mic_desc_size(argp)); + + *devpage = devp; +exit: + return ret; +} + +static void vop_init_device_ctrl(struct vop_vdev *vdev, + struct mic_device_desc *devpage) +{ + struct mic_device_ctrl *dc; + + dc = (void *)devpage + mic_aligned_desc_size(devpage); + + dc->config_change = 0; + dc->guest_ack = 0; + dc->vdev_reset = 0; + dc->host_ack = 0; + dc->used_address_updated = 0; + dc->c2h_vdev_db = -1; + dc->h2c_vdev_db = -1; + vdev->dc = dc; +} + +static int vop_virtio_add_device(struct vop_vdev *vdev, + struct mic_device_desc *argp) +{ + struct vop_info *vi = vdev->vi; + struct vop_device *vpdev = vi->vpdev; + struct mic_device_desc *dd = NULL; + struct mic_vqconfig *vqconfig; + int vr_size, i, j, ret; + u8 type = 0; + s8 db = -1; + char irqname[16]; + struct mic_bootparam *bootparam; + u16 num; + dma_addr_t vr_addr; + + bootparam = vpdev->hw_ops->get_dp(vpdev); + init_waitqueue_head(&vdev->waitq); + INIT_LIST_HEAD(&vdev->list); + vdev->vpdev = vpdev; + + ret = vop_copy_dp_entry(vdev, argp, &type, &dd); + if (ret) { + kfree(vdev); + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + return ret; + } + + vop_init_device_ctrl(vdev, dd); + + vdev->dd = dd; + vdev->virtio_id = type; + vqconfig = mic_vq_config(dd); + INIT_WORK(&vdev->virtio_bh_work, vop_bh_handler); + + for (i = 0; i < dd->num_vq; i++) { + struct vop_vringh *vvr = &vdev->vvr[i]; + struct mic_vring *vr = &vdev->vvr[i].vring; + + num = le16_to_cpu(vqconfig[i].num); + mutex_init(&vvr->vr_mutex); + vr_size = PAGE_ALIGN(vring_size(num, MIC_VIRTIO_RING_ALIGN) + + sizeof(struct _mic_vring_info)); + vr->va = (void *) + __get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(vr_size)); + if (!vr->va) { + ret = -ENOMEM; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vr->len = vr_size; + vr->info = vr->va + vring_size(num, MIC_VIRTIO_RING_ALIGN); + vr->info->magic = cpu_to_le32(MIC_MAGIC + vdev->virtio_id + i); + vr_addr = dma_map_single(&vpdev->dev, vr->va, vr_size, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(&vpdev->dev, vr_addr)) { + free_pages((unsigned long)vr->va, get_order(vr_size)); + ret = -ENOMEM; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vqconfig[i].address = cpu_to_le64(vr_addr); + + vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN); + ret = vringh_init_kern(&vvr->vrh, + *(u32 *)mic_vq_features(vdev->dd), + num, false, vr->vr.desc, vr->vr.avail, + vr->vr.used); + if (ret) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vringh_kiov_init(&vvr->riov, NULL, 0); + vringh_kiov_init(&vvr->wiov, NULL, 0); + vvr->head = USHRT_MAX; + vvr->vdev = vdev; + vvr->vrh.notify = _vop_notify; + dev_dbg(&vpdev->dev, + "%s %d index %d va %p info %p vr_size 0x%x\n", + __func__, __LINE__, i, vr->va, vr->info, vr_size); + vvr->buf = (void *)__get_free_pages(GFP_KERNEL, + get_order(VOP_INT_DMA_BUF_SIZE)); + vvr->buf_da = dma_map_single(&vpdev->dev, + vvr->buf, VOP_INT_DMA_BUF_SIZE, + DMA_BIDIRECTIONAL); + } + + snprintf(irqname, sizeof(irqname), "vop%dvirtio%d", vpdev->index, + vdev->virtio_id); + vdev->virtio_db = vpdev->hw_ops->next_db(vpdev); + vdev->virtio_cookie = vpdev->hw_ops->request_irq(vpdev, + _vop_virtio_intr_handler, irqname, vdev, + vdev->virtio_db); + if (IS_ERR(vdev->virtio_cookie)) { + ret = PTR_ERR(vdev->virtio_cookie); + dev_dbg(&vpdev->dev, "request irq failed\n"); + goto err; + } + + vdev->dc->c2h_vdev_db = vdev->virtio_db; + + /* + * Order the type update with previous stores. This write barrier + * is paired with the corresponding read barrier before the uncached + * system memory read of the type, on the card while scanning the + * device page. + */ + smp_wmb(); + dd->type = type; + argp->type = type; + + if (bootparam) { + db = bootparam->h2c_config_db; + if (db != -1) + vpdev->hw_ops->send_intr(vpdev, db); + } + dev_dbg(&vpdev->dev, "Added virtio id %d db %d\n", dd->type, db); + return 0; +err: + vqconfig = mic_vq_config(dd); + for (j = 0; j < i; j++) { + struct vop_vringh *vvr = &vdev->vvr[j]; + + dma_unmap_single(&vpdev->dev, le64_to_cpu(vqconfig[j].address), + vvr->vring.len, DMA_BIDIRECTIONAL); + free_pages((unsigned long)vvr->vring.va, + get_order(vvr->vring.len)); + } + return ret; +} + +static void vop_dev_remove(struct vop_info *pvi, struct mic_device_ctrl *devp, + struct vop_device *vpdev) +{ + struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); + s8 db; + int ret, retry; + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); + + devp->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE; + db = bootparam->h2c_config_db; + if (db != -1) + vpdev->hw_ops->send_intr(vpdev, db); + else + goto done; + for (retry = 15; retry--;) { + ret = wait_event_timeout(wake, devp->guest_ack, + msecs_to_jiffies(1000)); + if (ret) + break; + } +done: + devp->config_change = 0; + devp->guest_ack = 0; +} + +static void vop_virtio_del_device(struct vop_vdev *vdev) +{ + struct vop_info *vi = vdev->vi; + struct vop_device *vpdev = vdev->vpdev; + int i; + struct mic_vqconfig *vqconfig; + struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); + + if (!bootparam) + goto skip_hot_remove; + vop_dev_remove(vi, vdev->dc, vpdev); +skip_hot_remove: + vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); + flush_work(&vdev->virtio_bh_work); + vqconfig = mic_vq_config(vdev->dd); + for (i = 0; i < vdev->dd->num_vq; i++) { + struct vop_vringh *vvr = &vdev->vvr[i]; + + dma_unmap_single(&vpdev->dev, + vvr->buf_da, VOP_INT_DMA_BUF_SIZE, + DMA_BIDIRECTIONAL); + free_pages((unsigned long)vvr->buf, + get_order(VOP_INT_DMA_BUF_SIZE)); + vringh_kiov_cleanup(&vvr->riov); + vringh_kiov_cleanup(&vvr->wiov); + dma_unmap_single(&vpdev->dev, le64_to_cpu(vqconfig[i].address), + vvr->vring.len, DMA_BIDIRECTIONAL); + free_pages((unsigned long)vvr->vring.va, + get_order(vvr->vring.len)); + } + /* + * Order the type update with previous stores. This write barrier + * is paired with the corresponding read barrier before the uncached + * system memory read of the type, on the card while scanning the + * device page. + */ + smp_wmb(); + vdev->dd->type = -1; +} + +/* + * vop_sync_dma - Wrapper for synchronous DMAs. + * + * @dev - The address of the pointer to the device instance used + * for DMA registration. + * @dst - destination DMA address. + * @src - source DMA address. + * @len - size of the transfer. + * + * Return DMA_SUCCESS on success + */ +static int vop_sync_dma(struct vop_vdev *vdev, dma_addr_t dst, dma_addr_t src, + size_t len) +{ + int err = 0; + struct dma_device *ddev; + struct dma_async_tx_descriptor *tx; + struct vop_info *vi = dev_get_drvdata(&vdev->vpdev->dev); + struct dma_chan *vop_ch = vi->dma_ch; + + if (!vop_ch) { + err = -EBUSY; + goto error; + } + ddev = vop_ch->device; + tx = ddev->device_prep_dma_memcpy(vop_ch, dst, src, len, + DMA_PREP_FENCE); + if (!tx) { + err = -ENOMEM; + goto error; + } else { + dma_cookie_t cookie; + + cookie = tx->tx_submit(tx); + if (dma_submit_error(cookie)) { + err = -ENOMEM; + goto error; + } + dma_async_issue_pending(vop_ch); + err = dma_sync_wait(vop_ch, cookie); + } +error: + if (err) + dev_err(&vi->vpdev->dev, "%s %d err %d\n", + __func__, __LINE__, err); + return err; +} + +#define VOP_USE_DMA true + +/* + * Initiates the copies across the PCIe bus from card memory to a user + * space buffer. When transfers are done using DMA, source/destination + * addresses and transfer length must follow the alignment requirements of + * the MIC DMA engine. + */ +static int vop_virtio_copy_to_user(struct vop_vdev *vdev, void __user *ubuf, + size_t len, u64 daddr, size_t dlen, + int vr_idx) +{ + struct vop_device *vpdev = vdev->vpdev; + void __iomem *dbuf = vpdev->hw_ops->ioremap(vpdev, daddr, len); + struct vop_vringh *vvr = &vdev->vvr[vr_idx]; + struct vop_info *vi = dev_get_drvdata(&vpdev->dev); + size_t dma_alignment = 1 << vi->dma_ch->device->copy_align; + bool x200 = is_dma_copy_aligned(vi->dma_ch->device, 1, 1, 1); + size_t dma_offset, partlen; + int err; + + if (!VOP_USE_DMA) { + if (copy_to_user(ubuf, (void __force *)dbuf, len)) { + err = -EFAULT; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + vdev->in_bytes += len; + err = 0; + goto err; + } + + dma_offset = daddr - round_down(daddr, dma_alignment); + daddr -= dma_offset; + len += dma_offset; + /* + * X100 uses DMA addresses as seen by the card so adding + * the aperture base is not required for DMA. However x200 + * requires DMA addresses to be an offset into the bar so + * add the aperture base for x200. + */ + if (x200) + daddr += vpdev->aper->pa; + while (len) { + partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE); + err = vop_sync_dma(vdev, vvr->buf_da, daddr, + ALIGN(partlen, dma_alignment)); + if (err) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + if (copy_to_user(ubuf, vvr->buf + dma_offset, + partlen - dma_offset)) { + err = -EFAULT; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + daddr += partlen; + ubuf += partlen; + dbuf += partlen; + vdev->in_bytes_dma += partlen; + vdev->in_bytes += partlen; + len -= partlen; + dma_offset = 0; + } + err = 0; +err: + vpdev->hw_ops->iounmap(vpdev, dbuf); + dev_dbg(vop_dev(vdev), + "%s: ubuf %p dbuf %p len 0x%lx vr_idx 0x%x\n", + __func__, ubuf, dbuf, len, vr_idx); + return err; +} + +/* + * Initiates copies across the PCIe bus from a user space buffer to card + * memory. When transfers are done using DMA, source/destination addresses + * and transfer length must follow the alignment requirements of the MIC + * DMA engine. + */ +static int vop_virtio_copy_from_user(struct vop_vdev *vdev, void __user *ubuf, + size_t len, u64 daddr, size_t dlen, + int vr_idx) +{ + struct vop_device *vpdev = vdev->vpdev; + void __iomem *dbuf = vpdev->hw_ops->ioremap(vpdev, daddr, len); + struct vop_vringh *vvr = &vdev->vvr[vr_idx]; + struct vop_info *vi = dev_get_drvdata(&vdev->vpdev->dev); + size_t dma_alignment = 1 << vi->dma_ch->device->copy_align; + bool x200 = is_dma_copy_aligned(vi->dma_ch->device, 1, 1, 1); + size_t partlen; + bool dma = VOP_USE_DMA; + int err = 0; + + if (daddr & (dma_alignment - 1)) { + vdev->tx_dst_unaligned += len; + dma = false; + } else if (ALIGN(len, dma_alignment) > dlen) { + vdev->tx_len_unaligned += len; + dma = false; + } + + if (!dma) + goto memcpy; + + /* + * X100 uses DMA addresses as seen by the card so adding + * the aperture base is not required for DMA. However x200 + * requires DMA addresses to be an offset into the bar so + * add the aperture base for x200. + */ + if (x200) + daddr += vpdev->aper->pa; + while (len) { + partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE); + + if (copy_from_user(vvr->buf, ubuf, partlen)) { + err = -EFAULT; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + err = vop_sync_dma(vdev, daddr, vvr->buf_da, + ALIGN(partlen, dma_alignment)); + if (err) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + daddr += partlen; + ubuf += partlen; + dbuf += partlen; + vdev->out_bytes_dma += partlen; + vdev->out_bytes += partlen; + len -= partlen; + } +memcpy: + /* + * We are copying to IO below and should ideally use something + * like copy_from_user_toio(..) if it existed. + */ + if (copy_from_user((void __force *)dbuf, ubuf, len)) { + err = -EFAULT; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + vdev->out_bytes += len; + err = 0; +err: + vpdev->hw_ops->iounmap(vpdev, dbuf); + dev_dbg(vop_dev(vdev), + "%s: ubuf %p dbuf %p len 0x%lx vr_idx 0x%x\n", + __func__, ubuf, dbuf, len, vr_idx); + return err; +} + +#define MIC_VRINGH_READ true + +/* Determine the total number of bytes consumed in a VRINGH KIOV */ +static inline u32 vop_vringh_iov_consumed(struct vringh_kiov *iov) +{ + int i; + u32 total = iov->consumed; + + for (i = 0; i < iov->i; i++) + total += iov->iov[i].iov_len; + return total; +} + +/* + * Traverse the VRINGH KIOV and issue the APIs to trigger the copies. + * This API is heavily based on the vringh_iov_xfer(..) implementation + * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..) + * and vringh_iov_push_kern(..) directly is because there is no + * way to override the VRINGH xfer(..) routines as of v3.10. + */ +static int vop_vringh_copy(struct vop_vdev *vdev, struct vringh_kiov *iov, + void __user *ubuf, size_t len, bool read, int vr_idx, + size_t *out_len) +{ + int ret = 0; + size_t partlen, tot_len = 0; + + while (len && iov->i < iov->used) { + struct kvec *kiov = &iov->iov[iov->i]; + + partlen = min(kiov->iov_len, len); + if (read) + ret = vop_virtio_copy_to_user(vdev, ubuf, partlen, + (u64)kiov->iov_base, + kiov->iov_len, + vr_idx); + else + ret = vop_virtio_copy_from_user(vdev, ubuf, partlen, + (u64)kiov->iov_base, + kiov->iov_len, + vr_idx); + if (ret) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= partlen; + ubuf += partlen; + tot_len += partlen; + iov->consumed += partlen; + kiov->iov_len -= partlen; + kiov->iov_base += partlen; + if (!kiov->iov_len) { + /* Fix up old iov element then increment. */ + kiov->iov_len = iov->consumed; + kiov->iov_base -= iov->consumed; + + iov->consumed = 0; + iov->i++; + } + } + *out_len = tot_len; + return ret; +} + +/* + * Use the standard VRINGH infrastructure in the kernel to fetch new + * descriptors, initiate the copies and update the used ring. + */ +static int _vop_virtio_copy(struct vop_vdev *vdev, struct mic_copy_desc *copy) +{ + int ret = 0; + u32 iovcnt = copy->iovcnt; + struct iovec iov; + struct iovec __user *u_iov = copy->iov; + void __user *ubuf = NULL; + struct vop_vringh *vvr = &vdev->vvr[copy->vr_idx]; + struct vringh_kiov *riov = &vvr->riov; + struct vringh_kiov *wiov = &vvr->wiov; + struct vringh *vrh = &vvr->vrh; + u16 *head = &vvr->head; + struct mic_vring *vr = &vvr->vring; + size_t len = 0, out_len; + + copy->out_len = 0; + /* Fetch a new IOVEC if all previous elements have been processed */ + if (riov->i == riov->used && wiov->i == wiov->used) { + ret = vringh_getdesc_kern(vrh, riov, wiov, + head, GFP_KERNEL); + /* Check if there are available descriptors */ + if (ret <= 0) + return ret; + } + while (iovcnt) { + if (!len) { + /* Copy over a new iovec from user space. */ + ret = copy_from_user(&iov, u_iov, sizeof(*u_iov)); + if (ret) { + ret = -EINVAL; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len = iov.iov_len; + ubuf = iov.iov_base; + } + /* Issue all the read descriptors first */ + ret = vop_vringh_copy(vdev, riov, ubuf, len, + MIC_VRINGH_READ, copy->vr_idx, &out_len); + if (ret) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= out_len; + ubuf += out_len; + copy->out_len += out_len; + /* Issue the write descriptors next */ + ret = vop_vringh_copy(vdev, wiov, ubuf, len, + !MIC_VRINGH_READ, copy->vr_idx, &out_len); + if (ret) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= out_len; + ubuf += out_len; + copy->out_len += out_len; + if (!len) { + /* One user space iovec is now completed */ + iovcnt--; + u_iov++; + } + /* Exit loop if all elements in KIOVs have been processed. */ + if (riov->i == riov->used && wiov->i == wiov->used) + break; + } + /* + * Update the used ring if a descriptor was available and some data was + * copied in/out and the user asked for a used ring update. + */ + if (*head != USHRT_MAX && copy->out_len && copy->update_used) { + u32 total = 0; + + /* Determine the total data consumed */ + total += vop_vringh_iov_consumed(riov); + total += vop_vringh_iov_consumed(wiov); + vringh_complete_kern(vrh, *head, total); + *head = USHRT_MAX; + if (vringh_need_notify_kern(vrh) > 0) + vringh_notify(vrh); + vringh_kiov_cleanup(riov); + vringh_kiov_cleanup(wiov); + /* Update avail idx for user space */ + vr->info->avail_idx = vrh->last_avail_idx; + } + return ret; +} + +static inline int vop_verify_copy_args(struct vop_vdev *vdev, + struct mic_copy_desc *copy) +{ + if (!vdev || copy->vr_idx >= vdev->dd->num_vq) + return -EINVAL; + return 0; +} + +/* Copy a specified number of virtio descriptors in a chain */ +static int vop_virtio_copy_desc(struct vop_vdev *vdev, + struct mic_copy_desc *copy) +{ + int err; + struct vop_vringh *vvr = &vdev->vvr[copy->vr_idx]; + + err = vop_verify_copy_args(vdev, copy); + if (err) + return err; + + mutex_lock(&vvr->vr_mutex); + if (!vop_vdevup(vdev)) { + err = -ENODEV; + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + err = _vop_virtio_copy(vdev, copy); + if (err) { + dev_err(vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + } +err: + mutex_unlock(&vvr->vr_mutex); + return err; +} + +static int vop_open(struct inode *inode, struct file *f) +{ + struct vop_vdev *vdev; + struct vop_info *vi = container_of(f->private_data, + struct vop_info, miscdev); + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return -ENOMEM; + vdev->vi = vi; + mutex_init(&vdev->vdev_mutex); + f->private_data = vdev; + init_completion(&vdev->destroy); + complete(&vdev->destroy); + return 0; +} + +static int vop_release(struct inode *inode, struct file *f) +{ + struct vop_vdev *vdev = f->private_data, *vdev_tmp; + struct vop_info *vi = vdev->vi; + struct list_head *pos, *tmp; + bool found = false; + + mutex_lock(&vdev->vdev_mutex); + if (vdev->deleted) + goto unlock; + mutex_lock(&vi->vop_mutex); + list_for_each_safe(pos, tmp, &vi->vdev_list) { + vdev_tmp = list_entry(pos, struct vop_vdev, list); + if (vdev == vdev_tmp) { + vop_virtio_del_device(vdev); + list_del(pos); + found = true; + break; + } + } + mutex_unlock(&vi->vop_mutex); +unlock: + mutex_unlock(&vdev->vdev_mutex); + if (!found) + wait_for_completion(&vdev->destroy); + f->private_data = NULL; + kfree(vdev); + return 0; +} + +static long vop_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + struct vop_vdev *vdev = f->private_data; + struct vop_info *vi = vdev->vi; + void __user *argp = (void __user *)arg; + int ret; + + switch (cmd) { + case MIC_VIRTIO_ADD_DEVICE: + { + struct mic_device_desc dd, *dd_config; + + if (copy_from_user(&dd, argp, sizeof(dd))) + return -EFAULT; + + if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE || + dd.num_vq > MIC_MAX_VRINGS) + return -EINVAL; + + dd_config = kzalloc(mic_desc_size(&dd), GFP_KERNEL); + if (!dd_config) + return -ENOMEM; + if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) { + ret = -EFAULT; + goto free_ret; + } + mutex_lock(&vdev->vdev_mutex); + mutex_lock(&vi->vop_mutex); + ret = vop_virtio_add_device(vdev, dd_config); + if (ret) + goto unlock_ret; + list_add_tail(&vdev->list, &vi->vdev_list); +unlock_ret: + mutex_unlock(&vi->vop_mutex); + mutex_unlock(&vdev->vdev_mutex); +free_ret: + kfree(dd_config); + return ret; + } + case MIC_VIRTIO_COPY_DESC: + { + struct mic_copy_desc copy; + + mutex_lock(&vdev->vdev_mutex); + ret = vop_vdev_inited(vdev); + if (ret) + goto _unlock_ret; + + if (copy_from_user(©, argp, sizeof(copy))) { + ret = -EFAULT; + goto _unlock_ret; + } + + ret = vop_virtio_copy_desc(vdev, ©); + if (ret < 0) + goto _unlock_ret; + if (copy_to_user( + &((struct mic_copy_desc __user *)argp)->out_len, + ©.out_len, sizeof(copy.out_len))) + ret = -EFAULT; +_unlock_ret: + mutex_unlock(&vdev->vdev_mutex); + return ret; + } + case MIC_VIRTIO_CONFIG_CHANGE: + { + void *buf; + + mutex_lock(&vdev->vdev_mutex); + ret = vop_vdev_inited(vdev); + if (ret) + goto __unlock_ret; + buf = kzalloc(vdev->dd->config_len, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto __unlock_ret; + } + if (copy_from_user(buf, argp, vdev->dd->config_len)) { + ret = -EFAULT; + goto done; + } + ret = vop_virtio_config_change(vdev, buf); +done: + kfree(buf); +__unlock_ret: + mutex_unlock(&vdev->vdev_mutex); + return ret; + } + default: + return -ENOIOCTLCMD; + }; + return 0; +} + +/* + * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and + * not when previously enqueued buffers may be available. This means that + * in the card->host (TX) path, when userspace is unblocked by poll it + * must drain all available descriptors or it can stall. + */ +static unsigned int vop_poll(struct file *f, poll_table *wait) +{ + struct vop_vdev *vdev = f->private_data; + int mask = 0; + + mutex_lock(&vdev->vdev_mutex); + if (vop_vdev_inited(vdev)) { + mask = POLLERR; + goto done; + } + poll_wait(f, &vdev->waitq, wait); + if (vop_vdev_inited(vdev)) { + mask = POLLERR; + } else if (vdev->poll_wake) { + vdev->poll_wake = 0; + mask = POLLIN | POLLOUT; + } +done: + mutex_unlock(&vdev->vdev_mutex); + return mask; +} + +static inline int +vop_query_offset(struct vop_vdev *vdev, unsigned long offset, + unsigned long *size, unsigned long *pa) +{ + struct vop_device *vpdev = vdev->vpdev; + unsigned long start = MIC_DP_SIZE; + int i; + + /* + * MMAP interface is as follows: + * offset region + * 0x0 virtio device_page + * 0x1000 first vring + * 0x1000 + size of 1st vring second vring + * .... + */ + if (!offset) { + *pa = virt_to_phys(vpdev->hw_ops->get_dp(vpdev)); + *size = MIC_DP_SIZE; + return 0; + } + + for (i = 0; i < vdev->dd->num_vq; i++) { + struct vop_vringh *vvr = &vdev->vvr[i]; + + if (offset == start) { + *pa = virt_to_phys(vvr->vring.va); + *size = vvr->vring.len; + return 0; + } + start += vvr->vring.len; + } + return -1; +} + +/* + * Maps the device page and virtio rings to user space for readonly access. + */ +static int vop_mmap(struct file *f, struct vm_area_struct *vma) +{ + struct vop_vdev *vdev = f->private_data; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size; + int i, err; + + err = vop_vdev_inited(vdev); + if (err) + goto ret; + if (vma->vm_flags & VM_WRITE) { + err = -EACCES; + goto ret; + } + while (size_rem) { + i = vop_query_offset(vdev, offset, &size, &pa); + if (i < 0) { + err = -EINVAL; + goto ret; + } + err = remap_pfn_range(vma, vma->vm_start + offset, + pa >> PAGE_SHIFT, size, + vma->vm_page_prot); + if (err) + goto ret; + size_rem -= size; + offset += size; + } +ret: + return err; +} + +static const struct file_operations vop_fops = { + .open = vop_open, + .release = vop_release, + .unlocked_ioctl = vop_ioctl, + .poll = vop_poll, + .mmap = vop_mmap, + .owner = THIS_MODULE, +}; + +int vop_host_init(struct vop_info *vi) +{ + int rc; + struct miscdevice *mdev; + struct vop_device *vpdev = vi->vpdev; + + INIT_LIST_HEAD(&vi->vdev_list); + vi->dma_ch = vpdev->dma_ch; + mdev = &vi->miscdev; + mdev->minor = MISC_DYNAMIC_MINOR; + snprintf(vi->name, sizeof(vi->name), "vop_virtio%d", vpdev->index); + mdev->name = vi->name; + mdev->fops = &vop_fops; + mdev->parent = &vpdev->dev; + + rc = misc_register(mdev); + if (rc) + dev_err(&vpdev->dev, "%s failed rc %d\n", __func__, rc); + return rc; +} + +void vop_host_uninit(struct vop_info *vi) +{ + struct list_head *pos, *tmp; + struct vop_vdev *vdev; + + mutex_lock(&vi->vop_mutex); + vop_virtio_reset_devices(vi); + list_for_each_safe(pos, tmp, &vi->vdev_list) { + vdev = list_entry(pos, struct vop_vdev, list); + list_del(pos); + reinit_completion(&vdev->destroy); + mutex_unlock(&vi->vop_mutex); + mutex_lock(&vdev->vdev_mutex); + vop_virtio_del_device(vdev); + vdev->deleted = true; + mutex_unlock(&vdev->vdev_mutex); + complete(&vdev->destroy); + mutex_lock(&vi->vop_mutex); + } + mutex_unlock(&vi->vop_mutex); + misc_deregister(&vi->miscdev); +} -- cgit v1.2.3 From c1becd2849681e8be1043d2ce9034a9d9bb3d540 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 8 Feb 2016 15:48:16 -0800 Subject: misc: mic: Enable VOP card side functionality This patch moves virtio functionality from the MIC card driver into a separate hardware independent Virtio Over PCIe (VOP) driver. This functionality was introduced in commit 2141c7c5ee67 ("Intel MIC Card Driver Changes for Virtio Devices.") in drivers/misc/mic/card/mic_virtio.c. Apart from being moved into a separate driver the functionality is essentially unchanged. See the above mentioned commit for a description of this functionality. Signed-off-by: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_main.c | 755 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 755 insertions(+) create mode 100644 drivers/misc/mic/vop/vop_main.c (limited to 'drivers/misc') diff --git a/drivers/misc/mic/vop/vop_main.c b/drivers/misc/mic/vop/vop_main.c new file mode 100644 index 000000000000..1a2b67f3183d --- /dev/null +++ b/drivers/misc/mic/vop/vop_main.c @@ -0,0 +1,755 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Adapted from: + * + * virtio for kvm on s390 + * + * Copyright IBM Corp. 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + * Author(s): Christian Borntraeger + * + * Intel Virtio Over PCIe (VOP) driver. + * + */ +#include +#include +#include +#include + +#include "vop_main.h" + +#define VOP_MAX_VRINGS 4 + +/* + * _vop_vdev - Allocated per virtio device instance injected by the peer. + * + * @vdev: Virtio device + * @desc: Virtio device page descriptor + * @dc: Virtio device control + * @vpdev: VOP device which is the parent for this virtio device + * @vr: Buffer for accessing the VRING + * @used: Buffer for used + * @used_size: Size of the used buffer + * @reset_done: Track whether VOP reset is complete + * @virtio_cookie: Cookie returned upon requesting a interrupt + * @c2h_vdev_db: The doorbell used by the guest to interrupt the host + * @h2c_vdev_db: The doorbell used by the host to interrupt the guest + * @dnode: The destination node + */ +struct _vop_vdev { + struct virtio_device vdev; + struct mic_device_desc __iomem *desc; + struct mic_device_ctrl __iomem *dc; + struct vop_device *vpdev; + void __iomem *vr[VOP_MAX_VRINGS]; + dma_addr_t used[VOP_MAX_VRINGS]; + int used_size[VOP_MAX_VRINGS]; + struct completion reset_done; + struct mic_irq *virtio_cookie; + int c2h_vdev_db; + int h2c_vdev_db; + int dnode; +}; + +#define to_vopvdev(vd) container_of(vd, struct _vop_vdev, vdev) + +#define _vop_aligned_desc_size(d) __mic_align(_vop_desc_size(d), 8) + +/* Helper API to obtain the parent of the virtio device */ +static inline struct device *_vop_dev(struct _vop_vdev *vdev) +{ + return vdev->vdev.dev.parent; +} + +static inline unsigned _vop_desc_size(struct mic_device_desc __iomem *desc) +{ + return sizeof(*desc) + + ioread8(&desc->num_vq) * sizeof(struct mic_vqconfig) + + ioread8(&desc->feature_len) * 2 + + ioread8(&desc->config_len); +} + +static inline struct mic_vqconfig __iomem * +_vop_vq_config(struct mic_device_desc __iomem *desc) +{ + return (struct mic_vqconfig __iomem *)(desc + 1); +} + +static inline u8 __iomem * +_vop_vq_features(struct mic_device_desc __iomem *desc) +{ + return (u8 __iomem *)(_vop_vq_config(desc) + ioread8(&desc->num_vq)); +} + +static inline u8 __iomem * +_vop_vq_configspace(struct mic_device_desc __iomem *desc) +{ + return _vop_vq_features(desc) + ioread8(&desc->feature_len) * 2; +} + +static inline unsigned +_vop_total_desc_size(struct mic_device_desc __iomem *desc) +{ + return _vop_aligned_desc_size(desc) + sizeof(struct mic_device_ctrl); +} + +/* This gets the device's feature bits. */ +static u64 vop_get_features(struct virtio_device *vdev) +{ + unsigned int i, bits; + u32 features = 0; + struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; + u8 __iomem *in_features = _vop_vq_features(desc); + int feature_len = ioread8(&desc->feature_len); + + bits = min_t(unsigned, feature_len, sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) + if (ioread8(&in_features[i / 8]) & (BIT(i % 8))) + features |= BIT(i); + + return features; +} + +static int vop_finalize_features(struct virtio_device *vdev) +{ + unsigned int i, bits; + struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; + u8 feature_len = ioread8(&desc->feature_len); + /* Second half of bitmap is features we accept. */ + u8 __iomem *out_features = + _vop_vq_features(desc) + feature_len; + + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + + memset_io(out_features, 0, feature_len); + bits = min_t(unsigned, feature_len, + sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) { + if (__virtio_test_bit(vdev, i)) + iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)), + &out_features[i / 8]); + } + return 0; +} + +/* + * Reading and writing elements in config space + */ +static void vop_get(struct virtio_device *vdev, unsigned int offset, + void *buf, unsigned len) +{ + struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; + + if (offset + len > ioread8(&desc->config_len)) + return; + memcpy_fromio(buf, _vop_vq_configspace(desc) + offset, len); +} + +static void vop_set(struct virtio_device *vdev, unsigned int offset, + const void *buf, unsigned len) +{ + struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; + + if (offset + len > ioread8(&desc->config_len)) + return; + memcpy_toio(_vop_vq_configspace(desc) + offset, buf, len); +} + +/* + * The operations to get and set the status word just access the status + * field of the device descriptor. set_status also interrupts the host + * to tell about status changes. + */ +static u8 vop_get_status(struct virtio_device *vdev) +{ + return ioread8(&to_vopvdev(vdev)->desc->status); +} + +static void vop_set_status(struct virtio_device *dev, u8 status) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + struct vop_device *vpdev = vdev->vpdev; + + if (!status) + return; + iowrite8(status, &vdev->desc->status); + vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); +} + +/* Inform host on a virtio device reset and wait for ack from host */ +static void vop_reset_inform_host(struct virtio_device *dev) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + struct mic_device_ctrl __iomem *dc = vdev->dc; + struct vop_device *vpdev = vdev->vpdev; + int retry; + + iowrite8(0, &dc->host_ack); + iowrite8(1, &dc->vdev_reset); + vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); + + /* Wait till host completes all card accesses and acks the reset */ + for (retry = 100; retry--;) { + if (ioread8(&dc->host_ack)) + break; + msleep(100); + }; + + dev_dbg(_vop_dev(vdev), "%s: retry: %d\n", __func__, retry); + + /* Reset status to 0 in case we timed out */ + iowrite8(0, &vdev->desc->status); +} + +static void vop_reset(struct virtio_device *dev) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + + dev_dbg(_vop_dev(vdev), "%s: virtio id %d\n", + __func__, dev->id.device); + + vop_reset_inform_host(dev); + complete_all(&vdev->reset_done); +} + +/* + * The virtio_ring code calls this API when it wants to notify the Host. + */ +static bool vop_notify(struct virtqueue *vq) +{ + struct _vop_vdev *vdev = vq->priv; + struct vop_device *vpdev = vdev->vpdev; + + vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); + return true; +} + +static void vop_del_vq(struct virtqueue *vq, int n) +{ + struct _vop_vdev *vdev = to_vopvdev(vq->vdev); + struct vring *vr = (struct vring *)(vq + 1); + struct vop_device *vpdev = vdev->vpdev; + + dma_unmap_single(&vpdev->dev, vdev->used[n], + vdev->used_size[n], DMA_BIDIRECTIONAL); + free_pages((unsigned long)vr->used, get_order(vdev->used_size[n])); + vring_del_virtqueue(vq); + vpdev->hw_ops->iounmap(vpdev, vdev->vr[n]); + vdev->vr[n] = NULL; +} + +static void vop_del_vqs(struct virtio_device *dev) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + struct virtqueue *vq, *n; + int idx = 0; + + dev_dbg(_vop_dev(vdev), "%s\n", __func__); + + list_for_each_entry_safe(vq, n, &dev->vqs, list) + vop_del_vq(vq, idx++); +} + +/* + * This routine will assign vring's allocated in host/io memory. Code in + * virtio_ring.c however continues to access this io memory as if it were local + * memory without io accessors. + */ +static struct virtqueue *vop_find_vq(struct virtio_device *dev, + unsigned index, + void (*callback)(struct virtqueue *vq), + const char *name) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + struct vop_device *vpdev = vdev->vpdev; + struct mic_vqconfig __iomem *vqconfig; + struct mic_vqconfig config; + struct virtqueue *vq; + void __iomem *va; + struct _mic_vring_info __iomem *info; + void *used; + int vr_size, _vr_size, err, magic; + struct vring *vr; + u8 type = ioread8(&vdev->desc->type); + + if (index >= ioread8(&vdev->desc->num_vq)) + return ERR_PTR(-ENOENT); + + if (!name) + return ERR_PTR(-ENOENT); + + /* First assign the vring's allocated in host memory */ + vqconfig = _vop_vq_config(vdev->desc) + index; + memcpy_fromio(&config, vqconfig, sizeof(config)); + _vr_size = vring_size(le16_to_cpu(config.num), MIC_VIRTIO_RING_ALIGN); + vr_size = PAGE_ALIGN(_vr_size + sizeof(struct _mic_vring_info)); + va = vpdev->hw_ops->ioremap(vpdev, le64_to_cpu(config.address), + vr_size); + if (!va) + return ERR_PTR(-ENOMEM); + vdev->vr[index] = va; + memset_io(va, 0x0, _vr_size); + vq = vring_new_virtqueue( + index, + le16_to_cpu(config.num), MIC_VIRTIO_RING_ALIGN, + dev, + false, + (void __force *)va, vop_notify, callback, name); + if (!vq) { + err = -ENOMEM; + goto unmap; + } + info = va + _vr_size; + magic = ioread32(&info->magic); + + if (WARN(magic != MIC_MAGIC + type + index, "magic mismatch")) { + err = -EIO; + goto unmap; + } + + /* Allocate and reassign used ring now */ + vdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 + + sizeof(struct vring_used_elem) * + le16_to_cpu(config.num)); + used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(vdev->used_size[index])); + if (!used) { + err = -ENOMEM; + dev_err(_vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto del_vq; + } + vdev->used[index] = dma_map_single(&vpdev->dev, used, + vdev->used_size[index], + DMA_BIDIRECTIONAL); + if (dma_mapping_error(&vpdev->dev, vdev->used[index])) { + err = -ENOMEM; + dev_err(_vop_dev(vdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto free_used; + } + writeq(vdev->used[index], &vqconfig->used_address); + /* + * To reassign the used ring here we are directly accessing + * struct vring_virtqueue which is a private data structure + * in virtio_ring.c. At the minimum, a BUILD_BUG_ON() in + * vring_new_virtqueue() would ensure that + * (&vq->vring == (struct vring *) (&vq->vq + 1)); + */ + vr = (struct vring *)(vq + 1); + vr->used = used; + + vq->priv = vdev; + return vq; +free_used: + free_pages((unsigned long)used, + get_order(vdev->used_size[index])); +del_vq: + vring_del_virtqueue(vq); +unmap: + vpdev->hw_ops->iounmap(vpdev, vdev->vr[index]); + return ERR_PTR(err); +} + +static int vop_find_vqs(struct virtio_device *dev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char * const names[]) +{ + struct _vop_vdev *vdev = to_vopvdev(dev); + struct vop_device *vpdev = vdev->vpdev; + struct mic_device_ctrl __iomem *dc = vdev->dc; + int i, err, retry; + + /* We must have this many virtqueues. */ + if (nvqs > ioread8(&vdev->desc->num_vq)) + return -ENOENT; + + for (i = 0; i < nvqs; ++i) { + dev_dbg(_vop_dev(vdev), "%s: %d: %s\n", + __func__, i, names[i]); + vqs[i] = vop_find_vq(dev, i, callbacks[i], names[i]); + if (IS_ERR(vqs[i])) { + err = PTR_ERR(vqs[i]); + goto error; + } + } + + iowrite8(1, &dc->used_address_updated); + /* + * Send an interrupt to the host to inform it that used + * rings have been re-assigned. + */ + vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); + for (retry = 100; --retry;) { + if (!ioread8(&dc->used_address_updated)) + break; + msleep(100); + }; + + dev_dbg(_vop_dev(vdev), "%s: retry: %d\n", __func__, retry); + if (!retry) { + err = -ENODEV; + goto error; + } + + return 0; +error: + vop_del_vqs(dev); + return err; +} + +/* + * The config ops structure as defined by virtio config + */ +static struct virtio_config_ops vop_vq_config_ops = { + .get_features = vop_get_features, + .finalize_features = vop_finalize_features, + .get = vop_get, + .set = vop_set, + .get_status = vop_get_status, + .set_status = vop_set_status, + .reset = vop_reset, + .find_vqs = vop_find_vqs, + .del_vqs = vop_del_vqs, +}; + +static irqreturn_t vop_virtio_intr_handler(int irq, void *data) +{ + struct _vop_vdev *vdev = data; + struct vop_device *vpdev = vdev->vpdev; + struct virtqueue *vq; + + vpdev->hw_ops->ack_interrupt(vpdev, vdev->h2c_vdev_db); + list_for_each_entry(vq, &vdev->vdev.vqs, list) + vring_interrupt(0, vq); + + return IRQ_HANDLED; +} + +static void vop_virtio_release_dev(struct device *_d) +{ + /* + * No need for a release method similar to virtio PCI. + * Provide an empty one to avoid getting a warning from core. + */ +} + +/* + * adds a new device and register it with virtio + * appropriate drivers are loaded by the device model + */ +static int _vop_add_device(struct mic_device_desc __iomem *d, + unsigned int offset, struct vop_device *vpdev, + int dnode) +{ + struct _vop_vdev *vdev; + int ret; + u8 type = ioread8(&d->type); + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return -ENOMEM; + + vdev->vpdev = vpdev; + vdev->vdev.dev.parent = &vpdev->dev; + vdev->vdev.dev.release = vop_virtio_release_dev; + vdev->vdev.id.device = type; + vdev->vdev.config = &vop_vq_config_ops; + vdev->desc = d; + vdev->dc = (void __iomem *)d + _vop_aligned_desc_size(d); + vdev->dnode = dnode; + vdev->vdev.priv = (void *)(u64)dnode; + init_completion(&vdev->reset_done); + + vdev->h2c_vdev_db = vpdev->hw_ops->next_db(vpdev); + vdev->virtio_cookie = vpdev->hw_ops->request_irq(vpdev, + vop_virtio_intr_handler, "virtio intr", + vdev, vdev->h2c_vdev_db); + if (IS_ERR(vdev->virtio_cookie)) { + ret = PTR_ERR(vdev->virtio_cookie); + goto kfree; + } + iowrite8((u8)vdev->h2c_vdev_db, &vdev->dc->h2c_vdev_db); + vdev->c2h_vdev_db = ioread8(&vdev->dc->c2h_vdev_db); + + ret = register_virtio_device(&vdev->vdev); + if (ret) { + dev_err(_vop_dev(vdev), + "Failed to register vop device %u type %u\n", + offset, type); + goto free_irq; + } + writeq((u64)vdev, &vdev->dc->vdev); + dev_dbg(_vop_dev(vdev), "%s: registered vop device %u type %u vdev %p\n", + __func__, offset, type, vdev); + + return 0; + +free_irq: + vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); +kfree: + kfree(vdev); + return ret; +} + +/* + * match for a vop device with a specific desc pointer + */ +static int vop_match_desc(struct device *dev, void *data) +{ + struct virtio_device *_dev = dev_to_virtio(dev); + struct _vop_vdev *vdev = to_vopvdev(_dev); + + return vdev->desc == (void __iomem *)data; +} + +static void _vop_handle_config_change(struct mic_device_desc __iomem *d, + unsigned int offset, + struct vop_device *vpdev) +{ + struct mic_device_ctrl __iomem *dc + = (void __iomem *)d + _vop_aligned_desc_size(d); + struct _vop_vdev *vdev = (struct _vop_vdev *)readq(&dc->vdev); + + if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED) + return; + + dev_dbg(&vpdev->dev, "%s %d\n", __func__, __LINE__); + virtio_config_changed(&vdev->vdev); + iowrite8(1, &dc->guest_ack); +} + +/* + * removes a virtio device if a hot remove event has been + * requested by the host. + */ +static int _vop_remove_device(struct mic_device_desc __iomem *d, + unsigned int offset, struct vop_device *vpdev) +{ + struct mic_device_ctrl __iomem *dc + = (void __iomem *)d + _vop_aligned_desc_size(d); + struct _vop_vdev *vdev = (struct _vop_vdev *)readq(&dc->vdev); + u8 status; + int ret = -1; + + if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) { + dev_dbg(&vpdev->dev, + "%s %d config_change %d type %d vdev %p\n", + __func__, __LINE__, + ioread8(&dc->config_change), ioread8(&d->type), vdev); + status = ioread8(&d->status); + reinit_completion(&vdev->reset_done); + unregister_virtio_device(&vdev->vdev); + vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); + iowrite8(-1, &dc->h2c_vdev_db); + if (status & VIRTIO_CONFIG_S_DRIVER_OK) + wait_for_completion(&vdev->reset_done); + kfree(vdev); + iowrite8(1, &dc->guest_ack); + dev_dbg(&vpdev->dev, "%s %d guest_ack %d\n", + __func__, __LINE__, ioread8(&dc->guest_ack)); + iowrite8(-1, &d->type); + ret = 0; + } + return ret; +} + +#define REMOVE_DEVICES true + +static void _vop_scan_devices(void __iomem *dp, struct vop_device *vpdev, + bool remove, int dnode) +{ + s8 type; + unsigned int i; + struct mic_device_desc __iomem *d; + struct mic_device_ctrl __iomem *dc; + struct device *dev; + int ret; + + for (i = sizeof(struct mic_bootparam); + i < MIC_DP_SIZE; i += _vop_total_desc_size(d)) { + d = dp + i; + dc = (void __iomem *)d + _vop_aligned_desc_size(d); + /* + * This read barrier is paired with the corresponding write + * barrier on the host which is inserted before adding or + * removing a virtio device descriptor, by updating the type. + */ + rmb(); + type = ioread8(&d->type); + + /* end of list */ + if (type == 0) + break; + + if (type == -1) + continue; + + /* device already exists */ + dev = device_find_child(&vpdev->dev, (void __force *)d, + vop_match_desc); + if (dev) { + if (remove) + iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE, + &dc->config_change); + put_device(dev); + _vop_handle_config_change(d, i, vpdev); + ret = _vop_remove_device(d, i, vpdev); + if (remove) { + iowrite8(0, &dc->config_change); + iowrite8(0, &dc->guest_ack); + } + continue; + } + + /* new device */ + dev_dbg(&vpdev->dev, "%s %d Adding new virtio device %p\n", + __func__, __LINE__, d); + if (!remove) + _vop_add_device(d, i, vpdev, dnode); + } +} + +static void vop_scan_devices(struct vop_info *vi, + struct vop_device *vpdev, bool remove) +{ + void __iomem *dp = vpdev->hw_ops->get_remote_dp(vpdev); + + if (!dp) + return; + mutex_lock(&vi->vop_mutex); + _vop_scan_devices(dp, vpdev, remove, vpdev->dnode); + mutex_unlock(&vi->vop_mutex); +} + +/* + * vop_hotplug_device tries to find changes in the device page. + */ +static void vop_hotplug_devices(struct work_struct *work) +{ + struct vop_info *vi = container_of(work, struct vop_info, + hotplug_work); + + vop_scan_devices(vi, vi->vpdev, !REMOVE_DEVICES); +} + +/* + * Interrupt handler for hot plug/config changes etc. + */ +static irqreturn_t vop_extint_handler(int irq, void *data) +{ + struct vop_info *vi = data; + struct mic_bootparam __iomem *bp; + struct vop_device *vpdev = vi->vpdev; + + bp = vpdev->hw_ops->get_remote_dp(vpdev); + dev_dbg(&vpdev->dev, "%s %d hotplug work\n", + __func__, __LINE__); + vpdev->hw_ops->ack_interrupt(vpdev, ioread8(&bp->h2c_config_db)); + schedule_work(&vi->hotplug_work); + return IRQ_HANDLED; +} + +static int vop_driver_probe(struct vop_device *vpdev) +{ + struct vop_info *vi; + int rc; + + vi = kzalloc(sizeof(*vi), GFP_KERNEL); + if (!vi) { + rc = -ENOMEM; + goto exit; + } + dev_set_drvdata(&vpdev->dev, vi); + vi->vpdev = vpdev; + + mutex_init(&vi->vop_mutex); + INIT_WORK(&vi->hotplug_work, vop_hotplug_devices); + if (vpdev->dnode) { + rc = vop_host_init(vi); + if (rc < 0) + goto free; + } else { + struct mic_bootparam __iomem *bootparam; + + vop_scan_devices(vi, vpdev, !REMOVE_DEVICES); + + vi->h2c_config_db = vpdev->hw_ops->next_db(vpdev); + vi->cookie = vpdev->hw_ops->request_irq(vpdev, + vop_extint_handler, + "virtio_config_intr", + vi, vi->h2c_config_db); + if (IS_ERR(vi->cookie)) { + rc = PTR_ERR(vi->cookie); + goto free; + } + bootparam = vpdev->hw_ops->get_remote_dp(vpdev); + iowrite8(vi->h2c_config_db, &bootparam->h2c_config_db); + } + vop_init_debugfs(vi); + return 0; +free: + kfree(vi); +exit: + return rc; +} + +static void vop_driver_remove(struct vop_device *vpdev) +{ + struct vop_info *vi = dev_get_drvdata(&vpdev->dev); + + if (vpdev->dnode) { + vop_host_uninit(vi); + } else { + struct mic_bootparam __iomem *bootparam = + vpdev->hw_ops->get_remote_dp(vpdev); + if (bootparam) + iowrite8(-1, &bootparam->h2c_config_db); + vpdev->hw_ops->free_irq(vpdev, vi->cookie, vi); + flush_work(&vi->hotplug_work); + vop_scan_devices(vi, vpdev, REMOVE_DEVICES); + } + vop_exit_debugfs(vi); + kfree(vi); +} + +static struct vop_device_id id_table[] = { + { VOP_DEV_TRNSP, VOP_DEV_ANY_ID }, + { 0 }, +}; + +static struct vop_driver vop_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = vop_driver_probe, + .remove = vop_driver_remove, +}; + +module_vop_driver(vop_driver); + +MODULE_DEVICE_TABLE(mbus, id_table); +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) Virtio Over PCIe (VOP) driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8810df37762746657cfe84014a8f30758e8f366a Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:17 -0800 Subject: misc: mic: Enable VOP debugfs and driver build This patch moves the virtio specific debugfs hooks previously in mic_debugfs.c in the MIC host driver into the VOP driver. The Kconfig/Makefile is also updated to allow building the VOP driver. Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/Kconfig | 20 ++++ drivers/misc/mic/Makefile | 1 + drivers/misc/mic/vop/Makefile | 9 ++ drivers/misc/mic/vop/vop_debugfs.c | 232 +++++++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 drivers/misc/mic/vop/Makefile create mode 100644 drivers/misc/mic/vop/vop_debugfs.c (limited to 'drivers/misc') diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 840f7ef1a141..b03bb17cae77 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -124,3 +124,23 @@ config MIC_COSM More information about the Intel MIC family as well as the Linux OS and tools for MIC to use with this driver are available from . + +comment "VOP Driver" + +config VOP + tristate "VOP Driver" + depends on 64BIT && PCI && X86 && VOP_BUS + select VHOST_RING + help + This enables VOP (Virtio over PCIe) Driver support for the Intel + Many Integrated Core (MIC) family of PCIe form factor coprocessor + devices. The VOP driver allows virtio drivers, e.g. net, console + and block drivers, on the card connect to user space virtio + devices on the host. + + If you are building a host kernel with an Intel MIC device then + say M (recommended) or Y, else say N. If unsure say N. + + More information about the Intel MIC family as well as the Linux + OS and tools for MIC to use with this driver are available from + . diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile index e288a1106738..f2b1323ff96c 100644 --- a/drivers/misc/mic/Makefile +++ b/drivers/misc/mic/Makefile @@ -8,3 +8,4 @@ obj-y += bus/ obj-$(CONFIG_SCIF) += scif/ obj-$(CONFIG_MIC_COSM) += cosm/ obj-$(CONFIG_MIC_COSM) += cosm_client/ +obj-$(CONFIG_VOP) += vop/ diff --git a/drivers/misc/mic/vop/Makefile b/drivers/misc/mic/vop/Makefile new file mode 100644 index 000000000000..78819c8999f1 --- /dev/null +++ b/drivers/misc/mic/vop/Makefile @@ -0,0 +1,9 @@ +# +# Makefile - Intel MIC Linux driver. +# Copyright(c) 2016, Intel Corporation. +# +obj-m := vop.o + +vop-objs += vop_main.o +vop-objs += vop_debugfs.o +vop-objs += vop_vringh.o diff --git a/drivers/misc/mic/vop/vop_debugfs.c b/drivers/misc/mic/vop/vop_debugfs.c new file mode 100644 index 000000000000..ab43884e5cd7 --- /dev/null +++ b/drivers/misc/mic/vop/vop_debugfs.c @@ -0,0 +1,232 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel Virtio Over PCIe (VOP) driver. + * + */ +#include +#include + +#include "vop_main.h" + +static int vop_dp_show(struct seq_file *s, void *pos) +{ + struct mic_device_desc *d; + struct mic_device_ctrl *dc; + struct mic_vqconfig *vqconfig; + __u32 *features; + __u8 *config; + struct vop_info *vi = s->private; + struct vop_device *vpdev = vi->vpdev; + struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); + int j, k; + + seq_printf(s, "Bootparam: magic 0x%x\n", + bootparam->magic); + seq_printf(s, "Bootparam: h2c_config_db %d\n", + bootparam->h2c_config_db); + seq_printf(s, "Bootparam: node_id %d\n", + bootparam->node_id); + seq_printf(s, "Bootparam: c2h_scif_db %d\n", + bootparam->c2h_scif_db); + seq_printf(s, "Bootparam: h2c_scif_db %d\n", + bootparam->h2c_scif_db); + seq_printf(s, "Bootparam: scif_host_dma_addr 0x%llx\n", + bootparam->scif_host_dma_addr); + seq_printf(s, "Bootparam: scif_card_dma_addr 0x%llx\n", + bootparam->scif_card_dma_addr); + + for (j = sizeof(*bootparam); + j < MIC_DP_SIZE; j += mic_total_desc_size(d)) { + d = (void *)bootparam + j; + dc = (void *)d + mic_aligned_desc_size(d); + + /* end of list */ + if (d->type == 0) + break; + + if (d->type == -1) + continue; + + seq_printf(s, "Type %d ", d->type); + seq_printf(s, "Num VQ %d ", d->num_vq); + seq_printf(s, "Feature Len %d\n", d->feature_len); + seq_printf(s, "Config Len %d ", d->config_len); + seq_printf(s, "Shutdown Status %d\n", d->status); + + for (k = 0; k < d->num_vq; k++) { + vqconfig = mic_vq_config(d) + k; + seq_printf(s, "vqconfig[%d]: ", k); + seq_printf(s, "address 0x%llx ", + vqconfig->address); + seq_printf(s, "num %d ", vqconfig->num); + seq_printf(s, "used address 0x%llx\n", + vqconfig->used_address); + } + + features = (__u32 *)mic_vq_features(d); + seq_printf(s, "Features: Host 0x%x ", features[0]); + seq_printf(s, "Guest 0x%x\n", features[1]); + + config = mic_vq_configspace(d); + for (k = 0; k < d->config_len; k++) + seq_printf(s, "config[%d]=%d\n", k, config[k]); + + seq_puts(s, "Device control:\n"); + seq_printf(s, "Config Change %d ", dc->config_change); + seq_printf(s, "Vdev reset %d\n", dc->vdev_reset); + seq_printf(s, "Guest Ack %d ", dc->guest_ack); + seq_printf(s, "Host ack %d\n", dc->host_ack); + seq_printf(s, "Used address updated %d ", + dc->used_address_updated); + seq_printf(s, "Vdev 0x%llx\n", dc->vdev); + seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); + seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); + } + schedule_work(&vi->hotplug_work); + return 0; +} + +static int vop_dp_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, vop_dp_show, inode->i_private); +} + +static int vop_dp_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations dp_ops = { + .owner = THIS_MODULE, + .open = vop_dp_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = vop_dp_debug_release +}; + +static int vop_vdev_info_show(struct seq_file *s, void *unused) +{ + struct vop_info *vi = s->private; + struct list_head *pos, *tmp; + struct vop_vdev *vdev; + int i, j; + + mutex_lock(&vi->vop_mutex); + list_for_each_safe(pos, tmp, &vi->vdev_list) { + vdev = list_entry(pos, struct vop_vdev, list); + seq_printf(s, "VDEV type %d state %s in %ld out %ld in_dma %ld out_dma %ld\n", + vdev->virtio_id, + vop_vdevup(vdev) ? "UP" : "DOWN", + vdev->in_bytes, + vdev->out_bytes, + vdev->in_bytes_dma, + vdev->out_bytes_dma); + for (i = 0; i < MIC_MAX_VRINGS; i++) { + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; + struct vop_vringh *vvr = &vdev->vvr[i]; + struct vringh *vrh = &vvr->vrh; + int num = vrh->vring.num; + + if (!num) + continue; + desc = vrh->vring.desc; + seq_printf(s, "vring i %d avail_idx %d", + i, vvr->vring.info->avail_idx & (num - 1)); + seq_printf(s, " vring i %d avail_idx %d\n", + i, vvr->vring.info->avail_idx); + seq_printf(s, "vrh i %d weak_barriers %d", + i, vrh->weak_barriers); + seq_printf(s, " last_avail_idx %d last_used_idx %d", + vrh->last_avail_idx, vrh->last_used_idx); + seq_printf(s, " completed %d\n", vrh->completed); + for (j = 0; j < num; j++) { + seq_printf(s, "desc[%d] addr 0x%llx len %d", + j, desc->addr, desc->len); + seq_printf(s, " flags 0x%x next %d\n", + desc->flags, desc->next); + desc++; + } + avail = vrh->vring.avail; + seq_printf(s, "avail flags 0x%x idx %d\n", + vringh16_to_cpu(vrh, avail->flags), + vringh16_to_cpu(vrh, + avail->idx) & (num - 1)); + seq_printf(s, "avail flags 0x%x idx %d\n", + vringh16_to_cpu(vrh, avail->flags), + vringh16_to_cpu(vrh, avail->idx)); + for (j = 0; j < num; j++) + seq_printf(s, "avail ring[%d] %d\n", + j, avail->ring[j]); + used = vrh->vring.used; + seq_printf(s, "used flags 0x%x idx %d\n", + vringh16_to_cpu(vrh, used->flags), + vringh16_to_cpu(vrh, used->idx) & (num - 1)); + seq_printf(s, "used flags 0x%x idx %d\n", + vringh16_to_cpu(vrh, used->flags), + vringh16_to_cpu(vrh, used->idx)); + for (j = 0; j < num; j++) + seq_printf(s, "used ring[%d] id %d len %d\n", + j, vringh32_to_cpu(vrh, + used->ring[j].id), + vringh32_to_cpu(vrh, + used->ring[j].len)); + } + } + mutex_unlock(&vi->vop_mutex); + + return 0; +} + +static int vop_vdev_info_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, vop_vdev_info_show, inode->i_private); +} + +static int vop_vdev_info_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations vdev_info_ops = { + .owner = THIS_MODULE, + .open = vop_vdev_info_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = vop_vdev_info_debug_release +}; + +void vop_init_debugfs(struct vop_info *vi) +{ + char name[16]; + + snprintf(name, sizeof(name), "%s%d", KBUILD_MODNAME, vi->vpdev->dnode); + vi->dbg = debugfs_create_dir(name, NULL); + if (!vi->dbg) { + pr_err("can't create debugfs dir vop\n"); + return; + } + debugfs_create_file("dp", 0444, vi->dbg, vi, &dp_ops); + debugfs_create_file("vdev_info", 0444, vi->dbg, vi, &vdev_info_ops); +} + +void vop_exit_debugfs(struct vop_info *vi) +{ + debugfs_remove_recursive(vi->dbg); +} -- cgit v1.2.3 From c74c9318a3a76c3714785bc06147dd207a9d0aa3 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Mon, 8 Feb 2016 15:48:18 -0800 Subject: misc: mic: MIC host and card driver changes to enable VOP This patch modifies the MIC host and card drivers to start using the VOP driver. The MIC host and card drivers now implement the VOP bus operations and register a VOP device on the VOP bus. MIC driver stack documentation is also updated to include the new VOP driver. Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- Documentation/mic/mic_overview.txt | 54 ++++++++-------- Documentation/mic/mpssd/mpss | 2 +- Documentation/mic/mpssd/mpssd.c | 2 +- drivers/misc/mic/Kconfig | 7 ++- drivers/misc/mic/card/mic_device.c | 84 ++++++++++++++++++++++++- drivers/misc/mic/card/mic_device.h | 3 + drivers/misc/mic/card/mic_x100.c | 1 + drivers/misc/mic/host/mic_boot.c | 123 ++++++++++++++++++++++++++++++++++++- drivers/misc/mic/host/mic_device.h | 3 + drivers/misc/mic/host/mic_main.c | 1 + 10 files changed, 247 insertions(+), 33 deletions(-) (limited to 'drivers/misc') diff --git a/Documentation/mic/mic_overview.txt b/Documentation/mic/mic_overview.txt index 73f44fc3e715..074adbdf83a4 100644 --- a/Documentation/mic/mic_overview.txt +++ b/Documentation/mic/mic_overview.txt @@ -12,10 +12,19 @@ for the X100 devices. Since it is a PCIe card, it does not have the ability to host hardware devices for networking, storage and console. We provide these devices -on X100 coprocessors thus enabling a self-bootable equivalent environment -for applications. A key benefit of our solution is that it leverages -the standard virtio framework for network, disk and console devices, -though in our case the virtio framework is used across a PCIe bus. +on X100 coprocessors thus enabling a self-bootable equivalent +environment for applications. A key benefit of our solution is that it +leverages the standard virtio framework for network, disk and console +devices, though in our case the virtio framework is used across a PCIe +bus. A Virtio Over PCIe (VOP) driver allows creating user space +backends or devices on the host which are used to probe virtio drivers +for these devices on the MIC card. The existing VRINGH infrastructure +in the kernel is used to access virtio rings from the host. The card +VOP driver allows card virtio drivers to communicate with their user +space backends on the host via a device page. Ring 3 apps on the host +can add, remove and configure virtio devices. A thin MIC specific +virtio_config_ops is implemented which is borrowed heavily from +previous similar implementations in lguest and s390. MIC PCIe card has a dma controller with 8 channels. These channels are shared between the host s/w and the card s/w. 0 to 3 are used by host @@ -38,7 +47,6 @@ single threaded performance for the host compared to MIC, the ability of the host to initiate DMA's to/from the card using the MIC DMA engine and the fact that the virtio block storage backend can only be on the host. - | +----------+ | +----------+ | Card OS | | | Host OS | +----------+ | +----------+ @@ -47,27 +55,25 @@ the fact that the virtio block storage backend can only be on the host. | Virtio| |Virtio | |Virtio| | |Virtio | |Virtio | |Virtio | | Net | |Console | |Block | | |Net | |Console | |Block | | Driver| |Driver | |Driver| | |backend | |backend | |backend | - +-------+ +--------+ +------+ | +---------+ +--------+ +--------+ + +---+---+ +---+----+ +--+---+ | +---------+ +----+---+ +--------+ | | | | | | | | | | |User | | | - | | | |------|------------|---------|------- - +-------------------+ |Kernel +--------------------------+ - | | | Virtio over PCIe IOCTLs | - | | +--------------------------+ -+-----------+ | | | +-----------+ -| MIC DMA | | +------+ | +------+ +------+ | | MIC DMA | -| Driver | | | SCIF | | | SCIF | | COSM | | | Driver | -+-----------+ | +------+ | +------+ +--+---+ | +-----------+ - | | | | | | | | -+---------------+ | +------+ | +--+---+ +--+---+ | +----------------+ -|MIC virtual Bus| | |SCIF | | |SCIF | | COSM | | |MIC virtual Bus | -+---------------+ | |HW Bus| | |HW Bus| | Bus | | +----------------+ - | | +------+ | +--+---+ +------+ | | - | | | | | | | | - | +-----------+---+ | | | +---------------+ | - | |Intel MIC | | | | |Intel MIC | | - +---|Card Driver | | | | |Host Driver | | - +------------+--------+ | +----+---------------+-----+ + | | | |------|------------|--+------|------- + +---------+---------+ |Kernel | + | | | + +---------+ +---+----+ +------+ | +------+ +------+ +--+---+ +-------+ + |MIC DMA | | VOP | | SCIF | | | SCIF | | COSM | | VOP | |MIC DMA| + +---+-----+ +---+----+ +--+---+ | +--+---+ +--+---+ +------+ +----+--+ + | | | | | | | + +---+-----+ +---+----+ +--+---+ | +--+---+ +--+---+ +------+ +----+--+ + |MIC | | VOP | |SCIF | | |SCIF | | COSM | | VOP | | MIC | + |HW Bus | | HW Bus| |HW Bus| | |HW Bus| | Bus | |HW Bus| |HW Bus | + +---------+ +--------+ +--+---+ | +--+---+ +------+ +------+ +-------+ + | | | | | | | + | +-----------+--+ | | | +---------------+ | + | |Intel MIC | | | | |Intel MIC | | + | |Card Driver | | | | |Host Driver | | + +---+--------------+------+ | +----+---------------+-----+ | | | +-------------------------------------------------------------+ | | diff --git a/Documentation/mic/mpssd/mpss b/Documentation/mic/mpssd/mpss index 09ea90931649..5fcf9fa4b082 100755 --- a/Documentation/mic/mpssd/mpss +++ b/Documentation/mic/mpssd/mpss @@ -35,7 +35,7 @@ exec=/usr/sbin/mpssd sysfs="/sys/class/mic" -mic_modules="mic_host mic_x100_dma scif" +mic_modules="mic_host mic_x100_dma scif vop" start() { diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c index aaeafa18d99b..518dece71578 100644 --- a/Documentation/mic/mpssd/mpssd.c +++ b/Documentation/mic/mpssd/mpssd.c @@ -926,7 +926,7 @@ add_virtio_device(struct mic_info *mic, struct mic_device_desc *dd) char path[PATH_MAX]; int fd, err; - snprintf(path, PATH_MAX, "/dev/mic%d", mic->id); + snprintf(path, PATH_MAX, "/dev/vop_virtio%d", mic->id); fd = open(path, O_RDWR); if (fd < 0) { mpsslog("Could not open %s %s\n", path, strerror(errno)); diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index b03bb17cae77..2e4f3ba75c8e 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -53,8 +53,8 @@ comment "Intel MIC Host Driver" config INTEL_MIC_HOST tristate "Intel MIC Host Driver" - depends on 64BIT && PCI && X86 && INTEL_MIC_BUS && SCIF_BUS && MIC_COSM - select VHOST_RING + depends on 64BIT && PCI && X86 + depends on INTEL_MIC_BUS && SCIF_BUS && MIC_COSM && VOP_BUS help This enables Host Driver support for the Intel Many Integrated Core (MIC) family of PCIe form factor coprocessor devices that @@ -73,7 +73,8 @@ comment "Intel MIC Card Driver" config INTEL_MIC_CARD tristate "Intel MIC Card Driver" - depends on 64BIT && X86 && INTEL_MIC_BUS && SCIF_BUS && MIC_COSM + depends on 64BIT && X86 + depends on INTEL_MIC_BUS && SCIF_BUS && MIC_COSM && VOP_BUS select VIRTIO help This enables card driver support for the Intel Many Integrated diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index ff03c633541c..e749af48f736 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c @@ -249,12 +249,82 @@ static struct scif_hw_ops scif_hw_ops = { .iounmap = ___mic_iounmap, }; +static inline struct mic_driver *vpdev_to_mdrv(struct vop_device *vpdev) +{ + return dev_get_drvdata(vpdev->dev.parent); +} + +static struct mic_irq * +__mic_request_irq(struct vop_device *vpdev, + irqreturn_t (*func)(int irq, void *data), + const char *name, void *data, int intr_src) +{ + return mic_request_card_irq(func, NULL, name, data, intr_src); +} + +static void __mic_free_irq(struct vop_device *vpdev, + struct mic_irq *cookie, void *data) +{ + return mic_free_card_irq(cookie, data); +} + +static void __mic_ack_interrupt(struct vop_device *vpdev, int num) +{ + struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); + + mic_ack_interrupt(&mdrv->mdev); +} + +static int __mic_next_db(struct vop_device *vpdev) +{ + return mic_next_card_db(); +} + +static void __iomem *__mic_get_remote_dp(struct vop_device *vpdev) +{ + struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); + + return mdrv->dp; +} + +static void __mic_send_intr(struct vop_device *vpdev, int db) +{ + struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); + + mic_send_intr(&mdrv->mdev, db); +} + +static void __iomem *__mic_ioremap(struct vop_device *vpdev, + dma_addr_t pa, size_t len) +{ + struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); + + return mic_card_map(&mdrv->mdev, pa, len); +} + +static void __mic_iounmap(struct vop_device *vpdev, void __iomem *va) +{ + struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); + + mic_card_unmap(&mdrv->mdev, va); +} + +static struct vop_hw_ops vop_hw_ops = { + .request_irq = __mic_request_irq, + .free_irq = __mic_free_irq, + .ack_interrupt = __mic_ack_interrupt, + .next_db = __mic_next_db, + .get_remote_dp = __mic_get_remote_dp, + .send_intr = __mic_send_intr, + .ioremap = __mic_ioremap, + .iounmap = __mic_iounmap, +}; + static int mic_request_dma_chans(struct mic_driver *mdrv) { dma_cap_mask_t mask; struct dma_chan *chan; - request_module("mic_x100_dma"); dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask); @@ -308,6 +378,13 @@ int __init mic_driver_init(struct mic_driver *mdrv) rc = -ENODEV; goto irq_uninit; } + mdrv->vpdev = vop_register_device(mdrv->dev, VOP_DEV_TRNSP, + NULL, &vop_hw_ops, 0, + NULL, mdrv->dma_ch[0]); + if (IS_ERR(mdrv->vpdev)) { + rc = PTR_ERR(mdrv->vpdev); + goto dma_free; + } bootparam = mdrv->dp; node_id = ioread8(&bootparam->node_id); mdrv->scdev = scif_register_device(mdrv->dev, MIC_SCIF_DEV, @@ -317,11 +394,13 @@ int __init mic_driver_init(struct mic_driver *mdrv) mdrv->num_dma_ch, true); if (IS_ERR(mdrv->scdev)) { rc = PTR_ERR(mdrv->scdev); - goto dma_free; + goto vop_remove; } mic_create_card_debug_dir(mdrv); done: return rc; +vop_remove: + vop_unregister_device(mdrv->vpdev); dma_free: mic_free_dma_chans(mdrv); irq_uninit: @@ -342,6 +421,7 @@ void mic_driver_uninit(struct mic_driver *mdrv) { mic_delete_card_debug_dir(mdrv); scif_unregister_device(mdrv->scdev); + vop_unregister_device(mdrv->vpdev); mic_free_dma_chans(mdrv); mic_uninit_irq(); mic_dp_uninit(); diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h index 1dbf83c41289..333dbed972f6 100644 --- a/drivers/misc/mic/card/mic_device.h +++ b/drivers/misc/mic/card/mic_device.h @@ -32,6 +32,7 @@ #include #include #include "../bus/scif_bus.h" +#include "../bus/vop_bus.h" /** * struct mic_intr_info - Contains h/w specific interrupt sources info @@ -76,6 +77,7 @@ struct mic_device { * @dma_ch - Array of DMA channels * @num_dma_ch - Number of DMA channels available * @scdev: SCIF device on the SCIF virtual bus. + * @vpdev: Virtio over PCIe device on the VOP virtual bus. */ struct mic_driver { char name[20]; @@ -90,6 +92,7 @@ struct mic_driver { struct dma_chan *dma_ch[MIC_MAX_DMA_CHAN]; int num_dma_ch; struct scif_hw_dev *scdev; + struct vop_device *vpdev; }; /** diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c index b2958ce2368c..b9f0710ffa6b 100644 --- a/drivers/misc/mic/card/mic_x100.c +++ b/drivers/misc/mic/card/mic_x100.c @@ -326,6 +326,7 @@ static int __init mic_init(void) goto done; } + request_module("mic_x100_dma"); mic_init_card_debugfs(); ret = platform_device_register(&mic_platform_dev); if (ret) { diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index 3df305f6282c..8c91c9950b54 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -25,10 +25,118 @@ #include #include #include "../bus/scif_bus.h" +#include "../bus/vop_bus.h" #include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" +static inline struct mic_device *vpdev_to_mdev(struct device *dev) +{ + return dev_get_drvdata(dev->parent); +} + +static dma_addr_t +_mic_dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, struct dma_attrs *attrs) +{ + void *va = phys_to_virt(page_to_phys(page)) + offset; + struct mic_device *mdev = vpdev_to_mdev(dev); + + return mic_map_single(mdev, va, size); +} + +static void _mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + struct mic_device *mdev = vpdev_to_mdev(dev); + + mic_unmap_single(mdev, dma_addr, size); +} + +static const struct dma_map_ops _mic_dma_ops = { + .map_page = _mic_dma_map_page, + .unmap_page = _mic_dma_unmap_page, +}; + +static struct mic_irq * +__mic_request_irq(struct vop_device *vpdev, + irqreturn_t (*func)(int irq, void *data), + const char *name, void *data, int intr_src) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + return mic_request_threaded_irq(mdev, func, NULL, name, data, + intr_src, MIC_INTR_DB); +} + +static void __mic_free_irq(struct vop_device *vpdev, + struct mic_irq *cookie, void *data) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + return mic_free_irq(mdev, cookie, data); +} + +static void __mic_ack_interrupt(struct vop_device *vpdev, int num) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + mdev->ops->intr_workarounds(mdev); +} + +static int __mic_next_db(struct vop_device *vpdev) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + return mic_next_db(mdev); +} + +static void *__mic_get_dp(struct vop_device *vpdev) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + return mdev->dp; +} + +static void __iomem *__mic_get_remote_dp(struct vop_device *vpdev) +{ + return NULL; +} + +static void __mic_send_intr(struct vop_device *vpdev, int db) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + mdev->ops->send_intr(mdev, db); +} + +static void __iomem *__mic_ioremap(struct vop_device *vpdev, + dma_addr_t pa, size_t len) +{ + struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); + + return mdev->aper.va + pa; +} + +static void __mic_iounmap(struct vop_device *vpdev, void __iomem *va) +{ + /* nothing to do */ +} + +static struct vop_hw_ops vop_hw_ops = { + .request_irq = __mic_request_irq, + .free_irq = __mic_free_irq, + .ack_interrupt = __mic_ack_interrupt, + .next_db = __mic_next_db, + .get_dp = __mic_get_dp, + .get_remote_dp = __mic_get_remote_dp, + .send_intr = __mic_send_intr, + .ioremap = __mic_ioremap, + .iounmap = __mic_iounmap, +}; + static inline struct mic_device *scdev_to_mdev(struct scif_hw_dev *scdev) { return dev_get_drvdata(scdev->dev.parent); @@ -314,7 +422,6 @@ static int mic_request_dma_chans(struct mic_device *mdev) dma_cap_mask_t mask; struct dma_chan *chan; - request_module("mic_x100_dma"); dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask); @@ -386,9 +493,18 @@ static int _mic_start(struct cosm_device *cdev, int id) goto dma_free; } + mdev->vpdev = vop_register_device(&mdev->pdev->dev, + VOP_DEV_TRNSP, &_mic_dma_ops, + &vop_hw_ops, id + 1, &mdev->aper, + mdev->dma_ch[0]); + if (IS_ERR(mdev->vpdev)) { + rc = PTR_ERR(mdev->vpdev); + goto scif_remove; + } + rc = mdev->ops->load_mic_fw(mdev, NULL); if (rc) - goto scif_remove; + goto vop_remove; mic_smpt_restore(mdev); mic_intr_restore(mdev); mdev->intr_ops->enable_interrupts(mdev); @@ -396,6 +512,8 @@ static int _mic_start(struct cosm_device *cdev, int id) mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); mdev->ops->send_firmware_intr(mdev); goto unlock_ret; +vop_remove: + vop_unregister_device(mdev->vpdev); scif_remove: scif_unregister_device(mdev->scdev); dma_free: @@ -422,6 +540,7 @@ static void _mic_stop(struct cosm_device *cdev, bool force) * will be the first to be registered and the last to be * unregistered. */ + vop_unregister_device(mdev->vpdev); scif_unregister_device(mdev->scdev); mic_free_dma_chans(mdev); mbus_unregister_device(mdev->dma_mbdev); diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 8460de122929..52b12b22f4ae 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -29,6 +29,7 @@ #include #include #include "../bus/scif_bus.h" +#include "../bus/vop_bus.h" #include "../bus/cosm_bus.h" #include "mic_intr.h" @@ -68,6 +69,7 @@ extern struct cosm_hw_ops cosm_hw_ops; * @dma_ch - Array of DMA channels * @num_dma_ch - Number of DMA channels available * @scdev: SCIF device on the SCIF virtual bus. + * @vpdev: Virtio over PCIe device on the VOP virtual bus. * @cosm_dev: COSM device */ struct mic_device { @@ -92,6 +94,7 @@ struct mic_device { struct dma_chan *dma_ch[MIC_MAX_DMA_CHAN]; int num_dma_ch; struct scif_hw_dev *scdev; + struct vop_device *vpdev; struct cosm_device *cosm_dev; }; diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index 400def2106e7..035be3e9ceba 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -317,6 +317,7 @@ static int __init mic_init(void) { int ret; + request_module("mic_x100_dma"); mic_init_debugfs(); ida_init(&g_mic_ida); ret = pci_register_driver(&mic_driver); -- cgit v1.2.3 From e1379b56e9e88653fcb58cbaa71cd6b1cc304918 Mon Sep 17 00:00:00 2001 From: Cory Tusar Date: Wed, 10 Feb 2016 14:32:07 -0500 Subject: misc: eeprom_93xx46: Add quirks to support Atmel AT93C46D device. Atmel devices in this family have some quirks not found in other similar chips - they do not support a sequential read of the entire EEPROM contents, and the control word sent at the start of each operation varies in bit length. This commit adds quirk support to the driver and modifies the read implementation to support non-sequential reads for consistency with other misc/eeprom drivers. Tested on a custom Freescale VF610-based platform, with an AT93C46D device attached via dspi2. The spi-gpio driver was used to allow the necessary non-byte-sized transfers. Signed-off-by: Cory Tusar Tested-by: Chris Healy Reviewed-by: Vladimir Zapolskiy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/eeprom_93xx46.c | 126 ++++++++++++++++++++++++++---------- include/linux/eeprom_93xx46.h | 6 ++ 2 files changed, 97 insertions(+), 35 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index f0c30b366218..a3c3136abf3e 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -27,6 +27,15 @@ #define ADDR_ERAL 0x20 #define ADDR_EWEN 0x30 +struct eeprom_93xx46_devtype_data { + unsigned int quirks; +}; + +static const struct eeprom_93xx46_devtype_data atmel_at93c46d_data = { + .quirks = EEPROM_93XX46_QUIRK_SINGLE_WORD_READ | + EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH, +}; + struct eeprom_93xx46_dev { struct spi_device *spi; struct eeprom_93xx46_platform_data *pdata; @@ -35,6 +44,16 @@ struct eeprom_93xx46_dev { int addrlen; }; +static inline bool has_quirk_single_word_read(struct eeprom_93xx46_dev *edev) +{ + return edev->pdata->quirks & EEPROM_93XX46_QUIRK_SINGLE_WORD_READ; +} + +static inline bool has_quirk_instruction_length(struct eeprom_93xx46_dev *edev) +{ + return edev->pdata->quirks & EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH; +} + static ssize_t eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, @@ -42,58 +61,73 @@ eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj, { struct eeprom_93xx46_dev *edev; struct device *dev; - struct spi_message m; - struct spi_transfer t[2]; - int bits, ret; - u16 cmd_addr; + ssize_t ret = 0; dev = kobj_to_dev(kobj); edev = dev_get_drvdata(dev); - cmd_addr = OP_READ << edev->addrlen; + mutex_lock(&edev->lock); - if (edev->addrlen == 7) { - cmd_addr |= off & 0x7f; - bits = 10; - } else { - cmd_addr |= (off >> 1) & 0x3f; - bits = 9; - } + if (edev->pdata->prepare) + edev->pdata->prepare(edev); - dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n", - cmd_addr, edev->spi->max_speed_hz); + while (count) { + struct spi_message m; + struct spi_transfer t[2] = { { 0 } }; + u16 cmd_addr = OP_READ << edev->addrlen; + size_t nbytes = count; + int bits; + int err; + + if (edev->addrlen == 7) { + cmd_addr |= off & 0x7f; + bits = 10; + if (has_quirk_single_word_read(edev)) + nbytes = 1; + } else { + cmd_addr |= (off >> 1) & 0x3f; + bits = 9; + if (has_quirk_single_word_read(edev)) + nbytes = 2; + } - spi_message_init(&m); - memset(t, 0, sizeof(t)); + dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n", + cmd_addr, edev->spi->max_speed_hz); - t[0].tx_buf = (char *)&cmd_addr; - t[0].len = 2; - t[0].bits_per_word = bits; - spi_message_add_tail(&t[0], &m); + spi_message_init(&m); - t[1].rx_buf = buf; - t[1].len = count; - t[1].bits_per_word = 8; - spi_message_add_tail(&t[1], &m); + t[0].tx_buf = (char *)&cmd_addr; + t[0].len = 2; + t[0].bits_per_word = bits; + spi_message_add_tail(&t[0], &m); - mutex_lock(&edev->lock); + t[1].rx_buf = buf; + t[1].len = count; + t[1].bits_per_word = 8; + spi_message_add_tail(&t[1], &m); - if (edev->pdata->prepare) - edev->pdata->prepare(edev); + err = spi_sync(edev->spi, &m); + /* have to wait at least Tcsl ns */ + ndelay(250); - ret = spi_sync(edev->spi, &m); - /* have to wait at least Tcsl ns */ - ndelay(250); - if (ret) { - dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n", - count, (int)off, ret); + if (err) { + dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n", + nbytes, (int)off, err); + ret = err; + break; + } + + buf += nbytes; + off += nbytes; + count -= nbytes; + ret += nbytes; } if (edev->pdata->finish) edev->pdata->finish(edev); mutex_unlock(&edev->lock); - return ret ? : count; + return ret; } static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) @@ -112,7 +146,13 @@ static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) bits = 9; } - dev_dbg(&edev->spi->dev, "ew cmd 0x%04x\n", cmd_addr); + if (has_quirk_instruction_length(edev)) { + cmd_addr <<= 2; + bits += 2; + } + + dev_dbg(&edev->spi->dev, "ew%s cmd 0x%04x, %d bits\n", + is_on ? "en" : "ds", cmd_addr, bits); spi_message_init(&m); memset(&t, 0, sizeof(t)); @@ -247,6 +287,13 @@ static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev) bits = 9; } + if (has_quirk_instruction_length(edev)) { + cmd_addr <<= 2; + bits += 2; + } + + dev_dbg(&edev->spi->dev, "eral cmd 0x%04x, %d bits\n", cmd_addr, bits); + spi_message_init(&m); memset(&t, 0, sizeof(t)); @@ -298,12 +345,15 @@ static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase); static const struct of_device_id eeprom_93xx46_of_table[] = { { .compatible = "eeprom-93xx46", }, + { .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, }, {} }; MODULE_DEVICE_TABLE(of, eeprom_93xx46_of_table); static int eeprom_93xx46_probe_dt(struct spi_device *spi) { + const struct of_device_id *of_id = + of_match_device(eeprom_93xx46_of_table, &spi->dev); struct device_node *np = spi->dev.of_node; struct eeprom_93xx46_platform_data *pd; u32 tmp; @@ -331,6 +381,12 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) if (of_property_read_bool(np, "read-only")) pd->flags |= EE_READONLY; + if (of_id->data) { + const struct eeprom_93xx46_devtype_data *data = of_id->data; + + pd->quirks = data->quirks; + } + spi->dev.platform_data = pd; return 0; diff --git a/include/linux/eeprom_93xx46.h b/include/linux/eeprom_93xx46.h index 06791811e49d..92fa4c37ac1f 100644 --- a/include/linux/eeprom_93xx46.h +++ b/include/linux/eeprom_93xx46.h @@ -9,6 +9,12 @@ struct eeprom_93xx46_platform_data { #define EE_ADDR16 0x02 /* 16 bit addr. cfg */ #define EE_READONLY 0x08 /* forbid writing */ + unsigned int quirks; +/* Single word read transfers only; no sequential read. */ +#define EEPROM_93XX46_QUIRK_SINGLE_WORD_READ (1 << 0) +/* Instructions such as EWEN are (addrlen + 2) in length. */ +#define EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH (1 << 1) + /* * optional hooks to control additional logic * before and after spi transfer. -- cgit v1.2.3 From 3ca9b1ac28398c6fe0bed335d2d71a35e1c5f7c9 Mon Sep 17 00:00:00 2001 From: Cory Tusar Date: Wed, 10 Feb 2016 14:32:08 -0500 Subject: misc: eeprom_93xx46: Add support for a GPIO 'select' line. This commit adds support to the eeprom_93x46 driver allowing a GPIO line to function as a 'select' or 'enable' signal prior to accessing the EEPROM. Signed-off-by: Cory Tusar Tested-by: Chris Healy Reviewed-by: Vladimir Zapolskiy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/eeprom_93xx46.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/eeprom_93xx46.h | 3 +++ 2 files changed, 38 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index a3c3136abf3e..f62ab29e293c 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -10,11 +10,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -343,6 +345,20 @@ static ssize_t eeprom_93xx46_store_erase(struct device *dev, } static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase); +static void select_assert(void *context) +{ + struct eeprom_93xx46_dev *edev = context; + + gpiod_set_value_cansleep(edev->pdata->select, 1); +} + +static void select_deassert(void *context) +{ + struct eeprom_93xx46_dev *edev = context; + + gpiod_set_value_cansleep(edev->pdata->select, 0); +} + static const struct of_device_id eeprom_93xx46_of_table[] = { { .compatible = "eeprom-93xx46", }, { .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, }, @@ -357,6 +373,8 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) struct device_node *np = spi->dev.of_node; struct eeprom_93xx46_platform_data *pd; u32 tmp; + int gpio; + enum of_gpio_flags of_flags; int ret; pd = devm_kzalloc(&spi->dev, sizeof(*pd), GFP_KERNEL); @@ -381,6 +399,23 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) if (of_property_read_bool(np, "read-only")) pd->flags |= EE_READONLY; + gpio = of_get_named_gpio_flags(np, "select-gpios", 0, &of_flags); + if (gpio_is_valid(gpio)) { + unsigned long flags = + of_flags == OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0; + + ret = devm_gpio_request_one(&spi->dev, gpio, flags, + "eeprom_93xx46_select"); + if (ret) + return ret; + + pd->select = gpio_to_desc(gpio); + pd->prepare = select_assert; + pd->finish = select_deassert; + + gpiod_direction_output(pd->select, 0); + } + if (of_id->data) { const struct eeprom_93xx46_devtype_data *data = of_id->data; diff --git a/include/linux/eeprom_93xx46.h b/include/linux/eeprom_93xx46.h index 92fa4c37ac1f..885f587a3555 100644 --- a/include/linux/eeprom_93xx46.h +++ b/include/linux/eeprom_93xx46.h @@ -3,6 +3,8 @@ * platform description for 93xx46 EEPROMs. */ +struct gpio_desc; + struct eeprom_93xx46_platform_data { unsigned char flags; #define EE_ADDR8 0x01 /* 8 bit addr. cfg */ @@ -21,4 +23,5 @@ struct eeprom_93xx46_platform_data { */ void (*prepare)(void *); void (*finish)(void *); + struct gpio_desc *select; }; -- cgit v1.2.3 From 6cbb097fd3903fd6b419303ee8dc7f72b47d06f0 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 10 Feb 2016 23:57:26 +0200 Subject: mei: fix double freeing of a cb during link reset Fix double freeing of the cb that can happen if link reset kicks in the middle of blocked write from a device on the cl bus. Free cb inside mei_cl_write function on failure and drop cb free operation from callers, during a link reset the mei_cl_write function returns with an error, but the caller doesn't know if the cb was already queued or not so it doesn't know if the cb will be freed upon queue reclaim or it has to free it itself. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 4 +--- drivers/misc/mei/client.c | 4 +++- drivers/misc/mei/main.c | 26 ++++++++++---------------- 3 files changed, 14 insertions(+), 20 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index f4cf43b47c7a..5d5996e39a67 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -44,7 +44,7 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking) { struct mei_device *bus; - struct mei_cl_cb *cb = NULL; + struct mei_cl_cb *cb; ssize_t rets; if (WARN_ON(!cl || !cl->dev)) @@ -86,8 +86,6 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, out: mutex_unlock(&bus->device_lock); - if (rets < 0) - mei_io_cb_free(cb); return rets; } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index af6816bc268f..a9cdb92b52d1 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1645,7 +1645,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) if (rets < 0 && rets != -EINPROGRESS) { pm_runtime_put_noidle(dev->dev); cl_err(dev, cl, "rpm: get failed %d\n", rets); - return rets; + goto free; } cb->buf_idx = 0; @@ -1724,6 +1724,8 @@ err: cl_dbg(dev, cl, "rpm: autosuspend\n"); pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); +free: + mei_io_cb_free(cb); return rets; } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 17970163eacc..05775a6f7c88 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -267,7 +267,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, size_t length, loff_t *offset) { struct mei_cl *cl = file->private_data; - struct mei_cl_cb *write_cb = NULL; + struct mei_cl_cb *cb; struct mei_device *dev; int rets; @@ -305,36 +305,30 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, } *offset = 0; - write_cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file); - if (!write_cb) { + cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file); + if (!cb) { rets = -ENOMEM; goto out; } - rets = copy_from_user(write_cb->buf.data, ubuf, length); + rets = copy_from_user(cb->buf.data, ubuf, length); if (rets) { dev_dbg(dev->dev, "failed to copy data from userland\n"); rets = -EFAULT; + mei_io_cb_free(cb); goto out; } if (cl == &dev->iamthif_cl) { - rets = mei_amthif_write(cl, write_cb); - - if (rets) { - dev_err(dev->dev, - "amthif write failed with status = %d\n", rets); - goto out; - } - mutex_unlock(&dev->device_lock); - return length; + rets = mei_amthif_write(cl, cb); + if (!rets) + rets = length; + goto out; } - rets = mei_cl_write(cl, write_cb, false); + rets = mei_cl_write(cl, cb, false); out: mutex_unlock(&dev->device_lock); - if (rets < 0) - mei_io_cb_free(write_cb); return rets; } -- cgit v1.2.3 From 46d83a8782e73468c9f744d859fbb6af4d53c520 Mon Sep 17 00:00:00 2001 From: Amitoj Kaur Chawla Date: Wed, 10 Feb 2016 10:02:02 +0530 Subject: misc: ibmasm: Replace timeval with timespec64 This patch replaces timeval with timespec64 as 32 bit 'struct timeval' will not give current time beyond year 2038. The patch changes the code to use ktime_get_real_ts64() which returns a 'struct timespec64' instead of do_gettimeofday() which returns a 'struct timeval' This patch also alters the format strings in sprintf() for now.tv_sec and now.tv_nsec to incorporate 'long long' on 32 bit architectures and leading zeroes respectively. Signed-off-by: Amitoj Kaur Chawla Reviewed-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmasm/ibmasm.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/ibmasm/ibmasm.h b/drivers/misc/ibmasm/ibmasm.h index 5bd127727d8e..9fea49d2e15b 100644 --- a/drivers/misc/ibmasm/ibmasm.h +++ b/drivers/misc/ibmasm/ibmasm.h @@ -34,6 +34,7 @@ #include #include #include +#include /* Driver identification */ #define DRIVER_NAME "ibmasm" @@ -53,9 +54,11 @@ extern int ibmasm_debug; static inline char *get_timestamp(char *buf) { - struct timeval now; - do_gettimeofday(&now); - sprintf(buf, "%lu.%lu", now.tv_sec, now.tv_usec); + struct timespec64 now; + + ktime_get_real_ts64(&now); + sprintf(buf, "%llu.%.08lu", (long long)now.tv_sec, + now.tv_nsec / NSEC_PER_USEC); return buf; } -- cgit v1.2.3 From 6ddf2f0cb1e1931d16e08e30130ed04e5a441f2c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 12 Feb 2016 09:40:26 +0300 Subject: misc: mic: use after free printing error message Swap the printk and kfree() to avoid a use after free bug. Fixes: 61e9c905df78 ('misc: mic: Enable VOP host side functionality') Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_vringh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c index c1dd000f042b..c3613f39eb35 100644 --- a/drivers/misc/mic/vop/vop_vringh.c +++ b/drivers/misc/mic/vop/vop_vringh.c @@ -290,9 +290,9 @@ static int vop_virtio_add_device(struct vop_vdev *vdev, ret = vop_copy_dp_entry(vdev, argp, &type, &dd); if (ret) { - kfree(vdev); dev_err(vop_dev(vdev), "%s %d err %d\n", __func__, __LINE__, ret); + kfree(vdev); return ret; } -- cgit v1.2.3 From 59ea25904406a606d1329650e45e13e682e89320 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 12 Feb 2016 09:44:34 +0300 Subject: misc: mic: silence an overflow warning Static checkers complain that the this is a potential array overflow. We verify that it's not on the next line so this code is OK, but static checker warnings are annoying. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_vringh.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c index c3613f39eb35..e94c7fb6712a 100644 --- a/drivers/misc/mic/vop/vop_vringh.c +++ b/drivers/misc/mic/vop/vop_vringh.c @@ -848,12 +848,13 @@ static int vop_virtio_copy_desc(struct vop_vdev *vdev, struct mic_copy_desc *copy) { int err; - struct vop_vringh *vvr = &vdev->vvr[copy->vr_idx]; + struct vop_vringh *vvr; err = vop_verify_copy_args(vdev, copy); if (err) return err; + vvr = &vdev->vvr[copy->vr_idx]; mutex_lock(&vvr->vr_mutex); if (!vop_vdevup(vdev)) { err = -ENODEV; -- cgit v1.2.3 From 35bf7692e765c2275bf93fe573f7ca868ab73453 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 17 Feb 2016 18:27:34 +0200 Subject: mei: fix format string in debug prints buf_idx type was changed to size_t, and few places missed out to change the print format from %ld to %zu. Use also uz for buf.size which is also of size_t Fixes: commit 56988f22e097 ("mei: fix possible integer overflow issue")' Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 2 +- drivers/misc/mei/client.c | 2 +- drivers/misc/mei/interrupt.c | 6 +++--- drivers/misc/mei/main.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 04525ada9eda..194360a5f782 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -160,7 +160,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, * remove message from deletion list */ - dev_dbg(dev->dev, "amthif cb->buf.size - %zd cb->buf_idx - %zd\n", + dev_dbg(dev->dev, "amthif cb->buf.size - %zu cb->buf_idx - %zu\n", cb->buf.size, cb->buf_idx); /* length is being truncated to PAGE_SIZE, however, diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index a9cdb92b52d1..bab17e4197b6 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1583,7 +1583,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, return 0; } - cl_dbg(dev, cl, "buf: size = %zd idx = %zd\n", + cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n", cb->buf.size, cb->buf_idx); rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 06b744a503a3..1e5cb1f704f8 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -128,7 +128,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, buf_sz = mei_hdr->length + cb->buf_idx; /* catch for integer overflow */ if (buf_sz < cb->buf_idx) { - cl_err(dev, cl, "message is too big len %d idx %ld\n", + cl_err(dev, cl, "message is too big len %d idx %zu\n", mei_hdr->length, cb->buf_idx); list_move_tail(&cb->list, &complete_list->list); @@ -137,7 +137,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, } if (cb->buf.size < buf_sz) { - cl_dbg(dev, cl, "message overflow. size %zd len %d idx %zd\n", + cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n", cb->buf.size, mei_hdr->length, cb->buf_idx); buffer = krealloc(cb->buf.data, buf_sz, GFP_KERNEL); @@ -156,7 +156,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, cb->buf_idx += mei_hdr->length; if (mei_hdr->msg_complete) { - cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx); + cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx); list_move_tail(&cb->list, &complete_list->list); } else { pm_runtime_mark_last_busy(dev->dev); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 7e637eb46cb9..52635b063873 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -221,7 +221,7 @@ copy_buffer: goto free; } - cl_dbg(dev, cl, "buf.size = %zd buf.idx = %zd offset = %lld\n", + cl_dbg(dev, cl, "buf.size = %zu buf.idx = %zu offset = %lld\n", cb->buf.size, cb->buf_idx, *offset); if (*offset >= cb->buf_idx) { rets = 0; -- cgit v1.2.3